diff --git a/homeassistant/components/hassio/ingress.py b/homeassistant/components/hassio/ingress.py index f4cc97c3853..0ba83f1ca1b 100644 --- a/homeassistant/components/hassio/ingress.py +++ b/homeassistant/components/hassio/ingress.py @@ -65,6 +65,8 @@ class HassIOIngress(HomeAssistantView): post = _handle put = _handle delete = _handle + patch = _handle + options = _handle async def _handle_websocket( self, request: web.Request, token: str, path: str @@ -209,8 +211,8 @@ def _is_websocket(request: web.Request) -> bool: """Return True if request is a websocket.""" headers = request.headers - if headers.get(hdrs.CONNECTION) == "Upgrade" and \ - headers.get(hdrs.UPGRADE) == "websocket": + if "upgrade" in headers.get(hdrs.CONNECTION, "").lower() and \ + headers.get(hdrs.UPGRADE, "").lower() == "websocket": return True return False diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index 8d5e0ee88b1..ea9ca6ac31f 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -66,7 +66,8 @@ class HomeAssistantView: urls = [self.url] + self.extra_urls routes = [] - for method in ('get', 'post', 'delete', 'put'): + for method in ('get', 'post', 'delete', 'put', 'patch', 'head', + 'options'): handler = getattr(self, method, None) if not handler: diff --git a/tests/components/hassio/test_ingress.py b/tests/components/hassio/test_ingress.py index 343068375de..b4699dfbf8c 100644 --- a/tests/components/hassio/test_ingress.py +++ b/tests/components/hassio/test_ingress.py @@ -1,7 +1,6 @@ """The tests for the hassio component.""" from aiohttp.hdrs import X_FORWARDED_FOR, X_FORWARDED_HOST, X_FORWARDED_PROTO -from aiohttp.client_exceptions import WSServerHandshakeError import pytest @@ -137,6 +136,72 @@ async def test_ingress_request_delete( assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") + ]) +async def test_ingress_request_patch( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.patch("http://127.0.0.1/ingress/{}/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.patch( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + +@pytest.mark.parametrize( + 'build_type', [ + ("a3_vl", "test/beer/ping?index=1"), ("core", "index.html"), + ("local", "panel/config"), ("jk_921", "editor.php?idx=3&ping=5"), + ("fsadjf10312", "") + ]) +async def test_ingress_request_options( + hassio_client, build_type, aioclient_mock): + """Test no auth needed for .""" + aioclient_mock.options("http://127.0.0.1/ingress/{}/{}".format( + build_type[0], build_type[1]), text="test") + + resp = await hassio_client.options( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) + + # Check we got right response + assert resp.status == 200 + body = await resp.text() + assert body == "test" + + # Check we forwarded command + assert len(aioclient_mock.mock_calls) == 1 + assert aioclient_mock.mock_calls[-1][3]["X-Hassio-Key"] == "123456" + assert aioclient_mock.mock_calls[-1][3]["X-Ingress-Path"] == \ + "/api/hassio_ingress/{}".format(build_type[0]) + assert aioclient_mock.mock_calls[-1][3]["X-Test-Header"] == "beer" + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_FOR] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_HOST] + assert aioclient_mock.mock_calls[-1][3][X_FORWARDED_PROTO] + + @pytest.mark.parametrize( 'build_type', [ ("a3_vl", "test/beer/ws"), ("core", "ws.php"), @@ -150,11 +215,10 @@ async def test_ingress_websocket( build_type[0], build_type[1])) # Ignore error because we can setup a full IO infrastructure - with pytest.raises(WSServerHandshakeError): - await hassio_client.ws_connect( - '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), - headers={"X-Test-Header": "beer"} - ) + await hassio_client.ws_connect( + '/api/hassio_ingress/{}/{}'.format(build_type[0], build_type[1]), + headers={"X-Test-Header": "beer"} + ) # Check we forwarded command assert len(aioclient_mock.mock_calls) == 1 diff --git a/tests/test_util/aiohttp.py b/tests/test_util/aiohttp.py index ab759f03058..8c4a2073ad8 100644 --- a/tests/test_util/aiohttp.py +++ b/tests/test_util/aiohttp.py @@ -82,6 +82,10 @@ class AiohttpClientMocker: """Register a mock options request.""" self.request('options', *args, **kwargs) + def patch(self, *args, **kwargs): + """Register a mock patch request.""" + self.request('patch', *args, **kwargs) + @property def call_count(self): """Return the number of requests made.""" @@ -102,7 +106,7 @@ class AiohttpClientMocker: async def match_request(self, method, url, *, data=None, auth=None, params=None, headers=None, allow_redirects=None, - timeout=None, json=None, cookies=None): + timeout=None, json=None, cookies=None, **kwargs): """Match a request against pre-registered requests.""" data = data or json url = URL(url)