You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit a0c1a87
Browse filesBrowse the repository at this point in the historyBrowse files
* fix!: forward an explicit timeout=None as no timeout
* fix: map httpx2 RemoteProtocolError to NetworkError
* fix: map an invalid URL to TransportError when building the request
* fix: raise TransportError from stream() on a closed client
* fix: refuse retry for streaming bodies of requests passed to send()
* fix: reject async middleware when building a sync Client
* fix: read an unread async response body before decoding it
* test: run every line of the client error path tests
Copy file name to clipboardExpand all lines: docs/errors.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,6 +31,19 @@ ClientError (catch-all for anything httpware raises)
31
31
└── ResponseTooLargeError (response body exceeds max_response_body_bytes; status-agnostic)
32
32
```
33
33
34
+
## Mapping `httpx2` exceptions
35
+
36
+
An exception from `httpx2` is re-raised as an httpware one, with the original on `__cause__`:
37
+
38
+
|`httpx2` exception | httpware exception |
39
+
|---|---|
40
+
|`TimeoutException` and its subclasses |`TimeoutError`|
41
+
|`NetworkError` (connect, read, write and close errors) |`NetworkError`|
42
+
|`RemoteProtocolError`, such as a server dropping a keep-alive connection mid-response |`NetworkError`|
43
+
| any other `HTTPError`, `InvalidURL`, `CookieConflict`|`TransportError`|
44
+
45
+
`NetworkError` and `TimeoutError` are the transient ones: `AsyncRetry` and `Retry` retry them, and the circuit breaker counts them. An invalid URL raises `TransportError` from every method, whether `httpx2` rejects it while the request is built or while it is sent. So does a call on a client that has been closed, `stream()` included.
Copy file name to clipboardExpand all lines: docs/index.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -124,7 +124,7 @@ async def main() -> None:
124
124
125
125
`base_url` must not contain a query string; a client built with one raises `ValueError`. Put query parameters shared by every request in `params=` instead.
126
126
127
-
The other keywords of `httpx2.AsyncClient` and `httpx2.Client` (`verify`, `proxy`, `http2`, `transport`, `follow_redirects`, ...) are passed through to the `httpx2` client that httpware builds and closes. Two are refused. `cert` is deprecated by `httpx2` in favour of an `ssl.SSLContext` passed as `verify`. `event_hooks` run below the middleware chain, so use [middleware](middleware.md) instead.
127
+
The other keywords of `httpx2.AsyncClient` and `httpx2.Client` (`verify`, `proxy`, `http2`, `transport`, `follow_redirects`, ...) are passed through to the `httpx2` client that httpware builds and closes. An option set to `None` counts as not passed, so `httpx2` keeps its default, with one exception: `timeout=None` turns the timeout off. Leave `timeout` out to keep the `httpx2` default of 5 seconds. Two options are refused. `cert` is deprecated by `httpx2` in favour of an `ssl.SSLContext` passed as `verify`. `event_hooks` run below the middleware chain, so use [middleware](middleware.md) instead.
Copy file name to clipboardExpand all lines: docs/middleware.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -189,7 +189,7 @@ with Client(base_url="https://api.example.com", middleware=[add_request_id]) as
189
189
client.get("/users/1")
190
190
```
191
191
192
-
Sync and async middleware don't mix: pass `Middleware` to `Client` and `AsyncMiddleware` to `AsyncClient`.
192
+
Sync and async middleware don't mix: pass `Middleware` to `Client` and `AsyncMiddleware` to `AsyncClient`.`Client` raises `TypeError` when you build it with async middleware, such as `AsyncRetry` or a function wrapped by `@async_before_request`.
Copy file name to clipboardExpand all lines: docs/resilience.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -50,6 +50,8 @@ If the request body was an async iterable, `AsyncRetry` does not retry, because
50
50
httpware: not retrying because the request body is a stream that cannot replay across attempts
51
51
```
52
52
53
+
The same applies to a request you build yourself and pass to `send()` or `send_with_response()`, whether it comes from `client.build_request()` or `httpx2.Request()`. `send()` marks it as streaming when its body is not held in memory and is not a multipart upload, so `content=` with a generator is marked, while `content=b"..."`, `json=`, `data=` and `files=` are not. A body built from a list of chunks also counts as streaming here, because `send()` cannot tell it from a generator; `client.post()` and the other verb methods see the original argument and don't mark it.
54
+
53
55
The note, and the `retry.streaming_refused` event on `httpware.retry`, appear only when the failure would otherwise have been retried. A POST with a streaming body, for example, is re-raised without the note because POST is not retried in the first place. See [Observability](observability.md).
0 commit comments