Skip to content
Merged
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions pychromecast/dial.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

from __future__ import annotations

from collections.abc import Generator
from contextlib import contextmanager
from dataclasses import dataclass
from http import HTTPStatus
import json
import logging
import socket
Expand Down Expand Up @@ -74,6 +77,31 @@ def _get_host_from_zc_service_info(
return (host, port)


@contextmanager
Comment thread
emontnemery marked this conversation as resolved.
Outdated
def _urlopen(
url: str, timeout: float, context: ssl.SSLContext | None
) -> Generator[Any, None, None]:
"""Help open an URL."""
headers = {"content-type": "application/json"}
try:
req = urllib.request.Request(url, headers=headers)
yield urllib.request.urlopen(req, timeout=timeout, context=context)
Comment thread
elupus marked this conversation as resolved.
Outdated
Comment thread
emontnemery marked this conversation as resolved.
Outdated
except urllib.error.HTTPError as err:
if err.code != HTTPStatus.FORBIDDEN:
raise
# We may be blocked because we're connecting to a hostname specified directly
# instead of to an IP address resolved by mDNS, cast devices will reject the
# request when the address is set to the hostname. Do another attempt with an
# empty host header.
# Note: This is a simplified approach to not have to deal with name resolution
# in pychromecast. If devices reject the empty host header we need to do name
# resolution and instead set the host header to the string version of the IP.
_LOGGER.debug("Failed to fetch %s, retrying with empty host header", url)
headers["host"] = ""
req = urllib.request.Request(url, headers=headers)
yield urllib.request.urlopen(req, timeout=timeout, context=context)
Comment thread
emontnemery marked this conversation as resolved.
Outdated


def _get_status(
services: set[HostServiceInfo | MDNSServiceInfo],
zconf: zeroconf.Zeroconf | None,
Expand Down Expand Up @@ -101,8 +129,7 @@ def _get_status(
if secure and not has_context:
context = get_ssl_context()

req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=timeout, context=context) as response:
with _urlopen(url, timeout, context) as response:
data = response.read()
return (host, json.loads(data.decode("utf-8")))

Expand Down Expand Up @@ -246,7 +273,8 @@ def get_device_info( # pylint: disable=too-many-locals
multizone_supported,
)

except (urllib.error.HTTPError, urllib.error.URLError, OSError, ValueError):
except (urllib.error.HTTPError, urllib.error.URLError, OSError, ValueError) as err:
_LOGGER.debug("Failed to get device info for %s: %s (%s)", host, err, type(err))
return None


Expand Down Expand Up @@ -305,7 +333,10 @@ def get_multizone_status(

return MultizoneStatus(dynamic_groups, groups)

except (urllib.error.HTTPError, urllib.error.URLError, OSError, ValueError):
except (urllib.error.HTTPError, urllib.error.URLError, OSError, ValueError) as err:
_LOGGER.debug(
"Failed to get multizone status for %s: %s (%s)", host, err, type(err)
)
return None


Expand Down
Loading