Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for URL TTLs #8

Merged
merged 1 commit into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion custom_components/hass_proxy/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
17 changes: 13 additions & 4 deletions custom_components/hass_proxy/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
HASSProxyData,
)
from custom_components.hass_proxy.proxy_lib import (
HASSProxyLibExpiredError,
HASSProxyLibNotFoundRequestError,
ProxiedURL,
ProxyView,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -174,13 +176,18 @@ 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(
proxied_url.url_pattern,
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)
Expand All @@ -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(
Expand Down
6 changes: 6 additions & 0 deletions custom_components/hass_proxy/proxy_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down