From 2e1037005c4f107e67ced0876788cbe048707b0a Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Mon, 17 May 2021 20:34:25 -0700 Subject: [PATCH] Allow camera stream to fail safely (#50728) --- homeassistant/components/camera/__init__.py | 9 +++++--- tests/components/camera/test_init.py | 23 ++++++++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index b52a36515d8..d9ccb0490c6 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -160,7 +160,7 @@ async def async_get_stream_source(hass: HomeAssistant, entity_id: str) -> str | @bind_hass async def async_get_mjpeg_stream( hass: HomeAssistant, request: web.Request, entity_id: str -) -> web.StreamResponse: +) -> web.StreamResponse | None: """Fetch an mjpeg stream from a camera entity.""" camera = _get_camera_from_entity_id(hass, entity_id) @@ -399,7 +399,7 @@ class Camera(Entity): async def handle_async_mjpeg_stream( self, request: web.Request - ) -> web.StreamResponse: + ) -> web.StreamResponse | None: """Serve an HTTP MJPEG stream from the camera. This method can be overridden by camera platforms to proxy @@ -543,7 +543,10 @@ class CameraMjpegStream(CameraView): """Serve camera stream, possibly with interval.""" interval_str = request.query.get("interval") if interval_str is None: - return await camera.handle_async_mjpeg_stream(request) + stream = await camera.handle_async_mjpeg_stream(request) + if stream is None: + raise web.HTTPBadGateway() + return stream try: # Compose camera stream from stills diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 340a4b5d756..7c7890a3e5f 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -11,7 +11,12 @@ from homeassistant.components.camera.const import DOMAIN, PREF_PRELOAD_STREAM from homeassistant.components.camera.prefs import CameraEntityPreferences from homeassistant.components.websocket_api.const import TYPE_RESULT from homeassistant.config import async_process_ha_core_config -from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_START +from homeassistant.const import ( + ATTR_ENTITY_ID, + EVENT_HOMEASSISTANT_START, + HTTP_BAD_GATEWAY, + HTTP_OK, +) from homeassistant.exceptions import HomeAssistantError from homeassistant.setup import async_setup_component @@ -354,3 +359,19 @@ async def test_record_service(hass, mock_camera, mock_stream): # So long as we call stream.record, the rest should be covered # by those tests. assert mock_record.called + + +async def test_camera_proxy_stream(hass, mock_camera, hass_client): + """Test record service.""" + + client = await hass_client() + + response = await client.get("/api/camera_proxy_stream/camera.demo_camera") + assert response.status == HTTP_OK + + with patch( + "homeassistant.components.demo.camera.DemoCamera.handle_async_mjpeg_stream", + return_value=None, + ): + response = await client.get("/api/camera_proxy_stream/camera.demo_camera") + assert response.status == HTTP_BAD_GATEWAY