Add Client extensions to get, request, post2

pull/679/head
Jamie Bliss 2024-01-12 00:02:44 -05:00
rodzic bbc5cd989f
commit d8acdf4005
Nie znaleziono w bazie danych klucza dla tego podpisu
2 zmienionych plików z 73 dodań i 10 usunięć

Wyświetl plik

@ -10,16 +10,29 @@ The API is identical to httpx, but some features has been added:
""" """
import asyncio import asyncio
import ipaddress import ipaddress
import logging
import socket import socket
import typing import typing
from ssl import SSLCertVerificationError, SSLError
from types import EllipsisType from types import EllipsisType
import httpx import httpx
from django.conf import settings from django.conf import settings
from httpx._types import TimeoutTypes from httpx import RequestError
from httpx._types import TimeoutTypes, URLTypes
from idna.core import InvalidCodepoint
from .signatures import HttpSignature from .signatures import HttpSignature
__all__ = (
"SigningActor",
"Client",
"AsyncClient",
"RequestError",
)
logger = logging.getLogger(__name__)
class SigningActor(typing.Protocol): class SigningActor(typing.Protocol):
""" """
@ -209,8 +222,65 @@ class BaseClient(httpx._client.BaseClient):
class Client(BaseClient, httpx.Client): class Client(BaseClient, httpx.Client):
pass def request(self, url: URLTypes, method: str, **params) -> httpx.Response:
"""
Wraps some errors up nicer
"""
try:
response = super().request(
method, url, follow_redirects=method == "get", **params
)
except SSLError as invalid_cert:
# Not our problem if the other end doesn't have proper SSL
logger.info("Invalid cert on %s %s", url, invalid_cert)
raise SSLCertVerificationError(invalid_cert) from invalid_cert
except InvalidCodepoint as ex:
# Convert to a more generic error we handle
raise httpx.HTTPError(f"InvalidCodepoint: {str(ex)}") from None
else:
return response
# Deliberately not doing the above to stream() because those use cases don't
# want that handling
def get(
self, url: URLTypes, *, accept: str | None = "application/ld+json", **params
):
"""
Args:
accept: Accept header, set to None to get the open option
"""
if accept:
params.setdefault("headers", {})["Accept"] = accept
return super().get(url, **params)
def post2(self, url: URLTypes, *, activity=None, **params):
"""
Like .post() but:
* Adds activity which is like json but for activities
* Handles response errors a bit
"""
if activity is not None:
params["json"] = activity
params.setdefault("headers", {}).setdefault(
"Content-Type", "application/activity+json"
)
response = self.post(url, **params)
if (
response.status_code >= 400
and response.status_code < 500
and response.status_code != 404
):
raise ValueError(
f"POST error to {url}: {response.status_code} {response.content!r}"
)
return response
class AsyncClient(BaseClient, httpx.AsyncClient): class AsyncClient(BaseClient, httpx.AsyncClient):
# FIXME: Add the fancy methods the sync version has.
# (I'm being lazy because I don't think anyone's making async requests)
pass pass

Wyświetl plik

@ -1,5 +1,3 @@
import json
import httpx import httpx
import pytest import pytest
from django.test.client import RequestFactory from django.test.client import RequestFactory
@ -116,15 +114,10 @@ def test_sign_request(keypair):
"type": "Note", "type": "Note",
}, },
} }
# Normally, Client.post() handles all this encoding. But that doesn't apply
# to raw Request objects.
request = httpx.Request( request = httpx.Request(
"POST", "POST",
"https://example.com/test-actor", "https://example.com/test-actor",
headers={ json=document,
"Content-Type": "application/json",
},
content=json.dumps(document).encode("utf-8"),
) )
# Send the signed request to the mock library # Send the signed request to the mock library
HttpSignature.sign_request( HttpSignature.sign_request(