Prefer internal docker URL for VLC telnet when possible (#67090)

pull/67462/head
Mike Degatano 2022-03-02 00:56:20 -05:00 committed by GitHub
parent 65eefcacfc
commit 4ea6ca7f91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 7 deletions

View File

@ -10,14 +10,22 @@ import yarl
from homeassistant.components.http.auth import async_sign_path
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.network import get_url, is_hass_url
from homeassistant.helpers.network import (
get_supervisor_network_url,
get_url,
is_hass_url,
)
from .const import CONTENT_AUTH_EXPIRY_TIME, MEDIA_CLASS_DIRECTORY
@callback
def async_process_play_media_url(
hass: HomeAssistant, media_content_id: str, *, allow_relative_url: bool = False
hass: HomeAssistant,
media_content_id: str,
*,
allow_relative_url: bool = False,
for_supervisor_network: bool = False,
) -> str:
"""Update a media URL with authentication if it points at Home Assistant."""
if media_content_id[0] != "/" and not is_hass_url(hass, media_content_id):
@ -39,7 +47,14 @@ def async_process_play_media_url(
# convert relative URL to absolute URL
if media_content_id[0] == "/" and not allow_relative_url:
media_content_id = f"{get_url(hass)}{media_content_id}"
base_url = None
if for_supervisor_network:
base_url = get_supervisor_network_url(hass)
if not base_url:
base_url = get_url(hass)
media_content_id = f"{base_url}{media_content_id}"
return media_content_id

View File

@ -31,7 +31,7 @@ from homeassistant.components.media_player.const import (
SUPPORT_VOLUME_MUTE,
SUPPORT_VOLUME_SET,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.config_entries import SOURCE_HASSIO, ConfigEntry
from homeassistant.const import CONF_NAME, STATE_IDLE, STATE_PAUSED, STATE_PLAYING
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
@ -125,6 +125,7 @@ class VlcDevice(MediaPlayerEntity):
manufacturer="VideoLAN",
name=name,
)
self._using_addon = config_entry.source == SOURCE_HASSIO
@catch_vlc_errors
async def async_update(self) -> None:
@ -316,7 +317,9 @@ class VlcDevice(MediaPlayerEntity):
)
# If media ID is a relative URL, we serve it from HA.
media_id = async_process_play_media_url(self.hass, media_id)
media_id = async_process_play_media_url(
self.hass, media_id, for_supervisor_network=self._using_addon
)
await self._vlc.add(media_id)
self._state = STATE_PLAYING

View File

@ -1,6 +1,7 @@
"""Network helpers."""
from __future__ import annotations
from collections.abc import Callable
from contextlib import suppress
from ipaddress import ip_address
from typing import cast
@ -15,6 +16,7 @@ from homeassistant.util.network import is_ip_address, is_loopback, normalize_url
TYPE_URL_INTERNAL = "internal_url"
TYPE_URL_EXTERNAL = "external_url"
SUPERVISOR_NETWORK_HOST = "homeassistant"
class NoURLAvailableError(HomeAssistantError):
@ -33,6 +35,31 @@ def is_internal_request(hass: HomeAssistant) -> bool:
return False
@bind_hass
def get_supervisor_network_url(
hass: HomeAssistant, *, allow_ssl: bool = False
) -> str | None:
"""Get URL for home assistant within supervisor network."""
if hass.config.api is None or not hass.components.hassio.is_hassio():
return None
scheme = "http"
if hass.config.api.use_ssl:
# Certificate won't be valid for hostname so this URL usually won't work
if not allow_ssl:
return None
scheme = "https"
return str(
yarl.URL.build(
scheme=scheme,
host=SUPERVISOR_NETWORK_HOST,
port=hass.config.api.port,
)
)
def is_hass_url(hass: HomeAssistant, url: str) -> bool:
"""Return if the URL points at this Home Assistant instance."""
parsed = yarl.URL(normalize_url(url))
@ -53,11 +80,13 @@ def is_hass_url(hass: HomeAssistant, url: str) -> bool:
except NoURLAvailableError:
return None
potential_base_factory: Callable[[], str | None]
for potential_base_factory in (
lambda: hass.config.internal_url,
lambda: hass.config.external_url,
cloud_url,
host_ip,
lambda: get_supervisor_network_url(hass, allow_ssl=True),
):
potential_base = potential_base_factory()

View File

@ -8,9 +8,11 @@ from homeassistant.components.media_player.browse_media import (
)
from homeassistant.config import async_process_ha_core_config
from tests.common import mock_component
@pytest.fixture
def mock_sign_path():
@pytest.fixture(name="mock_sign_path")
def fixture_mock_sign_path():
"""Mock sign path."""
with patch(
"homeassistant.components.media_player.browse_media.async_sign_path",
@ -58,3 +60,35 @@ async def test_process_play_media_url(hass, mock_sign_path):
)
== "http://192.168.123.123:8123/path?hello=world"
)
async def test_process_play_media_url_for_addon(hass, mock_sign_path):
"""Test it uses the hostname for an addon if available."""
await async_process_ha_core_config(
hass,
{
"internal_url": "http://example.local:8123",
"external_url": "https://example.com",
},
)
# Not hassio or hassio not loaded yet, don't use supervisor network url
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
assert (
async_process_play_media_url(hass, "/path", for_supervisor_network=True)
!= "http://homeassistant:8123/path?authSig=bla"
)
# Is hassio and not SSL, use an supervisor network url
mock_component(hass, "hassio")
assert (
async_process_play_media_url(hass, "/path", for_supervisor_network=True)
== "http://homeassistant:8123/path?authSig=bla"
)
# Hassio loaded but using SSL, don't use an supervisor network url
hass.config.api = Mock(use_ssl=True, port=8123, local_ip="192.168.123.123")
assert (
async_process_play_media_url(hass, "/path", for_supervisor_network=True)
!= "https://homeassistant:8123/path?authSig=bla"
)

View File

@ -12,6 +12,7 @@ from homeassistant.helpers.network import (
_get_external_url,
_get_internal_url,
_get_request_host,
get_supervisor_network_url,
get_url,
is_hass_url,
is_internal_request,
@ -715,3 +716,41 @@ async def test_is_hass_url(hass):
assert is_hass_url(hass, "https://example.nabu.casa") is True
assert is_hass_url(hass, "http://example.nabu.casa:443") is False
assert is_hass_url(hass, "http://example.nabu.casa") is False
async def test_is_hass_url_addon_url(hass):
"""Test is_hass_url with a supervisor network URL."""
assert is_hass_url(hass, "http://homeassistant:8123") is False
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
await async_process_ha_core_config(
hass,
{"internal_url": "http://example.local:8123"},
)
assert is_hass_url(hass, "http://homeassistant:8123") is False
mock_component(hass, "hassio")
assert is_hass_url(hass, "http://homeassistant:8123")
assert not is_hass_url(hass, "https://homeassistant:8123")
hass.config.api = Mock(use_ssl=True, port=8123, local_ip="192.168.123.123")
assert not is_hass_url(hass, "http://homeassistant:8123")
assert is_hass_url(hass, "https://homeassistant:8123")
async def test_get_supervisor_network_url(hass):
"""Test get_supervisor_network_url."""
assert get_supervisor_network_url(hass) is None
hass.config.api = Mock(use_ssl=False, port=8123, local_ip="192.168.123.123")
await async_process_ha_core_config(hass, {})
assert get_supervisor_network_url(hass) is None
mock_component(hass, "hassio")
assert get_supervisor_network_url(hass) == "http://homeassistant:8123"
hass.config.api = Mock(use_ssl=True, port=8123, local_ip="192.168.123.123")
assert get_supervisor_network_url(hass) is None
assert (
get_supervisor_network_url(hass, allow_ssl=True) == "https://homeassistant:8123"
)