Don't allow setting http config if HA is not running (#177640)

This commit is contained in:
Robert Resch
2026-07-30 15:08:33 +02:00
committed by GitHub
parent 539b349740
commit 0dbbb00a15
2 changed files with 68 additions and 2 deletions
+15 -1
View File
@@ -9,7 +9,7 @@ from homeassistant.components.homeassistant import (
DOMAIN as HASS_DOMAIN,
SERVICE_HOMEASSISTANT_RESTART,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.core import CoreState, HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from .config import HTTP_STORAGE_SCHEMA, ConfData, async_get_and_load_store
@@ -17,6 +17,7 @@ from .const import ATTR_CONFIG, CONF_SERVER_PORT
from .server import async_verify_can_bind
ERR_BIND_FAILED: Final = "bind_failed"
ERR_NOT_RUNNING: Final = "not_running"
@callback
@@ -74,6 +75,10 @@ async def websocket_set_config(
) -> None:
"""Store a new pending HTTP configuration and restart to apply it.
Only allowed while Home Assistant is running: applying a config means
restarting, and restarting a start that has not finished yet leaves
integrations that are still setting up in an undefined state.
A new config is first verified to be applicable by binding its
configured address, so an unusable config is rejected here instead of
being discovered after the restart. The check is skipped when the port
@@ -85,6 +90,15 @@ async def websocket_set_config(
refreshed. The result reports whether a restart was triggered via
``{"restart": bool}``.
"""
if hass.state is not CoreState.running:
connection.send_error(
msg["id"],
ERR_NOT_RUNNING,
"The HTTP configuration can only be changed while Home Assistant "
f"is running, current state: {hass.state.value}",
)
return
config: ConfData | None = msg[ATTR_CONFIG]
if config is not None and config[CONF_SERVER_PORT] != hass.http.server_port:
try:
+53 -1
View File
@@ -33,7 +33,7 @@ from homeassistant.components.http.config import (
)
from homeassistant.components.http.const import ENV_SETUP_PORT, ENV_SUPERVISOR
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, HASSIO_USER_NAME
from homeassistant.core import HomeAssistant
from homeassistant.core import CoreState, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.http import KEY_HASS
@@ -2265,6 +2265,58 @@ async def test_websocket_configure_same_port_skips_bind_check(
assert len(restart_calls) == 1
@pytest.mark.parametrize(
"core_state",
[CoreState.not_running, CoreState.starting],
ids=["not-running", "starting"],
)
async def test_websocket_configure_rejected_while_not_running(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
hass_storage: dict[str, Any],
mock_create_server: Mock,
core_state: CoreState,
) -> None:
"""Staging a new config is rejected while Home Assistant is not running yet."""
assert await async_setup_component(hass, DOMAIN, {})
await async_setup_component(hass, "websocket_api", {})
await hass.async_start()
await hass.async_block_till_done()
restart_calls = async_mock_service(hass, "homeassistant", "restart")
ws_client = await hass_ws_client(hass)
hass.set_state(core_state)
await ws_client.send_json_auto_id(
{"type": "http/config/configure", "config": {"server_port": 9123}}
)
response = await ws_client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "not_running"
assert response["error"]["message"] == (
"The HTTP configuration can only be changed while Home Assistant is "
f"running, current state: {core_state.value}"
)
# The config is neither probed nor stored, and no restart is triggered.
assert mock_create_server.call_count == 1
assert hass_storage[DOMAIN]["data"]["pending"] is None
assert len(restart_calls) == 0
# Once the start finished, the same config is accepted.
hass.set_state(CoreState.running)
await ws_client.send_json_auto_id(
{"type": "http/config/configure", "config": {"server_port": 9123}}
)
response = await ws_client.receive_json()
assert response["success"]
assert response["result"] == {"restart": True}
assert hass_storage[DOMAIN]["data"]["pending"]["server_port"] == 9123
await hass.async_block_till_done()
assert len(restart_calls) == 1
async def test_pending_config_auto_reverts_to_stable(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,