mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
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
@ -10,7 +10,6 @@ from homeassistant.components.http import (
|
|||||||
CONF_SERVER_HOST,
|
CONF_SERVER_HOST,
|
||||||
CONF_SERVER_PORT,
|
CONF_SERVER_PORT,
|
||||||
CONF_SSL_CERTIFICATE,
|
CONF_SSL_CERTIFICATE,
|
||||||
DEFAULT_SERVER_HOST,
|
|
||||||
)
|
)
|
||||||
from homeassistant.const import HTTP_BAD_REQUEST, HTTP_OK, SERVER_PORT
|
from homeassistant.const import HTTP_BAD_REQUEST, HTTP_OK, SERVER_PORT
|
||||||
|
|
||||||
@ -142,10 +141,7 @@ class HassIO:
|
|||||||
"refresh_token": refresh_token.token,
|
"refresh_token": refresh_token.token,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if http_config.get(CONF_SERVER_HOST) is not None:
|
||||||
http_config.get(CONF_SERVER_HOST, DEFAULT_SERVER_HOST)
|
|
||||||
!= DEFAULT_SERVER_HOST
|
|
||||||
):
|
|
||||||
options["watchdog"] = False
|
options["watchdog"] = False
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Found incompatible HTTP option 'server_host'. Watchdog feature disabled"
|
"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 .real_ip import setup_real_ip
|
||||||
from .static import CACHE_HEADERS, CachingStaticResource
|
from .static import CACHE_HEADERS, CachingStaticResource
|
||||||
from .view import HomeAssistantView # noqa: F401
|
from .view import HomeAssistantView # noqa: F401
|
||||||
|
from .web_runner import HomeAssistantTCPSite
|
||||||
|
|
||||||
# mypy: allow-untyped-defs, no-check-untyped-defs
|
# mypy: allow-untyped-defs, no-check-untyped-defs
|
||||||
|
|
||||||
@ -53,7 +54,6 @@ SSL_INTERMEDIATE = "intermediate"
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_SERVER_HOST = "0.0.0.0"
|
|
||||||
DEFAULT_DEVELOPMENT = "0"
|
DEFAULT_DEVELOPMENT = "0"
|
||||||
# To be able to load custom cards.
|
# To be able to load custom cards.
|
||||||
DEFAULT_CORS = "https://cast.home-assistant.io"
|
DEFAULT_CORS = "https://cast.home-assistant.io"
|
||||||
@ -69,7 +69,9 @@ HTTP_SCHEMA = vol.All(
|
|||||||
cv.deprecated(CONF_BASE_URL),
|
cv.deprecated(CONF_BASE_URL),
|
||||||
vol.Schema(
|
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_SERVER_PORT, default=SERVER_PORT): cv.port,
|
||||||
vol.Optional(CONF_BASE_URL): cv.string,
|
vol.Optional(CONF_BASE_URL): cv.string,
|
||||||
vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
|
vol.Optional(CONF_SSL_CERTIFICATE): cv.isfile,
|
||||||
@ -190,7 +192,7 @@ async def async_setup(hass, config):
|
|||||||
if conf is None:
|
if conf is None:
|
||||||
conf = HTTP_SCHEMA({})
|
conf = HTTP_SCHEMA({})
|
||||||
|
|
||||||
server_host = conf[CONF_SERVER_HOST]
|
server_host = conf.get(CONF_SERVER_HOST)
|
||||||
server_port = conf[CONF_SERVER_PORT]
|
server_port = conf[CONF_SERVER_PORT]
|
||||||
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
|
ssl_certificate = conf.get(CONF_SSL_CERTIFICATE)
|
||||||
ssl_peer_certificate = conf.get(CONF_SSL_PEER_CERTIFICATE)
|
ssl_peer_certificate = conf.get(CONF_SSL_PEER_CERTIFICATE)
|
||||||
@ -255,8 +257,9 @@ async def async_setup(hass, config):
|
|||||||
|
|
||||||
if host:
|
if host:
|
||||||
port = None
|
port = None
|
||||||
elif server_host != DEFAULT_SERVER_HOST:
|
elif server_host is not None:
|
||||||
host = server_host
|
# Assume the first server host name provided as API host
|
||||||
|
host = server_host[0]
|
||||||
port = server_port
|
port = server_port
|
||||||
else:
|
else:
|
||||||
host = local_ip
|
host = local_ip
|
||||||
@ -412,7 +415,8 @@ class HomeAssistantHTTP:
|
|||||||
|
|
||||||
self.runner = web.AppRunner(self.app)
|
self.runner = web.AppRunner(self.app)
|
||||||
await self.runner.setup()
|
await self.runner.setup()
|
||||||
self.site = web.TCPSite(
|
|
||||||
|
self.site = HomeAssistantTCPSite(
|
||||||
self.runner, self.server_host, self.server_port, ssl_context=context
|
self.runner, self.server_host, self.server_port, ssl_context=context
|
||||||
)
|
)
|
||||||
try:
|
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"],
|
"cors_allowed_origins": ["http://google.com"],
|
||||||
"ip_ban_enabled": True,
|
"ip_ban_enabled": True,
|
||||||
"login_attempts_threshold": -1,
|
"login_attempts_threshold": -1,
|
||||||
"server_host": "0.0.0.0",
|
|
||||||
"server_port": 8123,
|
"server_port": 8123,
|
||||||
"ssl_profile": "modern",
|
"ssl_profile": "modern",
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user