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 ipaddress
import logging
import socket
import typing
from ssl import SSLCertVerificationError, SSLError
from types import EllipsisType
import httpx
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
__all__ = (
"SigningActor",
"Client",
"AsyncClient",
"RequestError",
)
logger = logging.getLogger(__name__)
class SigningActor(typing.Protocol):
"""
@ -209,8 +222,65 @@ class BaseClient(httpx._client.BaseClient):
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):
# FIXME: Add the fancy methods the sync version has.
# (I'm being lazy because I don't think anyone's making async requests)
pass

Wyświetl plik

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