diff options
| author | Danilo M. <danix@danix.xyz> | 2026-07-17 11:28:26 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-07-17 11:28:26 +0200 |
| commit | b2517c9270382794b3ec5be73201590b4127d553 (patch) | |
| tree | d0df4689f50b5dc435decc26e3aea2f7ffb532f9 | |
| parent | ade5d98c26da9e0e16685e181377d6c2d5cdf816 (diff) | |
| download | hyprsunset-qt-b2517c9270382794b3ec5be73201590b4127d553.tar.gz hyprsunset-qt-b2517c9270382794b3ec5be73201590b4127d553.zip | |
fix: send User-Agent to avoid Cloudflare 403 on sun API
sunrise-sunset.org sits behind Cloudflare, which rejects the default
Python-urllib User-Agent with 403 (error 1010). Send a normal UA in
_http_get so fetch_sun and geolocate get through.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| -rw-r--r-- | hyprsunset_qt/sun.py | 7 | ||||
| -rw-r--r-- | tests/test_sun.py | 19 |
2 files changed, 23 insertions, 3 deletions
diff --git a/hyprsunset_qt/sun.py b/hyprsunset_qt/sun.py index aba2ae3..d6b8cd9 100644 --- a/hyprsunset_qt/sun.py +++ b/hyprsunset_qt/sun.py @@ -39,9 +39,14 @@ def write_cache(payload: dict, path: Path) -> None: GEO_URL = "http://ip-api.com/json" SUN_URL = "https://api.sunrise-sunset.org/json?lat={lat}&lng={lon}&formatted=0" +# sunrise-sunset.org is behind Cloudflare, which returns 403 (error 1010) for +# the default Python-urllib User-Agent. Send a normal UA so requests get through. +_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) hyprsunset-qt" + def _http_get(url: str, timeout: float = 10.0) -> str: - with urllib.request.urlopen(url, timeout=timeout) as resp: + req = urllib.request.Request(url, headers={"User-Agent": _USER_AGENT}) + with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read().decode() diff --git a/tests/test_sun.py b/tests/test_sun.py index 4e15cb2..033dd26 100644 --- a/tests/test_sun.py +++ b/tests/test_sun.py @@ -1,8 +1,8 @@ import json from datetime import timezone from zoneinfo import ZoneInfo -from unittest.mock import patch -from hyprsunset_qt.sun import to_local_hm, read_cache, write_cache, geolocate, fetch_sun +from unittest.mock import patch, MagicMock +from hyprsunset_qt.sun import to_local_hm, read_cache, write_cache, geolocate, fetch_sun, _http_get def test_utc_to_local_hm(): @@ -22,6 +22,21 @@ def test_read_cache_missing(tmp_path): assert read_cache(tmp_path / "nope.json") is None +def test_http_get_sends_user_agent(): + # sunrise-sunset.org sits behind Cloudflare, which 403s the default + # Python-urllib User-Agent (error 1010). _http_get must send a real UA. + resp = MagicMock() + resp.read.return_value = b"{}" + resp.__enter__.return_value = resp + resp.__exit__.return_value = False + with patch("hyprsunset_qt.sun.urllib.request.urlopen", return_value=resp) as uo: + _http_get("https://example.com/x") + req = uo.call_args[0][0] + # urlopen was called with a Request carrying a non-default User-Agent + assert req.get_header("User-agent") + assert "urllib" not in req.get_header("User-agent").lower() + + def test_geolocate_parses_latlon(): body = json.dumps({"status": "success", "lat": 45.07, "lon": 7.68}) with patch("hyprsunset_qt.sun._http_get", return_value=body): |
