Add websocket proxy support (#286)

* Add websocket proxy support

* forward

* update proxy code

* fix import

* fix import

* fix

* reorder

* fix setup

* fix code

* stage al

* fix lint

* convert it into object

* fix lint

* fix url

* fix routing

* update log output

* fix future

* add loop

* Update log messages & error handling

* fix error message

* Update logging

* improve handling

* better error handling

* Fix server read

* fix cancel reader
This commit is contained in:
Pascal Vizeli
2017-12-26 00:51:07 +01:00
committed by GitHub
parent d7df423deb
commit b9ce405ada
4 changed files with 212 additions and 91 deletions

View File

@@ -2,18 +2,13 @@
import asyncio
import logging
import aiohttp
from aiohttp import web
from aiohttp.web_exceptions import HTTPBadGateway
from aiohttp.hdrs import CONTENT_TYPE
import async_timeout
import voluptuous as vol
from .util import api_process, api_process_raw, api_validate
from ..const import (
ATTR_VERSION, ATTR_LAST_VERSION, ATTR_DEVICES, ATTR_IMAGE, ATTR_CUSTOM,
ATTR_BOOT, ATTR_PORT, ATTR_PASSWORD, ATTR_SSL, ATTR_WATCHDOG,
CONTENT_TYPE_BINARY, HEADER_HA_ACCESS)
CONTENT_TYPE_BINARY)
from ..validate import HASS_DEVICES, NETWORK_PORT
_LOGGER = logging.getLogger(__name__)
@@ -46,45 +41,6 @@ class APIHomeAssistant(object):
self.loop = loop
self.homeassistant = homeassistant
async def homeassistant_proxy(self, path, request, timeout=300):
"""Return a client request with proxy origin for Home-Assistant."""
url = f"{self.homeassistant.api_url}/api/{path}"
try:
data = None
headers = {}
method = getattr(
self.homeassistant.websession, request.method.lower())
# read data
with async_timeout.timeout(30, loop=self.loop):
data = await request.read()
if data:
headers.update({CONTENT_TYPE: request.content_type})
# need api password?
if self.homeassistant.api_password:
headers = {HEADER_HA_ACCESS: self.homeassistant.api_password}
# reset headers
if not headers:
headers = None
client = await method(
url, data=data, headers=headers, timeout=timeout
)
return client
except aiohttp.ClientError as err:
_LOGGER.error("Client error on api %s request %s.", path, err)
except asyncio.TimeoutError:
_LOGGER.error("Client timeout error on api request %s.", path)
raise HTTPBadGateway()
@api_process
async def info(self, request):
"""Return host information."""
@@ -169,44 +125,3 @@ class APIHomeAssistant(object):
raise RuntimeError(message)
return True
async def api(self, request):
"""Proxy API request to Home-Assistant."""
path = request.match_info.get('path', '')
_LOGGER.info("Proxy /api/%s request", path)
# API stream
if path.startswith("stream"):
client = await self.homeassistant_proxy(
path, request, timeout=None)
response = web.StreamResponse()
response.content_type = request.headers.get(CONTENT_TYPE)
try:
await response.prepare(request)
while True:
data = await client.content.read(10)
if not data:
await response.write_eof()
break
response.write(data)
except aiohttp.ClientError:
await response.write_eof()
except asyncio.TimeoutError:
pass
finally:
client.close()
# Normal request
else:
client = await self.homeassistant_proxy(path, request)
data = await client.read()
return web.Response(
body=data,
status=client.status,
content_type=client.content_type
)