Proxy aiohttp websession / more rebust. (#5419)

This commit is contained in:
Pascal Vizeli 2017-01-19 18:55:27 +01:00 committed by Paulus Schoutsen
parent 9799631797
commit 8da398c0bd
5 changed files with 60 additions and 108 deletions

View File

@ -174,7 +174,7 @@ class Camera(Entity):
yield from asyncio.sleep(.5)
except asyncio.CancelledError:
except (asyncio.CancelledError, ConnectionResetError):
_LOGGER.debug("Close stream by frontend.")
response = None

View File

@ -8,9 +8,6 @@ import asyncio
import logging
import aiohttp
from aiohttp import web
from aiohttp.web_exceptions import HTTPGatewayTimeout
import async_timeout
import voluptuous as vol
import homeassistant.loader as loader
@ -18,7 +15,8 @@ from homeassistant.components.camera import (Camera, PLATFORM_SCHEMA)
from homeassistant.const import (
CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_PORT)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_stream)
REQUIREMENTS = ['amcrest==1.1.0']
@ -108,7 +106,6 @@ class AmcrestCam(Camera):
device_info.get(CONF_USERNAME),
password=device_info.get(CONF_PASSWORD)
)
self._websession = async_create_clientsession(hass)
def camera_image(self):
"""Return a still image reponse from the camera."""
@ -125,44 +122,16 @@ class AmcrestCam(Camera):
return
# Otherwise, stream an MJPEG image stream directly from the camera
websession = async_get_clientsession(self.hass)
streaming_url = '%s/mjpg/video.cgi?channel=0&subtype=%d' % (
self._base_url,
self._resolution
)
stream = None
response = None
try:
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
stream = yield from self._websession.get(
streaming_url,
auth=self._token,
timeout=TIMEOUT
)
response = web.StreamResponse()
response.content_type = stream.headers.get(CONTENT_TYPE_HEADER)
stream_coro = websession.get(
streaming_url, auth=self._token, timeout=TIMEOUT)
yield from response.prepare(request)
while True:
data = yield from stream.content.read(16384)
if not data:
break
response.write(data)
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", streaming_url)
raise HTTPGatewayTimeout()
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
if stream is not None:
stream.close()
if response is not None:
yield from response.write_eof()
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):

View File

@ -9,9 +9,6 @@ import logging
from contextlib import closing
import aiohttp
from aiohttp import web
from aiohttp.web_exceptions import HTTPGatewayTimeout
import async_timeout
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
import voluptuous as vol
@ -20,7 +17,8 @@ from homeassistant.const import (
CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_AUTHENTICATION,
HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION)
from homeassistant.components.camera import (PLATFORM_SCHEMA, Camera)
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_aiohttp_proxy_stream)
from homeassistant.helpers import config_validation as cv
_LOGGER = logging.getLogger(__name__)
@ -137,36 +135,9 @@ class MjpegCamera(Camera):
# connect to stream
websession = async_get_clientsession(self.hass)
stream = None
response = None
try:
with async_timeout.timeout(10, loop=self.hass.loop):
stream = yield from websession.get(self._mjpeg_url,
auth=self._auth)
stream_coro = websession.get(self._mjpeg_url, auth=self._auth)
response = web.StreamResponse()
response.content_type = stream.headers.get(CONTENT_TYPE_HEADER)
yield from response.prepare(request)
while True:
data = yield from stream.content.read(102400)
if not data:
break
response.write(data)
except asyncio.TimeoutError:
raise HTTPGatewayTimeout()
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
if stream is not None:
stream.close()
if response is not None:
yield from response.write_eof()
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):

View File

@ -10,8 +10,6 @@ import logging
import voluptuous as vol
import aiohttp
from aiohttp import web
from aiohttp.web_exceptions import HTTPGatewayTimeout
import async_timeout
from homeassistant.const import (
@ -20,7 +18,8 @@ from homeassistant.const import (
from homeassistant.components.camera import (
Camera, PLATFORM_SCHEMA)
from homeassistant.helpers.aiohttp_client import (
async_get_clientsession, async_create_clientsession)
async_get_clientsession, async_create_clientsession,
async_aiohttp_proxy_stream)
import homeassistant.helpers.config_validation as cv
from homeassistant.util.async import run_coroutine_threadsafe
@ -253,38 +252,10 @@ class SynologyCamera(Camera):
'cameraId': self._camera_id,
'format': 'mjpeg'
}
stream = None
response = None
try:
with async_timeout.timeout(TIMEOUT, loop=self.hass.loop):
stream = yield from self._websession.get(
streaming_url,
params=streaming_payload
)
response = web.StreamResponse()
response.content_type = stream.headers.get(CONTENT_TYPE_HEADER)
stream_coro = self._websession.get(
streaming_url, params=streaming_payload)
yield from response.prepare(request)
while True:
data = yield from stream.content.read(102400)
if not data:
break
response.write(data)
except (asyncio.TimeoutError, aiohttp.errors.ClientError):
_LOGGER.exception("Error on %s", streaming_url)
raise HTTPGatewayTimeout()
except asyncio.CancelledError:
_LOGGER.debug("Close stream by frontend.")
response = None
finally:
if stream is not None:
stream.close()
if response is not None:
yield from response.write_eof()
yield from async_aiohttp_proxy_stream(self.hass, request, stream_coro)
@property
def name(self):

View File

@ -1,9 +1,13 @@
"""Helper for aiohttp webclient stuff."""
import sys
import asyncio
import aiohttp
import sys
import aiohttp
from aiohttp.hdrs import USER_AGENT, CONTENT_TYPE
from aiohttp import web
from aiohttp.web_exceptions import HTTPGatewayTimeout
import async_timeout
from aiohttp.hdrs import USER_AGENT
from homeassistant.core import callback
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.const import __version__
@ -65,6 +69,43 @@ def async_create_clientsession(hass, verify_ssl=True, auto_cleanup=True,
return clientsession
@asyncio.coroutine
def async_aiohttp_proxy_stream(hass, request, stream_coro, buffer_size=102400,
timeout=10):
"""Stream websession request to aiohttp web response."""
response = None
stream = None
try:
with async_timeout.timeout(timeout, loop=hass.loop):
stream = yield from stream_coro
response = web.StreamResponse()
response.content_type = stream.headers.get(CONTENT_TYPE)
yield from response.prepare(request)
while True:
data = yield from stream.content.read(buffer_size)
response.write(data)
except asyncio.TimeoutError:
raise HTTPGatewayTimeout()
except (aiohttp.errors.ClientError,
aiohttp.errors.ClientDisconnectedError):
pass
except (asyncio.CancelledError, ConnectionResetError):
response = None
finally:
if stream is not None:
stream.close()
if response is not None:
yield from response.write_eof()
@callback
# pylint: disable=invalid-name
def _async_register_clientsession_shutdown(hass, clientsession):