diff --git a/apinator/client.py b/apinator/client.py index b86068b..41ce0a2 100644 --- a/apinator/client.py +++ b/apinator/client.py @@ -5,6 +5,7 @@ from __future__ import annotations import json +import platform import time import urllib.request import urllib.error @@ -17,6 +18,15 @@ from .errors import ValidationError, AuthenticationError, ApiError +def _user_agent() -> str: + # Imported here because the package's __init__ imports this module. + from . import __version__ + + # Cloudflare in front of the API blocks urllib's default + # "Python-urllib/x.y" User-Agent (error 1010), so always send our own. + return f"apinator-server-python/{__version__} (Python {platform.python_version()})" + + class Apinator: """ Apinator server SDK client. @@ -229,6 +239,7 @@ def _request( # Set headers req.add_header('Content-Type', 'application/json') + req.add_header('User-Agent', _user_agent()) req.add_header('X-Realtime-Key', self.key) req.add_header('X-Realtime-Timestamp', str(timestamp)) req.add_header('X-Realtime-Signature', signature) diff --git a/tests/test_client.py b/tests/test_client.py index 95365c0..6998b42 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -82,6 +82,8 @@ def test_trigger_single_channel(self): assert f"/apps/{TEST_APP_ID}/events" in request.full_url assert request.get_header('Content-type') == 'application/json' assert request.get_header('X-realtime-key') == TEST_KEY + # Cloudflare blocks urllib's default User-Agent (error 1010). + assert request.get_header('User-agent').startswith('apinator-server-python/') # Verify body body = json.loads(request.data.decode('utf-8'))