Support dual stack IP support (IPv4 and IPv6) (#38046)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
fe07d79744
commit
c2f5831181
4 changed files with 78 additions and 12 deletions
|
@ -10,7 +10,6 @@ from homeassistant.components.http import (
|
|||
CONF_SERVER_HOST,
|
||||
CONF_SERVER_PORT,
|
||||
CONF_SSL_CERTIFICATE,
|
||||
DEFAULT_SERVER_HOST,
|
||||
)
|
||||
from homeassistant.const import HTTP_BAD_REQUEST, HTTP_OK, SERVER_PORT
|
||||
|
||||
|
@ -142,10 +141,7 @@ class HassIO:
|
|||
"refresh_token": refresh_token.token,
|
||||
}
|
||||
|
||||
if (
|
||||
http_config.get(CONF_SERVER_HOST, DEFAULT_SERVER_HOST)
|
||||
!= DEFAULT_SERVER_HOST
|
||||
):
|
||||
if http_config.get(CONF_SERVER_HOST) is not None:
|
||||
options["watchdog"] = False
|
||||
_LOGGER.warning(
|
||||
"Found incompatible HTTP option 'server_host'. Watchdog feature disabled"
|
||||
|
|
|
@ -30,6 +30,7 @@ from .cors import setup_cors
|
|||
from .real_ip import setup_real_ip
|
||||
from .static import CACHE_HEADERS, CachingStaticResource
|
||||
from .view import HomeAssistantView # noqa: F401
|
||||
from .web_runner import HomeAssistantTCPSite
|
||||
|
||||
# mypy: allow-untyped-defs, no-check-untyped-defs
|
||||
|
||||
|
@ -53,7 +54,6 @@ SSL_INTERMEDIATE = "intermediate"
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_SERVER_HOST = "0.0.0.0"
|
||||
DEFAULT_DEVELOPMENT = "0"
|
||||
# To be able to load custom cards.
|
||||
DEFAULT_CORS = "https://cast.home-assistant.io"
|
||||
|
@ -69,7 +69,9 @@ HTTP_SCHEMA = vol.All(
|
|||
cv.deprecated(CONF_BASE_URL),
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_SERVER_HOST, default=DEFAULT_SERVER_HOST): cv.string,
|
||||
vol.Optional(CONF_SERVER_HOST): vol.All(
|
||||
cv.ensure_list, vol.Length(min=1), [cv.string]
|
||||
),
|
||||
vol.Optional(CONF_SERVER_PORT, default=SERVER_PORT): cv.port,
|
||||
vol.Optional(CONF_BASE_URL): cv.string,
|
||||
vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
|
||||
|
@ -190,7 +192,7 @@ async def async_setup(hass, config):
|
|||
if conf is None:
|
||||
conf = HTTP_SCHEMA({})
|
||||
|
||||
server_host = conf[CONF_SERVER_HOST]
|
||||
server_host = conf.get(CONF_SERVER_HOST)
|
||||
server_port = conf[CONF_SERVER_PORT]
|
||||
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
|
||||
ssl_peer_certificate = conf.get(CONF_SSL_PEER_CERTIFICATE)
|
||||
|
@ -255,8 +257,9 @@ async def async_setup(hass, config):
|
|||
|
||||
if host:
|
||||
port = None
|
||||
elif server_host != DEFAULT_SERVER_HOST:
|
||||
host = server_host
|
||||
elif server_host is not None:
|
||||
# Assume the first server host name provided as API host
|
||||
host = server_host[0]
|
||||
port = server_port
|
||||
else:
|
||||
host = local_ip
|
||||
|
@ -412,7 +415,8 @@ class HomeAssistantHTTP:
|
|||
|
||||
self.runner = web.AppRunner(self.app)
|
||||
await self.runner.setup()
|
||||
self.site = web.TCPSite(
|
||||
|
||||
self.site = HomeAssistantTCPSite(
|
||||
self.runner, self.server_host, self.server_port, ssl_context=context
|
||||
)
|
||||
try:
|
||||
|
|
67
homeassistant/components/http/web_runner.py
Normal file
67
homeassistant/components/http/web_runner.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
"""HomeAssistant specific aiohttp Site."""
|
||||
import asyncio
|
||||
from ssl import SSLContext
|
||||
from typing import List, Optional, Union
|
||||
|
||||
from aiohttp import web
|
||||
from yarl import URL
|
||||
|
||||
|
||||
class HomeAssistantTCPSite(web.BaseSite):
|
||||
"""HomeAssistant specific aiohttp Site.
|
||||
|
||||
Vanilla TCPSite accepts only str as host. However, the underlying asyncio's
|
||||
create_server() implementation does take a list of strings to bind to multiple
|
||||
host IP's. To support multiple server_host entries (e.g. to enable dual-stack
|
||||
explicitly), we would like to pass an array of strings. Bring our own
|
||||
implementation inspired by TCPSite.
|
||||
|
||||
Custom TCPSite can be dropped when https://github.com/aio-libs/aiohttp/pull/4894
|
||||
is merged.
|
||||
"""
|
||||
|
||||
__slots__ = ("_host", "_port", "_reuse_address", "_reuse_port", "_hosturl")
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
runner: "web.BaseRunner",
|
||||
host: Union[None, str, List[str]],
|
||||
port: int,
|
||||
*,
|
||||
shutdown_timeout: float = 60.0,
|
||||
ssl_context: Optional[SSLContext] = None,
|
||||
backlog: int = 128,
|
||||
reuse_address: Optional[bool] = None,
|
||||
reuse_port: Optional[bool] = None,
|
||||
) -> None: # noqa: D107
|
||||
super().__init__(
|
||||
runner,
|
||||
shutdown_timeout=shutdown_timeout,
|
||||
ssl_context=ssl_context,
|
||||
backlog=backlog,
|
||||
)
|
||||
self._host = host
|
||||
self._port = port
|
||||
self._reuse_address = reuse_address
|
||||
self._reuse_port = reuse_port
|
||||
|
||||
@property
|
||||
def name(self) -> str: # noqa: D102
|
||||
scheme = "https" if self._ssl_context else "http"
|
||||
host = self._host[0] if isinstance(self._host, list) else "0.0.0.0"
|
||||
return str(URL.build(scheme=scheme, host=host, port=self._port))
|
||||
|
||||
async def start(self) -> None: # noqa: D102
|
||||
await super().start()
|
||||
loop = asyncio.get_running_loop()
|
||||
server = self._runner.server
|
||||
assert server is not None
|
||||
self._server = await loop.create_server(
|
||||
server,
|
||||
self._host,
|
||||
self._port,
|
||||
ssl=self._ssl_context,
|
||||
backlog=self._backlog,
|
||||
reuse_address=self._reuse_address,
|
||||
reuse_port=self._reuse_port,
|
||||
)
|
|
@ -113,7 +113,6 @@ def test_secrets(isfile_patch, loop):
|
|||
"cors_allowed_origins": ["http://google.com"],
|
||||
"ip_ban_enabled": True,
|
||||
"login_attempts_threshold": -1,
|
||||
"server_host": "0.0.0.0",
|
||||
"server_port": 8123,
|
||||
"ssl_profile": "modern",
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue