httpcore2 2.1.0__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {httpcore2-2.1.0 → httpcore2-2.3.0}/CHANGELOG.md +17 -4
- {httpcore2-2.1.0 → httpcore2-2.3.0}/PKG-INFO +24 -15
- {httpcore2-2.1.0 → httpcore2-2.3.0}/README.md +1 -5
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/__init__.py +2 -2
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/__init__.py +2 -2
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/connection_pool.py +56 -43
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/http11.py +2 -2
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/http2.py +7 -7
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/http_proxy.py +1 -1
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/interfaces.py +8 -8
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/socks_proxy.py +8 -8
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_backends/anyio.py +5 -5
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_backends/auto.py +2 -2
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_backends/base.py +16 -16
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_backends/sync.py +10 -8
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_backends/trio.py +3 -3
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_exceptions.py +1 -1
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_models.py +12 -12
- httpcore2-2.3.0/httpcore2/_ssl.py +12 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/__init__.py +2 -2
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/connection_pool.py +56 -43
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/http11.py +2 -2
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/http2.py +7 -7
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/http_proxy.py +1 -1
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/interfaces.py +8 -8
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/socks_proxy.py +8 -8
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_synchronization.py +10 -10
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_utils.py +3 -3
- {httpcore2-2.1.0 → httpcore2-2.3.0}/pyproject.toml +7 -6
- httpcore2-2.1.0/httpcore2/_ssl.py +0 -9
- {httpcore2-2.1.0 → httpcore2-2.3.0}/.gitignore +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/LICENSE.md +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_api.py +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_async/connection.py +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_backends/__init__.py +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_backends/mock.py +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_sync/connection.py +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/_trace.py +0 -0
- {httpcore2-2.1.0 → httpcore2-2.3.0}/httpcore2/py.typed +0 -0
|
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
6
|
|
|
7
|
+
## 2.3.0 (June 1st, 2026)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* Use `truststore` instead of `certifi` for default SSL verification, loading the operating system's trust store. ([#1002](https://github.com/pydantic/httpx2/pull/1002))
|
|
12
|
+
* Rewrite `_assign_requests_to_connections` as a single-pass loop. ([#974](https://github.com/pydantic/httpx2/pull/974))
|
|
13
|
+
* Use `anyio`'s `fast_acquire` for `Lock` and `Semaphore`. ([#970](https://github.com/pydantic/httpx2/pull/970))
|
|
14
|
+
* Use `memoryview` in `write()` to avoid copies. ([#954](https://github.com/pydantic/httpx2/pull/954))
|
|
15
|
+
|
|
16
|
+
## 2.2.0 (May 16th, 2026)
|
|
17
|
+
|
|
18
|
+
No changes since `2.1.0`. Version bumped to stay in lockstep with `httpx2`.
|
|
19
|
+
|
|
7
20
|
## 2.1.0 (May 15th, 2026)
|
|
8
21
|
|
|
9
22
|
### Removed
|
|
@@ -60,7 +73,7 @@ Historical entries below are from upstream `encode/httpcore`.
|
|
|
60
73
|
## 1.0.5 (March 27th, 2024)
|
|
61
74
|
|
|
62
75
|
- Handle `EndOfStream` exception for anyio backend. (#899)
|
|
63
|
-
- Allow trio `0.25.*` series in package
|
|
76
|
+
- Allow trio `0.25.*` series in package dependencies. (#903)
|
|
64
77
|
|
|
65
78
|
## 1.0.4 (February 21st, 2024)
|
|
66
79
|
|
|
@@ -138,7 +151,7 @@ The project versioning policy is now explicitly governed by SEMVER. See https://
|
|
|
138
151
|
- Allow `ws` and `wss` schemes. Allows us to properly support websocket upgrade connections. (#625)
|
|
139
152
|
- Forwarding HTTP proxies use a connection-per-remote-host. Required by some proxy implementations. (#637)
|
|
140
153
|
- Don't raise `RuntimeError` when closing a connection pool with active connections. Removes some error cases when cancellations are used. (#631)
|
|
141
|
-
- Lazy import `anyio`, so that it's no longer a hard
|
|
154
|
+
- Lazy import `anyio`, so that it's no longer a hard dependency, and isn't imported if unused. (#639)
|
|
142
155
|
|
|
143
156
|
## 0.16.2 (November 25th, 2022)
|
|
144
157
|
|
|
@@ -196,7 +209,7 @@ The project versioning policy is now explicitly governed by SEMVER. See https://
|
|
|
196
209
|
## 0.14.1 (November 12th, 2021)
|
|
197
210
|
|
|
198
211
|
- `max_connections` becomes optional. (Pull #429)
|
|
199
|
-
- `certifi` is now included in the install
|
|
212
|
+
- `certifi` is now included in the install dependencies. (Pull #428)
|
|
200
213
|
- `h2` is now strictly optional. (Pull #428)
|
|
201
214
|
|
|
202
215
|
## 0.14.0 (November 11th, 2021)
|
|
@@ -261,7 +274,7 @@ Note that `curio` support is not currently available in 0.14.0. If you're using
|
|
|
261
274
|
|
|
262
275
|
### Fixed
|
|
263
276
|
|
|
264
|
-
- More
|
|
277
|
+
- More resilient testing for closed connections. (Pull #311)
|
|
265
278
|
- Don't raise exceptions on ungraceful connection closes. (Pull #310)
|
|
266
279
|
|
|
267
280
|
## 0.13.0 (April 21st, 2021)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: httpcore2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A minimal low-level HTTP client.
|
|
5
|
-
Project-URL:
|
|
6
|
-
Project-URL: Homepage, https://
|
|
7
|
-
Project-URL: Source, https://github.com/
|
|
5
|
+
Project-URL: Changelog, https://github.com/pydantic/httpx2/blob/main/src/httpcore2/CHANGELOG.md
|
|
6
|
+
Project-URL: Homepage, https://github.com/pydantic/httpx2
|
|
7
|
+
Project-URL: Source, https://github.com/pydantic/httpx2/blob/main/src/httpcore2
|
|
8
8
|
Author-email: Tom Christie <tom@tomchristie.com>
|
|
9
9
|
Maintainer-email: "Pydantic Services Inc." <engineering@pydantic.dev>
|
|
10
10
|
License-Expression: BSD-3-Clause
|
|
@@ -25,10 +25,10 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
27
|
Requires-Python: >=3.10
|
|
28
|
-
Requires-Dist: certifi
|
|
29
28
|
Requires-Dist: h11>=0.16
|
|
29
|
+
Requires-Dist: truststore>=0.10
|
|
30
30
|
Provides-Extra: asyncio
|
|
31
|
-
Requires-Dist: anyio<5.0,>=4.0; extra == 'asyncio'
|
|
31
|
+
Requires-Dist: anyio<5.0,>=4.5.0; extra == 'asyncio'
|
|
32
32
|
Provides-Extra: http2
|
|
33
33
|
Requires-Dist: h2<5,>=3; extra == 'http2'
|
|
34
34
|
Provides-Extra: socks
|
|
@@ -62,10 +62,6 @@ Some things HTTP Core does do:
|
|
|
62
62
|
* Provides both sync and async interfaces.
|
|
63
63
|
* Async backend support for `asyncio` and `trio`.
|
|
64
64
|
|
|
65
|
-
## Requirements
|
|
66
|
-
|
|
67
|
-
Python 3.8+
|
|
68
|
-
|
|
69
65
|
## Installation
|
|
70
66
|
|
|
71
67
|
For HTTP/1.1 only support, install with:
|
|
@@ -128,7 +124,7 @@ The motivation for `httpcore` is:
|
|
|
128
124
|
The `httpcore` package has the following dependencies...
|
|
129
125
|
|
|
130
126
|
* `h11`
|
|
131
|
-
* `
|
|
127
|
+
* `truststore`
|
|
132
128
|
|
|
133
129
|
And the following optional extras...
|
|
134
130
|
|
|
@@ -154,6 +150,19 @@ All notable changes to this project will be documented in this file.
|
|
|
154
150
|
|
|
155
151
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
156
152
|
|
|
153
|
+
## 2.3.0 (June 1st, 2026)
|
|
154
|
+
|
|
155
|
+
### Changed
|
|
156
|
+
|
|
157
|
+
* Use `truststore` instead of `certifi` for default SSL verification, loading the operating system's trust store. ([#1002](https://github.com/pydantic/httpx2/pull/1002))
|
|
158
|
+
* Rewrite `_assign_requests_to_connections` as a single-pass loop. ([#974](https://github.com/pydantic/httpx2/pull/974))
|
|
159
|
+
* Use `anyio`'s `fast_acquire` for `Lock` and `Semaphore`. ([#970](https://github.com/pydantic/httpx2/pull/970))
|
|
160
|
+
* Use `memoryview` in `write()` to avoid copies. ([#954](https://github.com/pydantic/httpx2/pull/954))
|
|
161
|
+
|
|
162
|
+
## 2.2.0 (May 16th, 2026)
|
|
163
|
+
|
|
164
|
+
No changes since `2.1.0`. Version bumped to stay in lockstep with `httpx2`.
|
|
165
|
+
|
|
157
166
|
## 2.1.0 (May 15th, 2026)
|
|
158
167
|
|
|
159
168
|
### Removed
|
|
@@ -210,7 +219,7 @@ Historical entries below are from upstream `encode/httpcore`.
|
|
|
210
219
|
## 1.0.5 (March 27th, 2024)
|
|
211
220
|
|
|
212
221
|
- Handle `EndOfStream` exception for anyio backend. (#899)
|
|
213
|
-
- Allow trio `0.25.*` series in package
|
|
222
|
+
- Allow trio `0.25.*` series in package dependencies. (#903)
|
|
214
223
|
|
|
215
224
|
## 1.0.4 (February 21st, 2024)
|
|
216
225
|
|
|
@@ -288,7 +297,7 @@ The project versioning policy is now explicitly governed by SEMVER. See https://
|
|
|
288
297
|
- Allow `ws` and `wss` schemes. Allows us to properly support websocket upgrade connections. (#625)
|
|
289
298
|
- Forwarding HTTP proxies use a connection-per-remote-host. Required by some proxy implementations. (#637)
|
|
290
299
|
- Don't raise `RuntimeError` when closing a connection pool with active connections. Removes some error cases when cancellations are used. (#631)
|
|
291
|
-
- Lazy import `anyio`, so that it's no longer a hard
|
|
300
|
+
- Lazy import `anyio`, so that it's no longer a hard dependency, and isn't imported if unused. (#639)
|
|
292
301
|
|
|
293
302
|
## 0.16.2 (November 25th, 2022)
|
|
294
303
|
|
|
@@ -346,7 +355,7 @@ The project versioning policy is now explicitly governed by SEMVER. See https://
|
|
|
346
355
|
## 0.14.1 (November 12th, 2021)
|
|
347
356
|
|
|
348
357
|
- `max_connections` becomes optional. (Pull #429)
|
|
349
|
-
- `certifi` is now included in the install
|
|
358
|
+
- `certifi` is now included in the install dependencies. (Pull #428)
|
|
350
359
|
- `h2` is now strictly optional. (Pull #428)
|
|
351
360
|
|
|
352
361
|
## 0.14.0 (November 11th, 2021)
|
|
@@ -411,7 +420,7 @@ Note that `curio` support is not currently available in 0.14.0. If you're using
|
|
|
411
420
|
|
|
412
421
|
### Fixed
|
|
413
422
|
|
|
414
|
-
- More
|
|
423
|
+
- More resilient testing for closed connections. (Pull #311)
|
|
415
424
|
- Don't raise exceptions on ungraceful connection closes. (Pull #310)
|
|
416
425
|
|
|
417
426
|
## 0.13.0 (April 21st, 2021)
|
|
@@ -23,10 +23,6 @@ Some things HTTP Core does do:
|
|
|
23
23
|
* Provides both sync and async interfaces.
|
|
24
24
|
* Async backend support for `asyncio` and `trio`.
|
|
25
25
|
|
|
26
|
-
## Requirements
|
|
27
|
-
|
|
28
|
-
Python 3.8+
|
|
29
|
-
|
|
30
26
|
## Installation
|
|
31
27
|
|
|
32
28
|
For HTTP/1.1 only support, install with:
|
|
@@ -89,7 +85,7 @@ The motivation for `httpcore` is:
|
|
|
89
85
|
The `httpcore` package has the following dependencies...
|
|
90
86
|
|
|
91
87
|
* `h11`
|
|
92
|
-
* `
|
|
88
|
+
* `truststore`
|
|
93
89
|
|
|
94
90
|
And the following optional extras...
|
|
95
91
|
|
|
@@ -51,7 +51,7 @@ from ._sync import (
|
|
|
51
51
|
# The 'httpcore2.AnyIOBackend' class is conditional on 'anyio' being installed.
|
|
52
52
|
try:
|
|
53
53
|
from ._backends.anyio import AnyIOBackend
|
|
54
|
-
except ImportError: # pragma:
|
|
54
|
+
except ImportError: # pragma: no cover
|
|
55
55
|
|
|
56
56
|
class AnyIOBackend: # type: ignore
|
|
57
57
|
def __init__(self, *args, **kwargs): # type: ignore
|
|
@@ -62,7 +62,7 @@ except ImportError: # pragma: nocover
|
|
|
62
62
|
# The 'httpcore2.TrioBackend' class is conditional on 'trio' being installed.
|
|
63
63
|
try:
|
|
64
64
|
from ._backends.trio import TrioBackend
|
|
65
|
-
except ImportError: # pragma:
|
|
65
|
+
except ImportError: # pragma: no cover
|
|
66
66
|
|
|
67
67
|
class TrioBackend: # type: ignore
|
|
68
68
|
def __init__(self, *args, **kwargs): # type: ignore
|
|
@@ -6,7 +6,7 @@ from .interfaces import AsyncConnectionInterface
|
|
|
6
6
|
|
|
7
7
|
try:
|
|
8
8
|
from .http2 import AsyncHTTP2Connection
|
|
9
|
-
except ImportError: # pragma:
|
|
9
|
+
except ImportError: # pragma: no cover
|
|
10
10
|
|
|
11
11
|
class AsyncHTTP2Connection: # type: ignore
|
|
12
12
|
def __init__(self, *args, **kwargs) -> None: # type: ignore
|
|
@@ -18,7 +18,7 @@ except ImportError: # pragma: nocover
|
|
|
18
18
|
|
|
19
19
|
try:
|
|
20
20
|
from .socks_proxy import AsyncSOCKSProxy
|
|
21
|
-
except ImportError: # pragma:
|
|
21
|
+
except ImportError: # pragma: no cover
|
|
22
22
|
|
|
23
23
|
class AsyncSOCKSProxy: # type: ignore
|
|
24
24
|
def __init__(self, *args, **kwargs) -> None: # type: ignore
|
|
@@ -229,7 +229,7 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
229
229
|
# In this case we clear the connection and try again.
|
|
230
230
|
pool_request.clear_connection()
|
|
231
231
|
else:
|
|
232
|
-
break # pragma:
|
|
232
|
+
break # pragma: no cover
|
|
233
233
|
|
|
234
234
|
except BaseException as exc:
|
|
235
235
|
with self._optional_thread_lock:
|
|
@@ -259,38 +259,49 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
259
259
|
Called whenever a new request is added or removed from the pool.
|
|
260
260
|
|
|
261
261
|
Any closing connections are returned, allowing the I/O for closing
|
|
262
|
-
those connections to be handled
|
|
262
|
+
those connections to be handled separately.
|
|
263
263
|
"""
|
|
264
|
-
closing_connections = []
|
|
264
|
+
closing_connections: list[AsyncConnectionInterface] = []
|
|
265
|
+
retained_connections: list[AsyncConnectionInterface] = []
|
|
265
266
|
|
|
266
|
-
# First we handle cleaning up any connections that are closed
|
|
267
|
-
# have expired their keep-alive,
|
|
268
|
-
for connection in
|
|
267
|
+
# First we handle cleaning up any connections that are closed
|
|
268
|
+
# or have expired their keep-alive, in a single pass.
|
|
269
|
+
for connection in self._connections:
|
|
269
270
|
if connection.is_closed():
|
|
270
|
-
|
|
271
|
-
self._connections.remove(connection)
|
|
271
|
+
continue
|
|
272
272
|
elif connection.has_expired():
|
|
273
|
-
# log: "closing expired connection"
|
|
274
|
-
self._connections.remove(connection)
|
|
275
|
-
closing_connections.append(connection)
|
|
276
|
-
elif (
|
|
277
|
-
connection.is_idle()
|
|
278
|
-
and sum(connection.is_idle() for connection in self._connections) > self._max_keepalive_connections
|
|
279
|
-
):
|
|
280
|
-
# log: "closing idle connection"
|
|
281
|
-
self._connections.remove(connection)
|
|
282
273
|
closing_connections.append(connection)
|
|
274
|
+
else:
|
|
275
|
+
retained_connections.append(connection)
|
|
276
|
+
|
|
277
|
+
# Then we close any surplus idle connections, to enforce the
|
|
278
|
+
# max_keepalive_connections setting.
|
|
279
|
+
idle_surplus = (
|
|
280
|
+
sum(connection.is_idle() for connection in retained_connections) - self._max_keepalive_connections
|
|
281
|
+
)
|
|
282
|
+
if idle_surplus > 0:
|
|
283
|
+
kept: list[AsyncConnectionInterface] = []
|
|
284
|
+
for connection in retained_connections:
|
|
285
|
+
if idle_surplus > 0 and connection.is_idle():
|
|
286
|
+
closing_connections.append(connection)
|
|
287
|
+
idle_surplus -= 1
|
|
288
|
+
else:
|
|
289
|
+
kept.append(connection)
|
|
290
|
+
retained_connections = kept
|
|
291
|
+
|
|
292
|
+
self._connections = retained_connections
|
|
293
|
+
|
|
294
|
+
# Snapshot the set of reusable connections once, rather than rebuilding
|
|
295
|
+
# it per queued request — this is what brings the loop from O(N*M) to
|
|
296
|
+
# O(N+M) in the common case.
|
|
297
|
+
available_connections = [connection for connection in self._connections if connection.is_available()]
|
|
298
|
+
new_connection_budget = self._max_connections - len(self._connections)
|
|
283
299
|
|
|
284
300
|
# Assign queued requests to connections.
|
|
285
|
-
|
|
286
|
-
|
|
301
|
+
for pool_request in self._requests:
|
|
302
|
+
if not pool_request.is_queued():
|
|
303
|
+
continue
|
|
287
304
|
origin = pool_request.request.url.origin
|
|
288
|
-
available_connections = [
|
|
289
|
-
connection
|
|
290
|
-
for connection in self._connections
|
|
291
|
-
if connection.can_handle_request(origin) and connection.is_available()
|
|
292
|
-
]
|
|
293
|
-
idle_connections = [connection for connection in self._connections if connection.is_idle()]
|
|
294
305
|
|
|
295
306
|
# There are three cases for how we may be able to handle the request:
|
|
296
307
|
#
|
|
@@ -298,24 +309,26 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
298
309
|
# 2. We can create a new connection to handle the request.
|
|
299
310
|
# 3. We can close an idle connection and then create a new connection
|
|
300
311
|
# to handle the request.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
connection
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
312
|
+
for connection in available_connections:
|
|
313
|
+
if connection.can_handle_request(origin):
|
|
314
|
+
pool_request.assign_to_connection(connection)
|
|
315
|
+
break
|
|
316
|
+
else:
|
|
317
|
+
if new_connection_budget > 0:
|
|
318
|
+
connection = self.create_connection(origin)
|
|
319
|
+
self._connections.append(connection)
|
|
320
|
+
pool_request.assign_to_connection(connection)
|
|
321
|
+
new_connection_budget -= 1
|
|
322
|
+
continue
|
|
323
|
+
for idx, connection in enumerate(available_connections):
|
|
324
|
+
if connection.is_idle():
|
|
325
|
+
del available_connections[idx]
|
|
326
|
+
self._connections.remove(connection)
|
|
327
|
+
closing_connections.append(connection)
|
|
328
|
+
connection = self.create_connection(origin)
|
|
329
|
+
self._connections.append(connection)
|
|
330
|
+
pool_request.assign_to_connection(connection)
|
|
331
|
+
break
|
|
319
332
|
|
|
320
333
|
return closing_connections
|
|
321
334
|
|
|
@@ -85,9 +85,9 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
85
85
|
await self._send_request_body(**kwargs)
|
|
86
86
|
except WriteError:
|
|
87
87
|
# If we get a write error while we're writing the request,
|
|
88
|
-
# then we
|
|
88
|
+
# then we suppress this error and move on to attempting to
|
|
89
89
|
# read the response. Servers can sometimes close the request
|
|
90
|
-
#
|
|
90
|
+
# preemptively and then respond with a well formed HTTP
|
|
91
91
|
# error response.
|
|
92
92
|
pass
|
|
93
93
|
|
|
@@ -120,7 +120,7 @@ class AsyncHTTP2Connection(AsyncConnectionInterface):
|
|
|
120
120
|
try:
|
|
121
121
|
stream_id = self._h2_state.get_next_available_stream_id()
|
|
122
122
|
self._events[stream_id] = []
|
|
123
|
-
except h2.exceptions.NoAvailableStreamIDError: # pragma:
|
|
123
|
+
except h2.exceptions.NoAvailableStreamIDError: # pragma: no cover
|
|
124
124
|
self._used_all_stream_ids = True
|
|
125
125
|
self._request_count -= 1
|
|
126
126
|
raise ConnectionNotAvailable()
|
|
@@ -161,11 +161,11 @@ class AsyncHTTP2Connection(AsyncConnectionInterface):
|
|
|
161
161
|
#
|
|
162
162
|
# In this case we'll have stored the event, and should raise
|
|
163
163
|
# it as a RemoteProtocolError.
|
|
164
|
-
if self._connection_terminated: # pragma:
|
|
164
|
+
if self._connection_terminated: # pragma: no cover
|
|
165
165
|
raise RemoteProtocolError(self._connection_terminated)
|
|
166
166
|
# If h2 raises a protocol error in some other state then we
|
|
167
167
|
# must somehow have made a protocol violation.
|
|
168
|
-
raise LocalProtocolError(exc) # pragma:
|
|
168
|
+
raise LocalProtocolError(exc) # pragma: no cover
|
|
169
169
|
|
|
170
170
|
raise exc
|
|
171
171
|
|
|
@@ -387,7 +387,7 @@ class AsyncHTTP2Connection(AsyncConnectionInterface):
|
|
|
387
387
|
if self._keepalive_expiry is not None:
|
|
388
388
|
now = time.monotonic()
|
|
389
389
|
self._expire_at = now + self._keepalive_expiry
|
|
390
|
-
if self._used_all_stream_ids: # pragma:
|
|
390
|
+
if self._used_all_stream_ids: # pragma: no cover
|
|
391
391
|
await self.aclose()
|
|
392
392
|
|
|
393
393
|
async def aclose(self) -> None:
|
|
@@ -404,7 +404,7 @@ class AsyncHTTP2Connection(AsyncConnectionInterface):
|
|
|
404
404
|
timeout = timeouts.get("read", None)
|
|
405
405
|
|
|
406
406
|
if self._read_exception is not None:
|
|
407
|
-
raise self._read_exception # pragma:
|
|
407
|
+
raise self._read_exception # pragma: no cover
|
|
408
408
|
|
|
409
409
|
try:
|
|
410
410
|
data = await self._network_stream.read(self.READ_NUM_BYTES, timeout)
|
|
@@ -435,11 +435,11 @@ class AsyncHTTP2Connection(AsyncConnectionInterface):
|
|
|
435
435
|
data_to_send = self._h2_state.data_to_send()
|
|
436
436
|
|
|
437
437
|
if self._write_exception is not None:
|
|
438
|
-
raise self._write_exception # pragma:
|
|
438
|
+
raise self._write_exception # pragma: no cover
|
|
439
439
|
|
|
440
440
|
try:
|
|
441
441
|
await self._network_stream.write(data_to_send, timeout)
|
|
442
|
-
except Exception as exc: # pragma:
|
|
442
|
+
except Exception as exc: # pragma: no cover
|
|
443
443
|
# If we get a network error we should:
|
|
444
444
|
#
|
|
445
445
|
# 1. Save the exception and just raise it immediately on any future write.
|
|
@@ -47,7 +47,7 @@ def merge_headers(
|
|
|
47
47
|
return default_headers + override_headers
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
class AsyncHTTPProxy(AsyncConnectionPool): # pragma:
|
|
50
|
+
class AsyncHTTPProxy(AsyncConnectionPool): # pragma: no cover
|
|
51
51
|
"""
|
|
52
52
|
A connection pool that sends requests via an HTTP proxy.
|
|
53
53
|
"""
|
|
@@ -82,18 +82,18 @@ class AsyncRequestInterface:
|
|
|
82
82
|
await response.aclose()
|
|
83
83
|
|
|
84
84
|
async def handle_async_request(self, request: Request) -> Response:
|
|
85
|
-
raise NotImplementedError() # pragma:
|
|
85
|
+
raise NotImplementedError() # pragma: no cover
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
class AsyncConnectionInterface(AsyncRequestInterface):
|
|
89
89
|
async def aclose(self) -> None:
|
|
90
|
-
raise NotImplementedError() # pragma:
|
|
90
|
+
raise NotImplementedError() # pragma: no cover
|
|
91
91
|
|
|
92
92
|
def info(self) -> str:
|
|
93
|
-
raise NotImplementedError() # pragma:
|
|
93
|
+
raise NotImplementedError() # pragma: no cover
|
|
94
94
|
|
|
95
95
|
def can_handle_request(self, origin: Origin) -> bool:
|
|
96
|
-
raise NotImplementedError() # pragma:
|
|
96
|
+
raise NotImplementedError() # pragma: no cover
|
|
97
97
|
|
|
98
98
|
def is_available(self) -> bool:
|
|
99
99
|
"""
|
|
@@ -111,7 +111,7 @@ class AsyncConnectionInterface(AsyncRequestInterface):
|
|
|
111
111
|
required exceptions if multiple requests are attempted over a connection
|
|
112
112
|
that ends up being established as HTTP/1.1.
|
|
113
113
|
"""
|
|
114
|
-
raise NotImplementedError() # pragma:
|
|
114
|
+
raise NotImplementedError() # pragma: no cover
|
|
115
115
|
|
|
116
116
|
def has_expired(self) -> bool:
|
|
117
117
|
"""
|
|
@@ -120,13 +120,13 @@ class AsyncConnectionInterface(AsyncRequestInterface):
|
|
|
120
120
|
This either means that the connection is idle and it has passed the
|
|
121
121
|
expiry time on its keep-alive, or that server has sent an EOF.
|
|
122
122
|
"""
|
|
123
|
-
raise NotImplementedError() # pragma:
|
|
123
|
+
raise NotImplementedError() # pragma: no cover
|
|
124
124
|
|
|
125
125
|
def is_idle(self) -> bool:
|
|
126
126
|
"""
|
|
127
127
|
Return `True` if the connection is currently idle.
|
|
128
128
|
"""
|
|
129
|
-
raise NotImplementedError() # pragma:
|
|
129
|
+
raise NotImplementedError() # pragma: no cover
|
|
130
130
|
|
|
131
131
|
def is_closed(self) -> bool:
|
|
132
132
|
"""
|
|
@@ -135,4 +135,4 @@ class AsyncConnectionInterface(AsyncRequestInterface):
|
|
|
135
135
|
Used when a response is closed to determine if the connection may be
|
|
136
136
|
returned to the connection pool or not.
|
|
137
137
|
"""
|
|
138
|
-
raise NotImplementedError() # pragma:
|
|
138
|
+
raise NotImplementedError() # pragma: no cover
|
|
@@ -96,7 +96,7 @@ async def _init_socks5_connection(
|
|
|
96
96
|
raise ProxyError(f"Proxy Server could not connect: {reply_code}.")
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
class AsyncSOCKSProxy(AsyncConnectionPool): # pragma:
|
|
99
|
+
class AsyncSOCKSProxy(AsyncConnectionPool): # pragma: no cover
|
|
100
100
|
"""
|
|
101
101
|
A connection pool that sends requests via an HTTP proxy.
|
|
102
102
|
"""
|
|
@@ -254,7 +254,7 @@ class AsyncSocks5Connection(AsyncConnectionInterface):
|
|
|
254
254
|
http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2"
|
|
255
255
|
|
|
256
256
|
# Create the HTTP/1.1 or HTTP/2 connection
|
|
257
|
-
if http2_negotiated or (self._http2 and not self._http1): # pragma:
|
|
257
|
+
if http2_negotiated or (self._http2 and not self._http1): # pragma: no cover
|
|
258
258
|
from .http2 import AsyncHTTP2Connection
|
|
259
259
|
|
|
260
260
|
self._connection = AsyncHTTP2Connection(
|
|
@@ -271,7 +271,7 @@ class AsyncSocks5Connection(AsyncConnectionInterface):
|
|
|
271
271
|
except Exception as exc:
|
|
272
272
|
self._connect_failed = True
|
|
273
273
|
raise exc
|
|
274
|
-
elif not self._connection.is_available(): # pragma:
|
|
274
|
+
elif not self._connection.is_available(): # pragma: no cover
|
|
275
275
|
raise ConnectionNotAvailable()
|
|
276
276
|
|
|
277
277
|
return await self._connection.handle_async_request(request)
|
|
@@ -284,7 +284,7 @@ class AsyncSocks5Connection(AsyncConnectionInterface):
|
|
|
284
284
|
await self._connection.aclose()
|
|
285
285
|
|
|
286
286
|
def is_available(self) -> bool:
|
|
287
|
-
if self._connection is None: # pragma:
|
|
287
|
+
if self._connection is None: # pragma: no cover
|
|
288
288
|
# If HTTP/2 support is enabled, and the resulting connection could
|
|
289
289
|
# end up as HTTP/2 then we should indicate the connection as being
|
|
290
290
|
# available to service multiple requests.
|
|
@@ -294,22 +294,22 @@ class AsyncSocks5Connection(AsyncConnectionInterface):
|
|
|
294
294
|
return self._connection.is_available()
|
|
295
295
|
|
|
296
296
|
def has_expired(self) -> bool:
|
|
297
|
-
if self._connection is None: # pragma:
|
|
297
|
+
if self._connection is None: # pragma: no cover
|
|
298
298
|
return self._connect_failed
|
|
299
299
|
return self._connection.has_expired()
|
|
300
300
|
|
|
301
301
|
def is_idle(self) -> bool:
|
|
302
|
-
if self._connection is None: # pragma:
|
|
302
|
+
if self._connection is None: # pragma: no cover
|
|
303
303
|
return self._connect_failed
|
|
304
304
|
return self._connection.is_idle()
|
|
305
305
|
|
|
306
306
|
def is_closed(self) -> bool:
|
|
307
|
-
if self._connection is None: # pragma:
|
|
307
|
+
if self._connection is None: # pragma: no cover
|
|
308
308
|
return self._connect_failed
|
|
309
309
|
return self._connection.is_closed()
|
|
310
310
|
|
|
311
311
|
def info(self) -> str:
|
|
312
|
-
if self._connection is None: # pragma:
|
|
312
|
+
if self._connection is None: # pragma: no cover
|
|
313
313
|
return "CONNECTION FAILED" if self._connect_failed else "CONNECTING"
|
|
314
314
|
return self._connection.info()
|
|
315
315
|
|
|
@@ -33,7 +33,7 @@ class AnyIOStream(AsyncNetworkStream):
|
|
|
33
33
|
with anyio.fail_after(timeout):
|
|
34
34
|
try:
|
|
35
35
|
return await self._stream.receive(max_bytes=max_bytes)
|
|
36
|
-
except anyio.EndOfStream: # pragma:
|
|
36
|
+
except anyio.EndOfStream: # pragma: no cover
|
|
37
37
|
return b""
|
|
38
38
|
|
|
39
39
|
async def write(self, buffer: bytes, timeout: float | None = None) -> None:
|
|
@@ -74,7 +74,7 @@ class AnyIOStream(AsyncNetworkStream):
|
|
|
74
74
|
standard_compatible=False,
|
|
75
75
|
server_side=False,
|
|
76
76
|
)
|
|
77
|
-
except Exception as exc: # pragma:
|
|
77
|
+
except Exception as exc: # pragma: no cover
|
|
78
78
|
await self.aclose()
|
|
79
79
|
raise exc
|
|
80
80
|
return AnyIOStream(ssl_stream)
|
|
@@ -102,7 +102,7 @@ class AnyIOBackend(AsyncNetworkBackend):
|
|
|
102
102
|
timeout: float | None = None,
|
|
103
103
|
local_address: str | None = None,
|
|
104
104
|
socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
|
|
105
|
-
) -> AsyncNetworkStream: # pragma:
|
|
105
|
+
) -> AsyncNetworkStream: # pragma: no cover
|
|
106
106
|
if socket_options is None:
|
|
107
107
|
socket_options = []
|
|
108
108
|
exc_map = {
|
|
@@ -127,7 +127,7 @@ class AnyIOBackend(AsyncNetworkBackend):
|
|
|
127
127
|
path: str,
|
|
128
128
|
timeout: float | None = None,
|
|
129
129
|
socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
|
|
130
|
-
) -> AsyncNetworkStream: # pragma:
|
|
130
|
+
) -> AsyncNetworkStream: # pragma: no cover
|
|
131
131
|
if socket_options is None:
|
|
132
132
|
socket_options = []
|
|
133
133
|
exc_map = {
|
|
@@ -143,4 +143,4 @@ class AnyIOBackend(AsyncNetworkBackend):
|
|
|
143
143
|
return AnyIOStream(stream)
|
|
144
144
|
|
|
145
145
|
async def sleep(self, seconds: float) -> None:
|
|
146
|
-
await anyio.sleep(seconds) # pragma:
|
|
146
|
+
await anyio.sleep(seconds) # pragma: no cover
|
|
@@ -41,10 +41,10 @@ class AutoBackend(AsyncNetworkBackend):
|
|
|
41
41
|
path: str,
|
|
42
42
|
timeout: float | None = None,
|
|
43
43
|
socket_options: typing.Iterable[SOCKET_OPTION] | None = None,
|
|
44
|
-
) -> AsyncNetworkStream: # pragma:
|
|
44
|
+
) -> AsyncNetworkStream: # pragma: no cover
|
|
45
45
|
await self._init_backend()
|
|
46
46
|
return await self._backend.connect_unix_socket(path, timeout=timeout, socket_options=socket_options)
|
|
47
47
|
|
|
48
|
-
async def sleep(self, seconds: float) -> None: # pragma:
|
|
48
|
+
async def sleep(self, seconds: float) -> None: # pragma: no cover
|
|
49
49
|
await self._init_backend()
|
|
50
50
|
return await self._backend.sleep(seconds)
|