httpcore2 2.0.0__tar.gz → 2.1.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.0.0 → httpcore2-2.1.0}/CHANGELOG.md +33 -2
- {httpcore2-2.0.0 → httpcore2-2.1.0}/LICENSE.md +1 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/PKG-INFO +37 -6
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/__init__.py +1 -3
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/connection.py +8 -28
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/connection_pool.py +14 -41
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/http11.py +13 -41
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/http2.py +21 -69
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/http_proxy.py +8 -29
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/socks_proxy.py +10 -34
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_backends/auto.py +1 -3
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_backends/sync.py +4 -12
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_backends/trio.py +1 -3
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_models.py +13 -43
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/connection.py +8 -28
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/connection_pool.py +14 -41
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/http11.py +13 -41
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/http2.py +21 -69
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/http_proxy.py +8 -29
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/socks_proxy.py +10 -34
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_synchronization.py +4 -12
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_trace.py +1 -3
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_utils.py +4 -14
- httpcore2-2.1.0/pyproject.toml +67 -0
- httpcore2-2.0.0/pyproject.toml +0 -122
- {httpcore2-2.0.0 → httpcore2-2.1.0}/.gitignore +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/README.md +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_api.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/__init__.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_async/interfaces.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_backends/__init__.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_backends/anyio.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_backends/base.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_backends/mock.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_exceptions.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_ssl.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/__init__.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/_sync/interfaces.py +0 -0
- {httpcore2-2.0.0 → httpcore2-2.1.0}/httpcore2/py.typed +0 -0
|
@@ -4,9 +4,40 @@ 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
|
-
##
|
|
7
|
+
## 2.1.0 (May 15th, 2026)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### Removed
|
|
10
|
+
|
|
11
|
+
* Drop support for Python 3.8 and 3.9. ([#208](https://github.com/pydantic/httpx2/pull/208))
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
* Add support for Python 3.14. ([#208](https://github.com/pydantic/httpx2/pull/208))
|
|
16
|
+
* Bundle `LICENSE.md` in the sdist. ([#938](https://github.com/pydantic/httpx2/pull/938))
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
* Wait for positive flow-control credit when a peer's `SETTINGS` frame drives a stream's send window negative, instead of letting h2 raise `LocalProtocolError`. ([#935](https://github.com/pydantic/httpx2/pull/935))
|
|
21
|
+
|
|
22
|
+
## 2.0.0
|
|
23
|
+
|
|
24
|
+
Official first release of `httpcore2`. No changes since `2.0.0b1`.
|
|
25
|
+
|
|
26
|
+
## 2.0.0b1
|
|
27
|
+
|
|
28
|
+
First release of `httpcore2`, a fork of [`httpcore`](https://github.com/encode/httpcore) maintained by Pydantic. Forked from `httpcore 1.0.9`.
|
|
29
|
+
|
|
30
|
+
### Breaking changes
|
|
31
|
+
|
|
32
|
+
* **Renamed package**: `httpcore` -> `httpcore2`. `import httpcore` becomes `import httpcore2`. No other public API changed.
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
* Fix `max_keepalive_connections` not being properly handled. (Inherited from upstream PR [encode/httpcore#1000](https://github.com/encode/httpcore/pull/1000).)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
Historical entries below are from upstream `encode/httpcore`.
|
|
10
41
|
|
|
11
42
|
## Version 1.0.9 (April 24th, 2025)
|
|
12
43
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: httpcore2
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: A minimal low-level HTTP client.
|
|
5
5
|
Project-URL: Documentation, https://www.encode.io/httpcore
|
|
6
6
|
Project-URL: Homepage, https://www.encode.io/httpcore/
|
|
@@ -18,13 +18,13 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
18
18
|
Classifier: Operating System :: OS Independent
|
|
19
19
|
Classifier: Programming Language :: Python :: 3
|
|
20
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
23
21
|
Classifier: Programming Language :: Python :: 3.10
|
|
24
22
|
Classifier: Programming Language :: Python :: 3.11
|
|
25
23
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
26
26
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
|
-
Requires-Python: >=3.
|
|
27
|
+
Requires-Python: >=3.10
|
|
28
28
|
Requires-Dist: certifi
|
|
29
29
|
Requires-Dist: h11>=0.16
|
|
30
30
|
Provides-Extra: asyncio
|
|
@@ -154,9 +154,40 @@ All notable changes to this project will be documented in this file.
|
|
|
154
154
|
|
|
155
155
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
156
156
|
|
|
157
|
-
##
|
|
157
|
+
## 2.1.0 (May 15th, 2026)
|
|
158
158
|
|
|
159
|
-
|
|
159
|
+
### Removed
|
|
160
|
+
|
|
161
|
+
* Drop support for Python 3.8 and 3.9. ([#208](https://github.com/pydantic/httpx2/pull/208))
|
|
162
|
+
|
|
163
|
+
### Added
|
|
164
|
+
|
|
165
|
+
* Add support for Python 3.14. ([#208](https://github.com/pydantic/httpx2/pull/208))
|
|
166
|
+
* Bundle `LICENSE.md` in the sdist. ([#938](https://github.com/pydantic/httpx2/pull/938))
|
|
167
|
+
|
|
168
|
+
### Fixed
|
|
169
|
+
|
|
170
|
+
* Wait for positive flow-control credit when a peer's `SETTINGS` frame drives a stream's send window negative, instead of letting h2 raise `LocalProtocolError`. ([#935](https://github.com/pydantic/httpx2/pull/935))
|
|
171
|
+
|
|
172
|
+
## 2.0.0
|
|
173
|
+
|
|
174
|
+
Official first release of `httpcore2`. No changes since `2.0.0b1`.
|
|
175
|
+
|
|
176
|
+
## 2.0.0b1
|
|
177
|
+
|
|
178
|
+
First release of `httpcore2`, a fork of [`httpcore`](https://github.com/encode/httpcore) maintained by Pydantic. Forked from `httpcore 1.0.9`.
|
|
179
|
+
|
|
180
|
+
### Breaking changes
|
|
181
|
+
|
|
182
|
+
* **Renamed package**: `httpcore` -> `httpcore2`. `import httpcore` becomes `import httpcore2`. No other public API changed.
|
|
183
|
+
|
|
184
|
+
### Fixed
|
|
185
|
+
|
|
186
|
+
* Fix `max_keepalive_connections` not being properly handled. (Inherited from upstream PR [encode/httpcore#1000](https://github.com/encode/httpcore/pull/1000).)
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
Historical entries below are from upstream `encode/httpcore`.
|
|
160
191
|
|
|
161
192
|
## Version 1.0.9 (April 24th, 2025)
|
|
162
193
|
|
|
@@ -55,9 +55,7 @@ except ImportError: # pragma: nocover
|
|
|
55
55
|
|
|
56
56
|
class AnyIOBackend: # type: ignore
|
|
57
57
|
def __init__(self, *args, **kwargs): # type: ignore
|
|
58
|
-
msg =
|
|
59
|
-
"Attempted to use 'httpcore2.AnyIOBackend' but 'anyio' is not installed."
|
|
60
|
-
)
|
|
58
|
+
msg = "Attempted to use 'httpcore2.AnyIOBackend' but 'anyio' is not installed."
|
|
61
59
|
raise RuntimeError(msg)
|
|
62
60
|
|
|
63
61
|
|
|
@@ -58,9 +58,7 @@ class AsyncHTTPConnection(AsyncConnectionInterface):
|
|
|
58
58
|
self._local_address = local_address
|
|
59
59
|
self._uds = uds
|
|
60
60
|
|
|
61
|
-
self._network_backend: AsyncNetworkBackend = (
|
|
62
|
-
AutoBackend() if network_backend is None else network_backend
|
|
63
|
-
)
|
|
61
|
+
self._network_backend: AsyncNetworkBackend = AutoBackend() if network_backend is None else network_backend
|
|
64
62
|
self._connection: AsyncConnectionInterface | None = None
|
|
65
63
|
self._connect_failed: bool = False
|
|
66
64
|
self._request_lock = AsyncLock()
|
|
@@ -68,9 +66,7 @@ class AsyncHTTPConnection(AsyncConnectionInterface):
|
|
|
68
66
|
|
|
69
67
|
async def handle_async_request(self, request: Request) -> Response:
|
|
70
68
|
if not self.can_handle_request(request.url.origin):
|
|
71
|
-
raise RuntimeError(
|
|
72
|
-
f"Attempted to send request to {request.url.origin} on connection to {self._origin}"
|
|
73
|
-
)
|
|
69
|
+
raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}")
|
|
74
70
|
|
|
75
71
|
try:
|
|
76
72
|
async with self._request_lock:
|
|
@@ -78,10 +74,7 @@ class AsyncHTTPConnection(AsyncConnectionInterface):
|
|
|
78
74
|
stream = await self._connect(request)
|
|
79
75
|
|
|
80
76
|
ssl_object = stream.get_extra_info("ssl_object")
|
|
81
|
-
http2_negotiated = (
|
|
82
|
-
ssl_object is not None
|
|
83
|
-
and ssl_object.selected_alpn_protocol() == "h2"
|
|
84
|
-
)
|
|
77
|
+
http2_negotiated = ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2"
|
|
85
78
|
if http2_negotiated or (self._http2 and not self._http1):
|
|
86
79
|
from .http2 import AsyncHTTP2Connection
|
|
87
80
|
|
|
@@ -129,27 +122,18 @@ class AsyncHTTPConnection(AsyncConnectionInterface):
|
|
|
129
122
|
"timeout": timeout,
|
|
130
123
|
"socket_options": self._socket_options,
|
|
131
124
|
}
|
|
132
|
-
async with Trace(
|
|
133
|
-
|
|
134
|
-
) as trace:
|
|
135
|
-
stream = await self._network_backend.connect_unix_socket(
|
|
136
|
-
**kwargs
|
|
137
|
-
)
|
|
125
|
+
async with Trace("connect_unix_socket", logger, request, kwargs) as trace:
|
|
126
|
+
stream = await self._network_backend.connect_unix_socket(**kwargs)
|
|
138
127
|
trace.return_value = stream
|
|
139
128
|
|
|
140
129
|
if self._origin.scheme in (b"https", b"wss"):
|
|
141
|
-
ssl_context = (
|
|
142
|
-
default_ssl_context()
|
|
143
|
-
if self._ssl_context is None
|
|
144
|
-
else self._ssl_context
|
|
145
|
-
)
|
|
130
|
+
ssl_context = default_ssl_context() if self._ssl_context is None else self._ssl_context
|
|
146
131
|
alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"]
|
|
147
132
|
ssl_context.set_alpn_protocols(alpn_protocols)
|
|
148
133
|
|
|
149
134
|
kwargs = {
|
|
150
135
|
"ssl_context": ssl_context,
|
|
151
|
-
"server_hostname": sni_hostname
|
|
152
|
-
or self._origin.host.decode("ascii"),
|
|
136
|
+
"server_hostname": sni_hostname or self._origin.host.decode("ascii"),
|
|
153
137
|
"timeout": timeout,
|
|
154
138
|
}
|
|
155
139
|
async with Trace("start_tls", logger, request, kwargs) as trace:
|
|
@@ -177,11 +161,7 @@ class AsyncHTTPConnection(AsyncConnectionInterface):
|
|
|
177
161
|
# If HTTP/2 support is enabled, and the resulting connection could
|
|
178
162
|
# end up as HTTP/2 then we should indicate the connection as being
|
|
179
163
|
# available to service multiple requests.
|
|
180
|
-
return (
|
|
181
|
-
self._http2
|
|
182
|
-
and (self._origin.scheme == b"https" or not self._http1)
|
|
183
|
-
and not self._connect_failed
|
|
184
|
-
)
|
|
164
|
+
return self._http2 and (self._origin.scheme == b"https" or not self._http1) and not self._connect_failed
|
|
185
165
|
return self._connection.is_available()
|
|
186
166
|
|
|
187
167
|
def has_expired(self) -> bool:
|
|
@@ -30,9 +30,7 @@ class AsyncPoolRequest:
|
|
|
30
30
|
self.connection = None
|
|
31
31
|
self._connection_acquired = AsyncEvent()
|
|
32
32
|
|
|
33
|
-
async def wait_for_connection(
|
|
34
|
-
self, timeout: float | None = None
|
|
35
|
-
) -> AsyncConnectionInterface:
|
|
33
|
+
async def wait_for_connection(self, timeout: float | None = None) -> AsyncConnectionInterface:
|
|
36
34
|
if self.connection is None:
|
|
37
35
|
await self._connection_acquired.wait(timeout=timeout)
|
|
38
36
|
assert self.connection is not None
|
|
@@ -93,17 +91,11 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
93
91
|
"""
|
|
94
92
|
self._ssl_context = ssl_context
|
|
95
93
|
self._proxy = proxy
|
|
96
|
-
self._max_connections =
|
|
97
|
-
sys.maxsize if max_connections is None else max_connections
|
|
98
|
-
)
|
|
94
|
+
self._max_connections = sys.maxsize if max_connections is None else max_connections
|
|
99
95
|
self._max_keepalive_connections = (
|
|
100
|
-
sys.maxsize
|
|
101
|
-
if max_keepalive_connections is None
|
|
102
|
-
else max_keepalive_connections
|
|
103
|
-
)
|
|
104
|
-
self._max_keepalive_connections = min(
|
|
105
|
-
self._max_connections, self._max_keepalive_connections
|
|
96
|
+
sys.maxsize if max_keepalive_connections is None else max_keepalive_connections
|
|
106
97
|
)
|
|
98
|
+
self._max_keepalive_connections = min(self._max_connections, self._max_keepalive_connections)
|
|
107
99
|
|
|
108
100
|
self._keepalive_expiry = keepalive_expiry
|
|
109
101
|
self._http1 = http1
|
|
@@ -112,9 +104,7 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
112
104
|
self._local_address = local_address
|
|
113
105
|
self._uds = uds
|
|
114
106
|
|
|
115
|
-
self._network_backend = (
|
|
116
|
-
AutoBackend() if network_backend is None else network_backend
|
|
117
|
-
)
|
|
107
|
+
self._network_backend = AutoBackend() if network_backend is None else network_backend
|
|
118
108
|
self._socket_options = socket_options
|
|
119
109
|
|
|
120
110
|
# The mutable state on a connection pool is the queue of incoming requests,
|
|
@@ -206,13 +196,9 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
206
196
|
"""
|
|
207
197
|
scheme = request.url.scheme.decode()
|
|
208
198
|
if scheme == "":
|
|
209
|
-
raise UnsupportedProtocol(
|
|
210
|
-
"Request URL is missing an 'http://' or 'https://' protocol."
|
|
211
|
-
)
|
|
199
|
+
raise UnsupportedProtocol("Request URL is missing an 'http://' or 'https://' protocol.")
|
|
212
200
|
if scheme not in ("http", "https", "ws", "wss"):
|
|
213
|
-
raise UnsupportedProtocol(
|
|
214
|
-
f"Request URL has an unsupported protocol '{scheme}://'."
|
|
215
|
-
)
|
|
201
|
+
raise UnsupportedProtocol(f"Request URL has an unsupported protocol '{scheme}://'.")
|
|
216
202
|
|
|
217
203
|
timeouts = request.extensions.get("timeout", {})
|
|
218
204
|
timeout = timeouts.get("pool", None)
|
|
@@ -235,9 +221,7 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
235
221
|
|
|
236
222
|
try:
|
|
237
223
|
# Send the request on the assigned connection.
|
|
238
|
-
response = await connection.handle_async_request(
|
|
239
|
-
pool_request.request
|
|
240
|
-
)
|
|
224
|
+
response = await connection.handle_async_request(pool_request.request)
|
|
241
225
|
except ConnectionNotAvailable:
|
|
242
226
|
# In some cases a connection may initially be available to
|
|
243
227
|
# handle a request, but then become unavailable.
|
|
@@ -263,9 +247,7 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
263
247
|
return Response(
|
|
264
248
|
status=response.status,
|
|
265
249
|
headers=response.headers,
|
|
266
|
-
content=PoolByteStream(
|
|
267
|
-
stream=response.stream, pool_request=pool_request, pool=self
|
|
268
|
-
),
|
|
250
|
+
content=PoolByteStream(stream=response.stream, pool_request=pool_request, pool=self),
|
|
269
251
|
extensions=response.extensions,
|
|
270
252
|
)
|
|
271
253
|
|
|
@@ -293,8 +275,7 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
293
275
|
closing_connections.append(connection)
|
|
294
276
|
elif (
|
|
295
277
|
connection.is_idle()
|
|
296
|
-
and sum(connection.is_idle() for connection in self._connections)
|
|
297
|
-
> self._max_keepalive_connections
|
|
278
|
+
and sum(connection.is_idle() for connection in self._connections) > self._max_keepalive_connections
|
|
298
279
|
):
|
|
299
280
|
# log: "closing idle connection"
|
|
300
281
|
self._connections.remove(connection)
|
|
@@ -309,9 +290,7 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
309
290
|
for connection in self._connections
|
|
310
291
|
if connection.can_handle_request(origin) and connection.is_available()
|
|
311
292
|
]
|
|
312
|
-
idle_connections = [
|
|
313
|
-
connection for connection in self._connections if connection.is_idle()
|
|
314
|
-
]
|
|
293
|
+
idle_connections = [connection for connection in self._connections if connection.is_idle()]
|
|
315
294
|
|
|
316
295
|
# There are three cases for how we may be able to handle the request:
|
|
317
296
|
#
|
|
@@ -369,21 +348,15 @@ class AsyncConnectionPool(AsyncRequestInterface):
|
|
|
369
348
|
class_name = self.__class__.__name__
|
|
370
349
|
with self._optional_thread_lock:
|
|
371
350
|
request_is_queued = [request.is_queued() for request in self._requests]
|
|
372
|
-
connection_is_idle = [
|
|
373
|
-
connection.is_idle() for connection in self._connections
|
|
374
|
-
]
|
|
351
|
+
connection_is_idle = [connection.is_idle() for connection in self._connections]
|
|
375
352
|
|
|
376
353
|
num_active_requests = request_is_queued.count(False)
|
|
377
354
|
num_queued_requests = request_is_queued.count(True)
|
|
378
355
|
num_active_connections = connection_is_idle.count(False)
|
|
379
356
|
num_idle_connections = connection_is_idle.count(True)
|
|
380
357
|
|
|
381
|
-
requests_info =
|
|
382
|
-
|
|
383
|
-
)
|
|
384
|
-
connection_info = (
|
|
385
|
-
f"Connections: {num_active_connections} active, {num_idle_connections} idle"
|
|
386
|
-
)
|
|
358
|
+
requests_info = f"Requests: {num_active_requests} active, {num_queued_requests} queued"
|
|
359
|
+
connection_info = f"Connections: {num_active_connections} active, {num_idle_connections} idle"
|
|
387
360
|
|
|
388
361
|
return f"<{class_name} [{requests_info} | {connection_info}]>"
|
|
389
362
|
|
|
@@ -66,10 +66,7 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
66
66
|
|
|
67
67
|
async def handle_async_request(self, request: Request) -> Response:
|
|
68
68
|
if not self.can_handle_request(request.url.origin):
|
|
69
|
-
raise RuntimeError(
|
|
70
|
-
f"Attempted to send request to {request.url.origin} on connection "
|
|
71
|
-
f"to {self._origin}"
|
|
72
|
-
)
|
|
69
|
+
raise RuntimeError(f"Attempted to send request to {request.url.origin} on connection to {self._origin}")
|
|
73
70
|
|
|
74
71
|
async with self._state_lock:
|
|
75
72
|
if self._state in (HTTPConnectionState.NEW, HTTPConnectionState.IDLE):
|
|
@@ -82,9 +79,7 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
82
79
|
try:
|
|
83
80
|
kwargs = {"request": request}
|
|
84
81
|
try:
|
|
85
|
-
async with Trace(
|
|
86
|
-
"send_request_headers", logger, request, kwargs
|
|
87
|
-
) as trace:
|
|
82
|
+
async with Trace("send_request_headers", logger, request, kwargs) as trace:
|
|
88
83
|
await self._send_request_headers(**kwargs)
|
|
89
84
|
async with Trace("send_request_body", logger, request, kwargs) as trace:
|
|
90
85
|
await self._send_request_body(**kwargs)
|
|
@@ -96,9 +91,7 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
96
91
|
# error response.
|
|
97
92
|
pass
|
|
98
93
|
|
|
99
|
-
async with Trace(
|
|
100
|
-
"receive_response_headers", logger, request, kwargs
|
|
101
|
-
) as trace:
|
|
94
|
+
async with Trace("receive_response_headers", logger, request, kwargs) as trace:
|
|
102
95
|
(
|
|
103
96
|
http_version,
|
|
104
97
|
status,
|
|
@@ -116,9 +109,7 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
116
109
|
network_stream = self._network_stream
|
|
117
110
|
|
|
118
111
|
# CONNECT or Upgrade request
|
|
119
|
-
if (status == 101) or (
|
|
120
|
-
(request.method == b"CONNECT") and (200 <= status < 300)
|
|
121
|
-
):
|
|
112
|
+
if (status == 101) or ((request.method == b"CONNECT") and (200 <= status < 300)):
|
|
122
113
|
network_stream = AsyncHTTP11UpgradeStream(network_stream, trailing_data)
|
|
123
114
|
|
|
124
115
|
return Response(
|
|
@@ -180,10 +171,7 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
180
171
|
event = await self._receive_event(timeout=timeout)
|
|
181
172
|
if isinstance(event, h11.Response):
|
|
182
173
|
break
|
|
183
|
-
if (
|
|
184
|
-
isinstance(event, h11.InformationalResponse)
|
|
185
|
-
and event.status_code == 101
|
|
186
|
-
):
|
|
174
|
+
if isinstance(event, h11.InformationalResponse) and event.status_code == 101:
|
|
187
175
|
break
|
|
188
176
|
|
|
189
177
|
http_version = b"HTTP/" + event.http_version
|
|
@@ -207,17 +195,13 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
207
195
|
elif isinstance(event, (h11.EndOfMessage, h11.PAUSED)):
|
|
208
196
|
break
|
|
209
197
|
|
|
210
|
-
async def _receive_event(
|
|
211
|
-
self, timeout: float | None = None
|
|
212
|
-
) -> h11.Event | type[h11.PAUSED]:
|
|
198
|
+
async def _receive_event(self, timeout: float | None = None) -> h11.Event | type[h11.PAUSED]:
|
|
213
199
|
while True:
|
|
214
200
|
with map_exceptions({h11.RemoteProtocolError: RemoteProtocolError}):
|
|
215
201
|
event = self._h11_state.next_event()
|
|
216
202
|
|
|
217
203
|
if event is h11.NEED_DATA:
|
|
218
|
-
data = await self._network_stream.read(
|
|
219
|
-
self.READ_NUM_BYTES, timeout=timeout
|
|
220
|
-
)
|
|
204
|
+
data = await self._network_stream.read(self.READ_NUM_BYTES, timeout=timeout)
|
|
221
205
|
|
|
222
206
|
# If we feed this case through h11 we'll raise an exception like:
|
|
223
207
|
#
|
|
@@ -238,10 +222,7 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
238
222
|
|
|
239
223
|
async def _response_closed(self) -> None:
|
|
240
224
|
async with self._state_lock:
|
|
241
|
-
if
|
|
242
|
-
self._h11_state.our_state is h11.DONE
|
|
243
|
-
and self._h11_state.their_state is h11.DONE
|
|
244
|
-
):
|
|
225
|
+
if self._h11_state.our_state is h11.DONE and self._h11_state.their_state is h11.DONE:
|
|
245
226
|
self._state = HTTPConnectionState.IDLE
|
|
246
227
|
self._h11_state.start_next_cycle()
|
|
247
228
|
if self._keepalive_expiry is not None:
|
|
@@ -279,9 +260,8 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
279
260
|
# If the HTTP connection is idle but the socket is readable, then the
|
|
280
261
|
# only valid state is that the socket is about to return b"", indicating
|
|
281
262
|
# a server-initiated disconnect.
|
|
282
|
-
server_disconnected = (
|
|
283
|
-
|
|
284
|
-
and self._network_stream.get_extra_info("is_readable")
|
|
263
|
+
server_disconnected = self._state == HTTPConnectionState.IDLE and self._network_stream.get_extra_info(
|
|
264
|
+
"is_readable"
|
|
285
265
|
)
|
|
286
266
|
|
|
287
267
|
return keepalive_expired or server_disconnected
|
|
@@ -294,18 +274,12 @@ class AsyncHTTP11Connection(AsyncConnectionInterface):
|
|
|
294
274
|
|
|
295
275
|
def info(self) -> str:
|
|
296
276
|
origin = str(self._origin)
|
|
297
|
-
return
|
|
298
|
-
f"{origin!r}, HTTP/1.1, {self._state.name}, "
|
|
299
|
-
f"Request Count: {self._request_count}"
|
|
300
|
-
)
|
|
277
|
+
return f"{origin!r}, HTTP/1.1, {self._state.name}, Request Count: {self._request_count}"
|
|
301
278
|
|
|
302
279
|
def __repr__(self) -> str:
|
|
303
280
|
class_name = self.__class__.__name__
|
|
304
281
|
origin = str(self._origin)
|
|
305
|
-
return
|
|
306
|
-
f"<{class_name} [{origin!r}, {self._state.name}, "
|
|
307
|
-
f"Request Count: {self._request_count}]>"
|
|
308
|
-
)
|
|
282
|
+
return f"<{class_name} [{origin!r}, {self._state.name}, Request Count: {self._request_count}]>"
|
|
309
283
|
|
|
310
284
|
# These context managers are not used in the standard flow, but are
|
|
311
285
|
# useful for testing or working with connection instances directly.
|
|
@@ -332,9 +306,7 @@ class HTTP11ConnectionByteStream:
|
|
|
332
306
|
kwargs = {"request": self._request}
|
|
333
307
|
try:
|
|
334
308
|
async with Trace("receive_response_body", logger, self._request, kwargs):
|
|
335
|
-
async with safe_async_iterate(
|
|
336
|
-
self._connection._receive_response_body(**kwargs)
|
|
337
|
-
) as iterator:
|
|
309
|
+
async with safe_async_iterate(self._connection._receive_response_body(**kwargs)) as iterator:
|
|
338
310
|
async for chunk in iterator:
|
|
339
311
|
yield chunk
|
|
340
312
|
except BaseException as exc:
|