From f7b9d65b1b30804257b42acfeb03f89c34a1d14c Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sat, 19 Oct 2024 20:45:20 -0700 Subject: [PATCH] Add support for URL TTL --- custom_components/hass_proxy/data.py | 2 +- custom_components/hass_proxy/proxy.py | 17 +++++++++++++---- custom_components/hass_proxy/proxy_lib.py | 6 ++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/custom_components/hass_proxy/data.py b/custom_components/hass_proxy/data.py index 2efa89b..f4590ea 100644 --- a/custom_components/hass_proxy/data.py +++ b/custom_components/hass_proxy/data.py @@ -18,7 +18,7 @@ class DynamicProxiedURL: ssl_verification: bool ssl_ciphers: str open_limit: int - time_to_live: int + expiration: int type HASSProxyConfigEntry = ConfigEntry[HASSProxyData] diff --git a/custom_components/hass_proxy/proxy.py b/custom_components/hass_proxy/proxy.py index 3fe5ff4..4fd4aea 100644 --- a/custom_components/hass_proxy/proxy.py +++ b/custom_components/hass_proxy/proxy.py @@ -27,6 +27,7 @@ HASSProxyData, ) from custom_components.hass_proxy.proxy_lib import ( + HASSProxyLibExpiredError, HASSProxyLibNotFoundRequestError, ProxiedURL, ProxyView, @@ -109,7 +110,7 @@ def create_proxied_url(call: ServiceCall) -> None: ssl_verification=call.data["ssl_verification"], ssl_ciphers=call.data["ssl_ciphers"], open_limit=call.data["open_limit"], - time_to_live=time.time() + ttl if ttl else 0, + expiration=time.time() + ttl if ttl else 0, ) def delete_proxied_url(call: ServiceCall) -> None: @@ -141,10 +142,11 @@ def delete_proxied_url(call: ServiceCall) -> None: @callback -async def async_unload_entry(hass: HomeAssistant, _entry: HASSProxyConfigEntry) -> None: +async def async_unload_entry(hass: HomeAssistant, entry: HASSProxyConfigEntry) -> None: """Unload the proxy entry.""" - hass.services.async_remove(DOMAIN, SERVICE_CREATE_PROXIED_URL) - hass.services.async_remove(DOMAIN, SERVICE_DELETE_PROXIED_URL) + if entry.options.get(CONF_DYNAMIC_URLS): + hass.services.async_remove(DOMAIN, SERVICE_CREATE_PROXIED_URL) + hass.services.async_remove(DOMAIN, SERVICE_DELETE_PROXIED_URL) class HAProxyView(ProxyView): @@ -174,6 +176,7 @@ def _get_proxied_url(self, request: web.Request) -> ProxiedURL: options = self._get_options() url_to_proxy = urllib.parse.unquote(request.query["url"]) + has_expired_match = False for proxied_url in self.get_dynamic_proxied_urls().values(): if urlmatch.urlmatch( @@ -181,6 +184,10 @@ def _get_proxied_url(self, request: web.Request) -> ProxiedURL: url_to_proxy, path_required=False, ): + if proxied_url.expiration and proxied_url.expiration < time.time(): + has_expired_match = True + continue + return ProxiedURL( url=url_to_proxy, ssl_context=self._get_ssl_context(proxied_url.ssl_ciphers) @@ -200,6 +207,8 @@ def _get_proxied_url(self, request: web.Request) -> ProxiedURL: else self._get_ssl_context_no_verify(ssl_cipher), ) + if has_expired_match: + raise HASSProxyLibExpiredError raise HASSProxyLibNotFoundRequestError def _get_ssl_context_no_verify( diff --git a/custom_components/hass_proxy/proxy_lib.py b/custom_components/hass_proxy/proxy_lib.py index 09f0ec7..a48b700 100644 --- a/custom_components/hass_proxy/proxy_lib.py +++ b/custom_components/hass_proxy/proxy_lib.py @@ -39,6 +39,10 @@ class HASSProxyLibNotFoundRequestError(HASSProxyLibError): """Exception to indicate something being not found.""" +class HASSProxyLibExpiredError(HASSProxyLibError): + """Exception to indicate a URL match that has expired.""" + + # These proxies are inspired by: # - https://github.com/home-assistant/supervisor/blob/main/supervisor/api/ingress.py # - https://github.com/blakeblackshear/frigate-hass-integration/blob/master/custom_components/frigate/views.py @@ -96,6 +100,8 @@ def _get_proxied_url_or_handle_error( return web.Response(status=HTTPStatus.NOT_FOUND) except HASSProxyLibBadRequestError: return web.Response(status=HTTPStatus.BAD_REQUEST) + except HASSProxyLibExpiredError: + return web.Response(status=HTTPStatus.GONE) if not url or not url.url: return web.Response(status=HTTPStatus.NOT_FOUND)