boring-http 2.2.1__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.
Files changed (29) hide show
  1. {boring_http-2.2.1 → boring_http-2.3.0}/PKG-INFO +4 -4
  2. {boring_http-2.2.1 → boring_http-2.3.0}/README.md +1 -1
  3. {boring_http-2.2.1 → boring_http-2.3.0}/pyproject.toml +3 -3
  4. {boring_http-2.2.1 → boring_http-2.3.0}/pyproject.toml.orig +3 -3
  5. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/client.py +16 -4
  6. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/__init__.py +0 -0
  7. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/codings.py +0 -0
  8. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/cookies.py +0 -0
  9. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/errors.py +0 -0
  10. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/hpack.py +0 -0
  11. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/hpack_tables.py +0 -0
  12. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/http1.py +0 -0
  13. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/http2.py +0 -0
  14. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/http3.py +0 -0
  15. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/message.py +0 -0
  16. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/pool.py +0 -0
  17. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/public_suffixes.py +0 -0
  18. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/py.typed +0 -0
  19. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/qpack.py +0 -0
  20. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/qpack_tables.py +0 -0
  21. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/quic/__init__.py +0 -0
  22. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/quic/connection.py +0 -0
  23. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/quic/packet.py +0 -0
  24. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/quic/protocol.py +0 -0
  25. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/quic/recovery.py +0 -0
  26. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/quic/streams.py +0 -0
  27. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/revocation.py +0 -0
  28. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/timing.py +0 -0
  29. {boring_http-2.2.1 → boring_http-2.3.0}/src/boring_http/websocket.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: boring-http
3
- Version: 2.2.1
3
+ Version: 2.3.0
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
- Requires-Dist: boring-ssl2>=1.2.0
6
- Requires-Dist: ssl2>=2.0.1
5
+ Requires-Dist: boring-ssl2>=1.3.0
6
+ Requires-Dist: ssl2>=2.1.0
7
7
  Requires-Python: >=3.14
8
8
  Project-URL: Repository, https://github.com/baobien-dev/boring-http
9
9
  Description-Content-Type: text/markdown
@@ -42,7 +42,7 @@ async def main() -> None:
42
42
  asyncio.run(main())
43
43
  ```
44
44
 
45
- Certificates are checked against the system trust store, or against `trust_store=TrustStore.from_file(...)` from `ssl2`; `verify=False` turns the check off for a test server. Revocation is checked against the CRL the certificate names: fetched over HTTP, verified with the issuer from the chain, cached until its next update, and a listed certificate is refused with `RevocationError`. `revocation=Revocation.STRICT` also refuses when no valid CRL can be had, `Revocation.NONE` skips the check, and the default `Revocation.SOFT` lets such a connection through, as browsers do. The check covers the origin's certificate and an HTTPS proxy's own certificate on a plain-HTTP request through it; the proxy's certificate on a TLS tunnel is verified but not checked for revocation. `certificate_chain=SigningChain(...)` from `ssl2` presents a client certificate over TCP and QUIC. `check_peer=` is awaited with the peer's chain in DER, leaf first, and its authority after the same checks; raising from it refuses the connection, which is where a key pin goes. Connections are pooled per origin and proxy under `max_connections` and `max_connections_per_host`; one that has served nothing for `max_idle_time` seconds, 90 by default, is closed instead of reused, and `None` keeps it until the server closes it. Headers go on the wire in the order given, client headers first and then the request's own. `Host` leads an HTTP/1.1 request; HTTP/2 and HTTP/3 lowercase the names and drop the connection-specific ones, nothing else changes. `params`, `json`, `data`, `content`, `files`, `auth` and `cookies` are accepted by `get`, `post`, `put`, `patch`, `delete`, `head`, `options` and `request`.
45
+ Certificates are checked against the system trust store, or against `trust_store=TrustStore.from_file(...)` from `ssl2`; `verify=False` turns the check off for a test server. Revocation is checked against the CRL the certificate names: fetched over HTTP, verified with the issuer from the chain, cached until its next update, and a listed certificate is refused with `RevocationError`. `revocation=Revocation.STRICT` also refuses when no valid CRL can be had, `Revocation.NONE` skips the check, and the default `Revocation.SOFT` lets such a connection through, as browsers do. The check covers the origin's certificate and an HTTPS proxy's own certificate on a plain-HTTP request through it; the proxy's certificate on a TLS tunnel is verified but not checked for revocation. `certificate_chain=SigningChain(...)` from `ssl2` presents a client certificate over TCP and QUIC. `check_peer=` is awaited with the peer's chain in DER, leaf first, and its authority after the same checks; raising from it refuses the connection, which is where a key pin goes. `key_log="keys.txt"` appends the secrets of every TLS and QUIC handshake in the NSS key log format, for Wireshark. `resolve={("example.com", 443): ("203.0.113.7", 443)}` sends a connection without a proxy to that address instead of resolving the name, while SNI and certificate verification keep the name from the URL. Connections are pooled per origin and proxy under `max_connections` and `max_connections_per_host`; one that has served nothing for `max_idle_time` seconds, 90 by default, is closed instead of reused, and `None` keeps it until the server closes it. Headers go on the wire in the order given, client headers first and then the request's own. `Host` leads an HTTP/1.1 request; HTTP/2 and HTTP/3 lowercase the names and drop the connection-specific ones, nothing else changes. `params`, `json`, `data`, `content`, `files`, `auth` and `cookies` are accepted by `get`, `post`, `put`, `patch`, `delete`, `head`, `options` and `request`.
46
46
 
47
47
  ## Responses
48
48
 
@@ -32,7 +32,7 @@ async def main() -> None:
32
32
  asyncio.run(main())
33
33
  ```
34
34
 
35
- Certificates are checked against the system trust store, or against `trust_store=TrustStore.from_file(...)` from `ssl2`; `verify=False` turns the check off for a test server. Revocation is checked against the CRL the certificate names: fetched over HTTP, verified with the issuer from the chain, cached until its next update, and a listed certificate is refused with `RevocationError`. `revocation=Revocation.STRICT` also refuses when no valid CRL can be had, `Revocation.NONE` skips the check, and the default `Revocation.SOFT` lets such a connection through, as browsers do. The check covers the origin's certificate and an HTTPS proxy's own certificate on a plain-HTTP request through it; the proxy's certificate on a TLS tunnel is verified but not checked for revocation. `certificate_chain=SigningChain(...)` from `ssl2` presents a client certificate over TCP and QUIC. `check_peer=` is awaited with the peer's chain in DER, leaf first, and its authority after the same checks; raising from it refuses the connection, which is where a key pin goes. Connections are pooled per origin and proxy under `max_connections` and `max_connections_per_host`; one that has served nothing for `max_idle_time` seconds, 90 by default, is closed instead of reused, and `None` keeps it until the server closes it. Headers go on the wire in the order given, client headers first and then the request's own. `Host` leads an HTTP/1.1 request; HTTP/2 and HTTP/3 lowercase the names and drop the connection-specific ones, nothing else changes. `params`, `json`, `data`, `content`, `files`, `auth` and `cookies` are accepted by `get`, `post`, `put`, `patch`, `delete`, `head`, `options` and `request`.
35
+ Certificates are checked against the system trust store, or against `trust_store=TrustStore.from_file(...)` from `ssl2`; `verify=False` turns the check off for a test server. Revocation is checked against the CRL the certificate names: fetched over HTTP, verified with the issuer from the chain, cached until its next update, and a listed certificate is refused with `RevocationError`. `revocation=Revocation.STRICT` also refuses when no valid CRL can be had, `Revocation.NONE` skips the check, and the default `Revocation.SOFT` lets such a connection through, as browsers do. The check covers the origin's certificate and an HTTPS proxy's own certificate on a plain-HTTP request through it; the proxy's certificate on a TLS tunnel is verified but not checked for revocation. `certificate_chain=SigningChain(...)` from `ssl2` presents a client certificate over TCP and QUIC. `check_peer=` is awaited with the peer's chain in DER, leaf first, and its authority after the same checks; raising from it refuses the connection, which is where a key pin goes. `key_log="keys.txt"` appends the secrets of every TLS and QUIC handshake in the NSS key log format, for Wireshark. `resolve={("example.com", 443): ("203.0.113.7", 443)}` sends a connection without a proxy to that address instead of resolving the name, while SNI and certificate verification keep the name from the URL. Connections are pooled per origin and proxy under `max_connections` and `max_connections_per_host`; one that has served nothing for `max_idle_time` seconds, 90 by default, is closed instead of reused, and `None` keeps it until the server closes it. Headers go on the wire in the order given, client headers first and then the request's own. `Host` leads an HTTP/1.1 request; HTTP/2 and HTTP/3 lowercase the names and drop the connection-specific ones, nothing else changes. `params`, `json`, `data`, `content`, `files`, `auth` and `cookies` are accepted by `get`, `post`, `put`, `patch`, `delete`, `head`, `options` and `request`.
36
36
 
37
37
  ## Responses
38
38
 
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "boring-http"
3
- version = "2.2.1"
3
+ version = "2.3.0"
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"
7
7
  dependencies = [
8
- "boring-ssl2>=1.2.0",
9
- "ssl2>=2.0.1",
8
+ "boring-ssl2>=1.3.0",
9
+ "ssl2>=2.1.0",
10
10
  ]
11
11
 
12
12
  [project.urls]
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "boring-http"
3
- version = "2.2.1"
3
+ version = "2.3.0"
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"
7
7
  dependencies = [
8
- "boring-ssl2>=1.2.0",
9
- "ssl2>=2.0.1",
8
+ "boring-ssl2>=1.3.0",
9
+ "ssl2>=2.1.0",
10
10
  ]
11
11
 
12
12
  [project.urls]
@@ -1,4 +1,5 @@
1
1
  import base64
2
+ import os
2
3
  from collections.abc import (
3
4
  AsyncGenerator,
4
5
  AsyncIterable,
@@ -117,6 +118,8 @@ class Client:
117
118
  revocation: Revocation = Revocation.SOFT,
118
119
  certificate_chain: SigningChain | None = None,
119
120
  check_peer: Callable[[tuple[bytes, ...], str], Awaitable[None]] | None = None,
121
+ key_log: str | os.PathLike[str] | None = None,
122
+ resolve: Mapping[tuple[str, int], tuple[str, int]] | None = None,
120
123
  cookies: CookieJar | None = None,
121
124
  base_url: str | None = None,
122
125
  max_connections: int = 100,
@@ -151,6 +154,8 @@ class Client:
151
154
  self._revocations = Revocations(revocation, self._fetch_crl)
152
155
  self._certificate_chain = certificate_chain
153
156
  self._check_peer = check_peer
157
+ self._key_log = key_log
158
+ self._resolve = {} if resolve is None else dict(resolve)
154
159
  self._base_url = base_url
155
160
  self._max_redirects = max_redirects
156
161
  self._max_message_size = max_message_size
@@ -475,7 +480,7 @@ class Client:
475
480
  else None
476
481
  )
477
482
  self._contexts[protocols] = ClientContext(
478
- configuration, session_cache=cache
483
+ configuration, session_cache=cache, key_log=self._key_log
479
484
  )
480
485
  return self._contexts[protocols]
481
486
 
@@ -528,13 +533,19 @@ class Client:
528
533
  *,
529
534
  http1_only: bool = False,
530
535
  ) -> Connection:
536
+ # a resolve= entry sends a direct connection elsewhere, while the URL's host stays the name sent and verified
537
+ address = (
538
+ self._resolve.get(url.address, url.address)
539
+ if proxy is None
540
+ else url.address
541
+ )
531
542
  try:
532
543
  if quic and self._http3 is not None:
533
544
  memory = self._memory.setdefault(url.address, OriginMemory())
534
545
  client = await QuicClient.connect(
535
546
  self._context((NextProtocol.H3,)),
536
547
  url.host,
537
- url.address,
548
+ address,
538
549
  self._http3.transport,
539
550
  version=self._http3.quic_version,
540
551
  proxy=proxy,
@@ -551,11 +562,12 @@ class Client:
551
562
  if url.secure:
552
563
  stream = await TLSStream.connect(
553
564
  self._context((NextProtocol.HTTP1,) if http1_only else None),
554
- url.address,
565
+ address,
555
566
  proxy=proxy,
556
567
  proxy_context=self._context((NextProtocol.HTTP1,))
557
568
  if proxy is not None and proxy.scheme is ProxyScheme.HTTPS
558
569
  else None,
570
+ server_hostname=url.host,
559
571
  )
560
572
  await self._checked(stream, url.authority)
561
573
  if (
@@ -587,7 +599,7 @@ class Client:
587
599
  return Http1Connection(
588
600
  transport, absolute_form=True, proxy_authorization=authorization
589
601
  )
590
- tunnel = await TLSStream.tunnel(self._context(), url.address, proxy)
602
+ tunnel = await TLSStream.tunnel(self._context(), address, proxy)
591
603
  return Http1Connection(tunnel.transport, leftover=tunnel.leftover)
592
604
  except ProxyError, ConfigurationError, RevocationError:
593
605
  raise