boring-http 2.2.0__tar.gz → 2.2.1__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.
Files changed (29) hide show
  1. {boring_http-2.2.0 → boring_http-2.2.1}/PKG-INFO +2 -2
  2. {boring_http-2.2.0 → boring_http-2.2.1}/README.md +1 -1
  3. {boring_http-2.2.0 → boring_http-2.2.1}/pyproject.toml +1 -1
  4. {boring_http-2.2.0 → boring_http-2.2.1}/pyproject.toml.orig +1 -1
  5. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/http2.py +5 -1
  6. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/http3.py +11 -3
  7. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/__init__.py +0 -0
  8. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/client.py +0 -0
  9. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/codings.py +0 -0
  10. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/cookies.py +0 -0
  11. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/errors.py +0 -0
  12. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/hpack.py +0 -0
  13. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/hpack_tables.py +0 -0
  14. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/http1.py +0 -0
  15. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/message.py +0 -0
  16. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/pool.py +0 -0
  17. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/public_suffixes.py +0 -0
  18. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/py.typed +0 -0
  19. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/qpack.py +0 -0
  20. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/qpack_tables.py +0 -0
  21. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/quic/__init__.py +0 -0
  22. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/quic/connection.py +0 -0
  23. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/quic/packet.py +0 -0
  24. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/quic/protocol.py +0 -0
  25. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/quic/recovery.py +0 -0
  26. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/quic/streams.py +0 -0
  27. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/revocation.py +0 -0
  28. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/timing.py +0 -0
  29. {boring_http-2.2.0 → boring_http-2.2.1}/src/boring_http/websocket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: boring-http
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: Asyncio HTTP client on BoringSSL: HTTP/1.1, HTTP/2 and HTTP/3 over QUIC v1 and v2, WebSocket on all three, HTTP and SOCKS proxies, headers in your order
5
5
  Requires-Dist: boring-ssl2>=1.2.0
6
6
  Requires-Dist: ssl2>=2.0.1
@@ -455,7 +455,7 @@ async def main() -> None:
455
455
  asyncio.run(main())
456
456
  ```
457
457
 
458
- Over HTTP/1.1 this is the Upgrade handshake. Over HTTP/2 and HTTP/3 it is Extended CONNECT when the server advertises `SETTINGS_ENABLE_CONNECT_PROTOCOL`, otherwise the client opens an HTTP/1.1 connection for it. `subprotocols=[...]` offers subprotocols; `websocket.subprotocol` is the one chosen. The `total` of `timeout` covers the handshake only; afterwards `read` and `write` still limit each wait, so `Timeout(read=None)` lets `receive()` wait through any silence.
458
+ Over HTTP/1.1 this is the Upgrade handshake. Over HTTP/2 and HTTP/3 it is Extended CONNECT when the server advertises `SETTINGS_ENABLE_CONNECT_PROTOCOL`, otherwise the client opens an HTTP/1.1 connection for it. Each WebSocket has a connection of its own, which `close()` closes after the closing handshake. `subprotocols=[...]` offers subprotocols; `websocket.subprotocol` is the one chosen. The `total` of `timeout` covers the handshake only; afterwards `read` and `write` still limit each wait, so `Timeout(read=None)` lets `receive()` wait through any silence.
459
459
 
460
460
  ## uvloop
461
461
 
@@ -445,7 +445,7 @@ async def main() -> None:
445
445
  asyncio.run(main())
446
446
  ```
447
447
 
448
- Over HTTP/1.1 this is the Upgrade handshake. Over HTTP/2 and HTTP/3 it is Extended CONNECT when the server advertises `SETTINGS_ENABLE_CONNECT_PROTOCOL`, otherwise the client opens an HTTP/1.1 connection for it. `subprotocols=[...]` offers subprotocols; `websocket.subprotocol` is the one chosen. The `total` of `timeout` covers the handshake only; afterwards `read` and `write` still limit each wait, so `Timeout(read=None)` lets `receive()` wait through any silence.
448
+ Over HTTP/1.1 this is the Upgrade handshake. Over HTTP/2 and HTTP/3 it is Extended CONNECT when the server advertises `SETTINGS_ENABLE_CONNECT_PROTOCOL`, otherwise the client opens an HTTP/1.1 connection for it. Each WebSocket has a connection of its own, which `close()` closes after the closing handshake. `subprotocols=[...]` offers subprotocols; `websocket.subprotocol` is the one chosen. The `total` of `timeout` covers the handshake only; afterwards `read` and `write` still limit each wait, so `Timeout(read=None)` lets `receive()` wait through any silence.
449
449
 
450
450
  ## uvloop
451
451
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "boring-http"
3
- version = "2.2.0"
3
+ version = "2.2.1"
4
4
  description = "Asyncio HTTP client on BoringSSL: HTTP/1.1, HTTP/2 and HTTP/3 over QUIC v1 and v2, WebSocket on all three, HTTP and SOCKS proxies, headers in your order"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "boring-http"
3
- version = "2.2.0"
3
+ version = "2.2.1"
4
4
  description = "Asyncio HTTP client on BoringSSL: HTTP/1.1, HTTP/2 and HTTP/3 over QUIC v1 and v2, WebSocket on all three, HTTP and SOCKS proxies, headers in your order"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.14"
@@ -687,4 +687,8 @@ class Http2StreamTransport:
687
687
  async def close(self) -> None:
688
688
  if not self._closed:
689
689
  self._closed = True
690
- await self._connection.write_data(self._stream, b"", end=True)
690
+ # Client.websocket opens a connection for the tunnel alone, so the connection ends with it
691
+ try:
692
+ await self._connection.write_data(self._stream, b"", end=True)
693
+ finally:
694
+ await self._connection.close()
@@ -1,6 +1,6 @@
1
1
  import asyncio
2
2
  import secrets
3
- from collections.abc import AsyncGenerator, Callable
3
+ from collections.abc import AsyncGenerator, Awaitable, Callable
4
4
  from dataclasses import dataclass
5
5
  from enum import IntEnum
6
6
 
@@ -311,7 +311,7 @@ class Http3Connection:
311
311
  return (
312
312
  status,
313
313
  headers,
314
- Http3StreamTransport(self._quic, stream_id, deadline, frames),
314
+ Http3StreamTransport(self._quic, stream_id, deadline, frames, self.close),
315
315
  )
316
316
 
317
317
  async def close(self) -> None:
@@ -531,12 +531,18 @@ class Http3Connection:
531
531
 
532
532
  class Http3StreamTransport:
533
533
  def __init__(
534
- self, quic: QuicClient, stream_id: int, deadline: Deadline, frames: StreamFrames
534
+ self,
535
+ quic: QuicClient,
536
+ stream_id: int,
537
+ deadline: Deadline,
538
+ frames: StreamFrames,
539
+ close_connection: Callable[[], Awaitable[None]],
535
540
  ) -> None:
536
541
  self._quic = quic
537
542
  self._stream_id = stream_id
538
543
  self._frames = frames
539
544
  self._deadline = deadline
545
+ self._close_connection = close_connection
540
546
  self._pending = b""
541
547
  self._closed = False
542
548
 
@@ -564,3 +570,5 @@ class Http3StreamTransport:
564
570
  if not self._closed:
565
571
  self._closed = True
566
572
  self._quic.send(self._stream_id, b"", end=True)
573
+ # Client.websocket opens a connection for the tunnel alone, so the connection ends with it
574
+ await self._close_connection()