From 6c16227c3577fdda539891b6a8c503cd9e6e128b Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Mon, 27 Jul 2026 13:14:41 +0200 Subject: [PATCH] Add httpunk transport --- src/httpx2/httpx2/__init__.py | 1 + src/httpx2/httpx2/_transports/__init__.py | 2 + src/httpx2/httpx2/_transports/httpunk.py | 190 ++++++++++++++++++++++ src/httpx2/pyproject.toml | 3 + tests/httpx2/test_httpunk_transport.py | 45 +++++ uv.lock | 104 +++++++++++- 6 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 src/httpx2/httpx2/_transports/httpunk.py create mode 100644 tests/httpx2/test_httpunk_transport.py diff --git a/src/httpx2/httpx2/__init__.py b/src/httpx2/httpx2/__init__.py index 3c43a988..f0618640 100644 --- a/src/httpx2/httpx2/__init__.py +++ b/src/httpx2/httpx2/__init__.py @@ -23,6 +23,7 @@ "AsyncByteStream", "AsyncClient", "AsyncHTTPTransport", + "AsyncHTTPunkTransport", "Auth", "BaseTransport", "BasicAuth", diff --git a/src/httpx2/httpx2/_transports/__init__.py b/src/httpx2/httpx2/_transports/__init__.py index 6a83e1d3..d248cbf3 100644 --- a/src/httpx2/httpx2/_transports/__init__.py +++ b/src/httpx2/httpx2/_transports/__init__.py @@ -1,6 +1,7 @@ from .asgi import ASGITransport from .base import AsyncBaseTransport, BaseTransport from .default import AsyncHTTPTransport, HTTPTransport +from .httpunk import AsyncHTTPunkTransport from .mock import MockTransport from .wsgi import WSGITransport @@ -11,5 +12,6 @@ "AsyncHTTPTransport", "HTTPTransport", "MockTransport", + "AsyncHTTPunkTransport", "WSGITransport", ] diff --git a/src/httpx2/httpx2/_transports/httpunk.py b/src/httpx2/httpx2/_transports/httpunk.py new file mode 100644 index 00000000..c2bfecee --- /dev/null +++ b/src/httpx2/httpx2/_transports/httpunk.py @@ -0,0 +1,190 @@ +from __future__ import annotations + +import asyncio +import contextlib +import ssl +import typing +from collections.abc import AsyncIterator +from dataclasses import dataclass, field + +from .._exceptions import UnsupportedProtocol +from .._models import Request, Response +from .._types import AsyncByteStream +from .base import AsyncBaseTransport + +__all__ = ["AsyncHTTPunkTransport"] + + +@dataclass +class _HTTPunkConnection: + protocol: typing.Any + connection: typing.Any + + async def aclose(self) -> None: + await self.protocol.aclose() + + +@dataclass +class _HTTPunkOriginPool: + max_connections: int + idle: asyncio.Queue[_HTTPunkConnection] = field(default_factory=asyncio.Queue) + lock: asyncio.Lock = field(default_factory=asyncio.Lock) + count: int = 0 + all_connections: list[_HTTPunkConnection] = field(default_factory=list) + + +class _HTTPunkStream(AsyncByteStream): + def __init__( + self, + response: typing.Any, + transport: AsyncHTTPunkTransport, + origin: tuple[str, str, int], + connection: _HTTPunkConnection, + ) -> None: + self._response = response + self._transport = transport + self._origin = origin + self._connection = connection + self._closed = False + + async def __aiter__(self) -> AsyncIterator[bytes]: + try: + async for chunk in self._response.aiter_bytes(): + yield chunk + finally: + await self.aclose() + + async def aclose(self) -> None: + if self._closed: + return + self._closed = True + with contextlib.suppress(Exception): + await self._response.aclose() + await self._transport._release(self._origin, self._connection) + + +class AsyncHTTPunkTransport(AsyncBaseTransport): + """An experimental async HTTP/1.1 transport backed by httpunk.""" + + def __init__( + self, + *, + max_connections: int = 100, + verify: bool | ssl.SSLContext = True, + ) -> None: + self._max_connections = max_connections + self._verify = verify + self._pools: dict[tuple[str, str, int], _HTTPunkOriginPool] = {} + self._closed = False + + async def handle_async_request(self, request: Request) -> Response: + if self._closed: + raise RuntimeError("Cannot send a request after transport is closed.") + + scheme = request.url.scheme + if scheme not in ("http", "https"): + raise UnsupportedProtocol(f"Request URL has an unsupported protocol '{scheme}://'.") + + host = request.url.host + port = request.url.port or (443 if scheme == "https" else 80) + origin = (scheme, host, port) + connection = await self._acquire(origin) + + target = request.url.raw_path.decode("ascii") or "/" + body = await request.aread() + + try: + response = await connection.connection.request( + request.method, + target, + headers=request.headers.multi_items(), + body=body or None, + ) + except BaseException: + await self._discard(origin, connection) + raise + + return Response( + response.status, + headers=list(response.headers.items()), + stream=_HTTPunkStream(response, self, origin, connection), + extensions={"http_version": b"HTTP/1.1"}, + ) + + async def _acquire(self, origin: tuple[str, str, int]) -> _HTTPunkConnection: + pool = self._pools.get(origin) + if pool is None: + pool = self._pools[origin] = _HTTPunkOriginPool(max_connections=self._max_connections) + + try: + return pool.idle.get_nowait() + except asyncio.QueueEmpty: + pass + + async with pool.lock: + if pool.count < pool.max_connections: + pool.count += 1 + connection = await self._connect(origin) + pool.all_connections.append(connection) + return connection + + return await pool.idle.get() + + async def _connect(self, origin: tuple[str, str, int]) -> _HTTPunkConnection: + try: + import httpunk.asyncio + except ImportError as exc: # pragma: no cover + msg = "Using 'AsyncHTTPunkTransport' requires installing the 'httpunk' package." + raise RuntimeError(msg) from exc + + scheme, host, port = origin + ssl_context = self._ssl_context(scheme) + authority = f"{host}:{port}" + + loop = asyncio.get_running_loop() + _transport, protocol = await loop.create_connection( + lambda: httpunk.asyncio.H1ClientProtocol(authority=authority), + host, + port, + ssl=ssl_context, + ) + connection = await protocol.ready() + return _HTTPunkConnection(protocol=protocol, connection=connection) + + def _ssl_context(self, scheme: str) -> ssl.SSLContext | None: + if scheme != "https": + return None + if isinstance(self._verify, ssl.SSLContext): + ssl_context = self._verify + elif self._verify: + ssl_context = ssl.create_default_context() + else: + ssl_context = ssl._create_unverified_context() + ssl_context.set_alpn_protocols(["http/1.1"]) + return ssl_context + + async def _release(self, origin: tuple[str, str, int], connection: _HTTPunkConnection) -> None: + pool = self._pools.get(origin) + if pool is None or self._closed: + await connection.aclose() + return + pool.idle.put_nowait(connection) + + async def _discard(self, origin: tuple[str, str, int], connection: _HTTPunkConnection) -> None: + with contextlib.suppress(Exception): + await connection.aclose() + + pool = self._pools.get(origin) + if pool is None: + return + with contextlib.suppress(ValueError): + pool.all_connections.remove(connection) + pool.count -= 1 + + async def aclose(self) -> None: + self._closed = True + connections = [connection for pool in self._pools.values() for connection in pool.all_connections] + self._pools.clear() + for connection in connections: + with contextlib.suppress(Exception): + await connection.aclose() diff --git a/src/httpx2/pyproject.toml b/src/httpx2/pyproject.toml index 8fb99957..97384729 100644 --- a/src/httpx2/pyproject.toml +++ b/src/httpx2/pyproject.toml @@ -64,6 +64,9 @@ cli = [ http2 = [ "h2>=3,<5", ] +httpunk = [ + "httpunk>=0.1.2", +] socks = [ "socksio==1.*", ] diff --git a/tests/httpx2/test_httpunk_transport.py b/tests/httpx2/test_httpunk_transport.py new file mode 100644 index 00000000..c271341c --- /dev/null +++ b/tests/httpx2/test_httpunk_transport.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import asyncio + +import pytest + +import httpx2 + +pytest.importorskip("httpunk") + + +def test_async_httpunk_transport(server: object) -> None: + async def run() -> httpx2.Response: + async with httpx2.AsyncClient(transport=httpx2.AsyncHTTPunkTransport()) as client: + return await client.get(str(server.url)) # type: ignore[attr-defined] + + response = asyncio.run(run()) + + assert response.status_code == 200 + assert response.text == "Hello, world!" + assert response.http_version == "HTTP/1.1" + + +def test_async_httpunk_transport_request_body(server: object) -> None: + async def run() -> httpx2.Response: + async with httpx2.AsyncClient(transport=httpx2.AsyncHTTPunkTransport()) as client: + return await client.post(str(server.url.copy_with(path="/echo_body")), content=b"hello") # type: ignore[attr-defined] + + response = asyncio.run(run()) + + assert response.status_code == 200 + assert response.content == b"hello" + + +def test_async_httpunk_transport_connection_reuse(server: object) -> None: + async def run() -> None: + transport = httpx2.AsyncHTTPunkTransport() + async with httpx2.AsyncClient(transport=transport) as client: + response = await client.get(str(server.url)) # type: ignore[attr-defined] + assert response.status_code == 200 + response = await client.get(str(server.url)) # type: ignore[attr-defined] + assert response.status_code == 200 + assert sum(pool.count for pool in transport._pools.values()) == 1 + + asyncio.run(run()) diff --git a/uv.lock b/uv.lock index af58e71f..9ed1edf7 100644 --- a/uv.lock +++ b/uv.lock @@ -1357,6 +1357,104 @@ requires-dist = [ ] provides-extras = ["asyncio", "http2", "socks", "trio"] +[[package]] +name = "httpunk" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/60/9db58e5fbe39a6e8563933d3e0ae06afe115c189dce298f9161b0482ff81/httpunk-0.1.2.tar.gz", hash = "sha256:98c892670ad8cfd9f3f5dea9e1d1d2b434e1f5ba39cc47ad9bd43fc821f67e46", size = 286648, upload-time = "2026-07-14T10:43:03.354Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/21/da3361b63adbe45594be50fd2c815846908c34a30178890268cb00434ef0/httpunk-0.1.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:62537ccb654421cdef09659fc47f677fff2e8ac8099daadb3e4911cb507ee078", size = 513782, upload-time = "2026-07-14T10:41:02.808Z" }, + { url = "https://files.pythonhosted.org/packages/4a/49/dd51596bd2455b737cd274b0148c1d7091e25e8ac0fe151ea3e12af7a447/httpunk-0.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c02b4b09a65ef268480e480775a6c1702d37a21afe0c9edebedca22a24cfc92c", size = 490245, upload-time = "2026-07-14T10:41:04.338Z" }, + { url = "https://files.pythonhosted.org/packages/20/d6/130152f8b8a00471fb52119f9b0321c03f03b2aa9b814aac7a4f6e46849d/httpunk-0.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80c71497a22ae89f8adb675c77e42505b33a67b246d39f0936134121e24f1816", size = 517831, upload-time = "2026-07-14T10:41:05.569Z" }, + { url = "https://files.pythonhosted.org/packages/02/7c/3e153f3f4da76a735f678a3e1de2e81e9d7e47cb6d53c94e9e436372e93f/httpunk-0.1.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3cb3527d83a8d942534507a8067379ea241ec565951d0b2ed784b86c5dfe9388", size = 528944, upload-time = "2026-07-14T10:41:06.991Z" }, + { url = "https://files.pythonhosted.org/packages/5b/03/74b57611b8d211403c563d0d986e39e2f70b2e05438072b8fd7c6252d536/httpunk-0.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892dcf7f0f19f054f83acc07e2d007e80956443b5d9d4c93dad092c65e8138b9", size = 536804, upload-time = "2026-07-14T10:41:08.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/ee/7cfa44902ba1c6853dee43d3b7dac011a62c05dae57fc280e2a72db93b57/httpunk-0.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0c8a99b9b7768e41f9f556d9e236eb6b20fba4813c7c3483115efa1bef3d3bc8", size = 566338, upload-time = "2026-07-14T10:41:09.438Z" }, + { url = "https://files.pythonhosted.org/packages/c8/42/aa081b321771d24379c5bc88466ef794a2056a6020536c5d0d3c55ae654e/httpunk-0.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:509796607ce268aea51f3449a1e526124d9dcc80384821ee299f004daee1d204", size = 695623, upload-time = "2026-07-14T10:41:10.645Z" }, + { url = "https://files.pythonhosted.org/packages/99/54/84be27b12728c7ade9ab866110d6f3b609aea404f87ed03677bed28a9ac7/httpunk-0.1.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f33901642dc0d2dbc70368647225c913e7c20d89444d49983186386693958a0d", size = 804480, upload-time = "2026-07-14T10:41:12.014Z" }, + { url = "https://files.pythonhosted.org/packages/79/40/31815ab3cb272e7ff32d9211a9b98258446fb6638a46967574940c90e89e/httpunk-0.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6891d305852889e28e6fa4d3f7fab3bab52a0db5d4c93adddf144138f84b7049", size = 750072, upload-time = "2026-07-14T10:41:13.269Z" }, + { url = "https://files.pythonhosted.org/packages/c2/19/19aabb31e293fcdda77554af773a43ba040c7d0a0498d87bb0ecf977b518/httpunk-0.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:28584f7b865bc55e86db881cb14328abbe821741b668aff0ef976deb6e1921b3", size = 430845, upload-time = "2026-07-14T10:41:14.58Z" }, + { url = "https://files.pythonhosted.org/packages/e5/61/0bc0c73ebc041054fd70c372525b43fe2af8ce9bf055f8972219ec96b7cc/httpunk-0.1.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:e4329ef13d7aba42e02b5e289b669a7b35264b492acc66a1d7744b8466ce119e", size = 513336, upload-time = "2026-07-14T10:41:15.945Z" }, + { url = "https://files.pythonhosted.org/packages/7e/b1/0a71d4b74cd12651568322ee7cfa5afb541d663aec10a410dba9fca2016b/httpunk-0.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:379944538a837ee4c4c2f6e8b3463d5e5e821aaca36a5106181733665f798f55", size = 489570, upload-time = "2026-07-14T10:41:17.126Z" }, + { url = "https://files.pythonhosted.org/packages/88/b2/96e4a1e11574db0695bc5a56da2b38d577e377abb0e1d0dccc49c1adec37/httpunk-0.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76f6269d47583552b2f170d0c9616c2c455dcb0966348ea211ea68724a1593c", size = 517513, upload-time = "2026-07-14T10:41:18.358Z" }, + { url = "https://files.pythonhosted.org/packages/7b/b2/5093ff46a7ff5e4523808ff0b20bdfdfb53209749cd41e35835de4154d39/httpunk-0.1.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4c5e7593068e13889cae99c9dee989134fbc4620a211c80c053795765222d9e0", size = 528333, upload-time = "2026-07-14T10:41:19.551Z" }, + { url = "https://files.pythonhosted.org/packages/f2/cf/bb6531269c2cc4bda65189bd92a5eb5e69de3781579b3ec74144f0d59150/httpunk-0.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7aba649656313f3c0dd89d5dabd87d9022bac36138dee001d1f1f190e76b519", size = 536315, upload-time = "2026-07-14T10:41:20.8Z" }, + { url = "https://files.pythonhosted.org/packages/30/67/5af215be5116cfbeb76b4f5238043ee33e8911a916b57bb5afb2a6039cd1/httpunk-0.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fd746697ec408056e256e4108694a3d1547c4dc1a3d6477815765c6e1883671", size = 566016, upload-time = "2026-07-14T10:41:22.041Z" }, + { url = "https://files.pythonhosted.org/packages/85/cb/4feceb6b8f19b503c5ae77f25d0c2ee8925bee01193e83400163653d97f5/httpunk-0.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b415eb0d4e2d9c3d7e3ccf765bf43aa0736448a70b0a4a025cf87313aa7487eb", size = 695250, upload-time = "2026-07-14T10:41:23.382Z" }, + { url = "https://files.pythonhosted.org/packages/73/a3/4670c5b1a258a970078a6cf22579933e9150f3b525114f8270cca3e313bc/httpunk-0.1.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:9ae584b6cff4fba2c2a5d8b6f75e1c3bcfaf021dd34bc0f62f412872fb9d285d", size = 803916, upload-time = "2026-07-14T10:41:24.932Z" }, + { url = "https://files.pythonhosted.org/packages/56/7f/f980aa818741df8dd787df6c1a265020761f4129de375bdf40b782b8734e/httpunk-0.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8a5e5032c4cb7e8b5be21fc4f7a08f3fcd2d83d31bd5565d07fff20735c03a9b", size = 749596, upload-time = "2026-07-14T10:41:26.2Z" }, + { url = "https://files.pythonhosted.org/packages/ad/f8/f23e59263d98ddd846102869a96f856fb50197e55d7eccfc0801df44e147/httpunk-0.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:08090fdae5757b4fcac425e23ef8d8a81584776cb9ba9dba7edc421dd3cb4488", size = 430542, upload-time = "2026-07-14T10:41:27.55Z" }, + { url = "https://files.pythonhosted.org/packages/0a/80/0559df290ac414117e021987308d2d5b342da496aa0ab7500577c019d874/httpunk-0.1.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:09142abf0dcbb01ae78105dd2de962cf7a625ff19dc197436ac05d02c2106b02", size = 513750, upload-time = "2026-07-14T10:41:28.877Z" }, + { url = "https://files.pythonhosted.org/packages/c1/76/07906af5da64bb8eebada6c12351e9481f1c272ba27d3d2184d07e3abe63/httpunk-0.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7ac77efd39e59ae1b905927e88fce3eaba232b581ab4e168dde754f751d8e795", size = 485529, upload-time = "2026-07-14T10:41:30.102Z" }, + { url = "https://files.pythonhosted.org/packages/44/89/7dabfe602d0c37003dab745ba341fe9ccbb3c0453d835f103330ff4f2eb4/httpunk-0.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd5ee9e4bed854cea848e30b5d5be8010b7fc06b9315f1175063a16e5d318bd7", size = 517279, upload-time = "2026-07-14T10:41:31.334Z" }, + { url = "https://files.pythonhosted.org/packages/57/ee/9e9c98f07fcbea4725498b2b668978331e3028f43e06cce0a18b5024938e/httpunk-0.1.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c19d30ec8dd9dbd48444de6041fc3ba375be895db9f3799338830d02f830a6d7", size = 524220, upload-time = "2026-07-14T10:41:32.483Z" }, + { url = "https://files.pythonhosted.org/packages/56/76/728a2ea43e92c3151ad1e63d7fa66622710151aedf30925cadfa1eded8c3/httpunk-0.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2cd22e04b70d9a868da7331d2617266cc18e7bc3821a66a34d7093127771689", size = 537621, upload-time = "2026-07-14T10:41:33.839Z" }, + { url = "https://files.pythonhosted.org/packages/60/8b/afbff9ec967c73c8b91de95da841dc3d597589cc924318dd188462e7baf6/httpunk-0.1.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ce2b1112aafce7e311f20421f16f4b04f0b629ca96cedb25d647411588a773f", size = 558537, upload-time = "2026-07-14T10:41:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/5f/2e/6b3897a0180b7ea9372782f7daf78a7d0580cdf0d93607d9a5ce4332ac95/httpunk-0.1.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bdf2ceb6078c6e86524c17b62a89e28849dffdcefa95956b6c6ec1ad547eb8ef", size = 694383, upload-time = "2026-07-14T10:41:36.532Z" }, + { url = "https://files.pythonhosted.org/packages/50/70/e06633689482ed0e7cb1d4e73b16676c00dd988cb41032c8adfc8a1928ce/httpunk-0.1.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:ffe8031024dae955de67352763ee5021477688d1903def5cb7e765208cb413a7", size = 799352, upload-time = "2026-07-14T10:41:37.755Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2c/615116b2bdc63b1e9b8f36a2b585512589216bd820713f4bd77a9200ec6e/httpunk-0.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e03543a0f5558fd40d01f940df4c723fb8222f1ef916f5d097e3adffe6db28f4", size = 750903, upload-time = "2026-07-14T10:41:39.241Z" }, + { url = "https://files.pythonhosted.org/packages/cd/2d/387d33106492137bf2dfd73a39caa1ad3d2911ae9d0d3d149596d0eca05e/httpunk-0.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c7b147bd24b43229024394510604ca1addb970873c118b29973a104a610d7f4", size = 430893, upload-time = "2026-07-14T10:41:40.484Z" }, + { url = "https://files.pythonhosted.org/packages/54/69/ba88026bbc5bdd94f9bf04392d81c0fd14f1ccc3323d6f099851fc0ca81a/httpunk-0.1.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6c75384608f1c6078727fef0281b48b6425e1f0e05b3fddebb8055e81c350d59", size = 513602, upload-time = "2026-07-14T10:41:41.705Z" }, + { url = "https://files.pythonhosted.org/packages/df/67/e8b08d18df40e8bb57b37a2893e998649946af0398c670447a29e5ac6538/httpunk-0.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:77bede1ed5d35fd4154716b0460d6b13d7316b139fe821580bfb7976fb32ef03", size = 485290, upload-time = "2026-07-14T10:41:42.846Z" }, + { url = "https://files.pythonhosted.org/packages/58/74/15f61c22018eb517152fb65cc3ac17610468f6958f9ec8943645a6943ea2/httpunk-0.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3a9bab0efaf2a06ee76ac34824c3c73bb8e7f2de92ab4c288a3a6ed1d9fcd02", size = 517148, upload-time = "2026-07-14T10:41:44.057Z" }, + { url = "https://files.pythonhosted.org/packages/e5/a1/602508fc65b5b70f143bb60c221189fc9a5ffe1d15c840e340914b9cc53d/httpunk-0.1.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e44d5e07788e4ea941fcb3d41ac18f48960e1f71ad66edce22b18cb9d625ea8d", size = 523635, upload-time = "2026-07-14T10:41:45.308Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/477ba256a56db4950f7b7f1cd91fa646159449d01d9000139d83891c5523/httpunk-0.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d113d52c1c024a4ba227ffefe482957c99f20937c3094896527db6311ece245", size = 537444, upload-time = "2026-07-14T10:41:46.648Z" }, + { url = "https://files.pythonhosted.org/packages/8d/8e/d7366d85bcd4f80dd9f5e3ea1d36dfb37f107416df73260421f03c5b7d15/httpunk-0.1.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4daca3a9070bbde9e990c600cc958ffe9b4bfec6b54e1e856f54d31492a0caf4", size = 558093, upload-time = "2026-07-14T10:41:47.868Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c5/dd1670fee81e70396b60f84e3b7d36d51575e6b30b670aa73920987b6350/httpunk-0.1.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:dfb0dcf8086b28b3fd0275a1ac47045a01f1905eb0a3aea57d6b0c1977b36935", size = 694317, upload-time = "2026-07-14T10:41:49.112Z" }, + { url = "https://files.pythonhosted.org/packages/fe/31/0a981f28966b6b27b49160cbe1efd8732995da2c7abfc53678f82c73d626/httpunk-0.1.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:9413dbb7c1c26b21ab693f31bb39482ac716f68144cd95ad39c59a28ea614a41", size = 798821, upload-time = "2026-07-14T10:41:50.517Z" }, + { url = "https://files.pythonhosted.org/packages/09/e9/bc12b58f4e350852b5948914afc02fb4c3454ff3d9fbef8993a438bf2d51/httpunk-0.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:bdd7a0f6b24cfd42851cc7c24411dbc49df21258254033daac661b31630c7045", size = 750672, upload-time = "2026-07-14T10:41:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9d/b5a504d4b965ef3116c2135ef542d3c76572a442b83e2431cbc04e29f255/httpunk-0.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:06166ae7fa3abe40366ef2e1e8d4f028b4a6c7c83d6c5df852e05d504cffc7a3", size = 430844, upload-time = "2026-07-14T10:41:53.028Z" }, + { url = "https://files.pythonhosted.org/packages/db/18/f468989386bf3c9fd47dbe0782fb1c8929c05acb6d4f118d8904316e1cae/httpunk-0.1.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:4581b8cb8e230cf49d3602d6ad108bda518a35cf1985f82b30c9eaacf4b7871c", size = 513335, upload-time = "2026-07-14T10:41:54.246Z" }, + { url = "https://files.pythonhosted.org/packages/93/f6/574d2a4440f3902e4fe7b99a559d2e74efdf89771ab6a0939de91b410550/httpunk-0.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2c337c8bc998d8ebca3e4d8e76c45d1c09227bbb181956772533bcb09af986e0", size = 484980, upload-time = "2026-07-14T10:41:55.482Z" }, + { url = "https://files.pythonhosted.org/packages/37/5d/d043923f028bfed7a20e6fbf6b858a3bd180389757bd1d400e78e73b5a3a/httpunk-0.1.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:af0c22f256aeab94fc21a28a3686a0e1d5717bedd0745775f67f6de68bace6cd", size = 515253, upload-time = "2026-07-14T10:41:56.96Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ce/cfe7fcdfcec86d0adf9ba1440cb1ec086fb86f80ebf9c67b3e1059c5a30c/httpunk-0.1.2-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b310fe014cc4be39b15cf798fe8485d6eb7e1dd55ac07b54256b2868b3889217", size = 523999, upload-time = "2026-07-14T10:41:58.176Z" }, + { url = "https://files.pythonhosted.org/packages/27/e1/eb3cb95434b12703f278ca7e258593c8cf0028d706d065e8c10acd817661/httpunk-0.1.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d50518f2b9b024167ed83606fd3e9e03b7789d930735f5b50c6ada588aeb883", size = 535388, upload-time = "2026-07-14T10:41:59.421Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e0/71c07fa71620dfbd1b75f45ce1f0c75facee6c537cdc039a6f1030aac82e/httpunk-0.1.2-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d0926f7235220d88d22b800c436918c396076097a0de555fbfaaa34aa65fbc3", size = 558605, upload-time = "2026-07-14T10:42:00.703Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f1/5aaf4117d33ae088ee253a6777ca7b31ade6dab6cdc101b12e0d32cdb43f/httpunk-0.1.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:6d8fd1444ee0d25bb4e28ea9be7524f71b8c1433101696da58240648c1d1762c", size = 692488, upload-time = "2026-07-14T10:42:02.001Z" }, + { url = "https://files.pythonhosted.org/packages/a2/e2/c23a501149f26738a6f58ef36c6040b3950edcf781733f0f3b8a69cc0e99/httpunk-0.1.2-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:4aa41dc843baa7e1e8abc288de835faee3c27c399b214bb602a112d6943195e1", size = 799230, upload-time = "2026-07-14T10:42:03.258Z" }, + { url = "https://files.pythonhosted.org/packages/01/8d/db407ef5f6238fa17a0ea74e62dec93ee117fbd5250d619ae15f8b3bff40/httpunk-0.1.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:bedcf34b543f3a1f9071330417835f3f70b428dc7a026c599713f8a3c550b9a3", size = 748526, upload-time = "2026-07-14T10:42:04.531Z" }, + { url = "https://files.pythonhosted.org/packages/1d/24/3d050b384085d057f247424559ff2d6d2855de7c27ece3f371576c3910ec/httpunk-0.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:534a742c62ae693c84b637e691df7c3b31f74af801337a461943ca6e4c2d6b1f", size = 427575, upload-time = "2026-07-14T10:42:05.943Z" }, + { url = "https://files.pythonhosted.org/packages/0f/f8/5be42be3275598d7a13efda11d3c857733a47a92453a17647fcb9904ecd0/httpunk-0.1.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:0b1086c425be47019a13129d4f572b2306660c68100a32aeab72b003a0f1cb0a", size = 510946, upload-time = "2026-07-14T10:42:07.258Z" }, + { url = "https://files.pythonhosted.org/packages/42/3d/2f7fd53e12a00a008e005eba3d6df04d38cfcb1fd07c9d391eb66e6266f4/httpunk-0.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1c2813160bce81ae954b4f38b8481d1b8bac4a93e4ef900685783ca06dacd74b", size = 482276, upload-time = "2026-07-14T10:42:08.465Z" }, + { url = "https://files.pythonhosted.org/packages/62/97/4fa0f20ff65be498e97239d475504163f4770e901f7bb95c5d8f4f76db24/httpunk-0.1.2-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3209c56d43de2d8a3ff43004b02c5320f659299453486fd254e6b34245317f4", size = 512029, upload-time = "2026-07-14T10:42:09.755Z" }, + { url = "https://files.pythonhosted.org/packages/af/26/f7e538d814ed09a185d71c1cedd7b929b98800e473b061c794074c0cfd0b/httpunk-0.1.2-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e12a4dd26d6f550bdb129ff1856025e6716437ab68063c7124cb08e972ff119", size = 520695, upload-time = "2026-07-14T10:42:11.171Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/a1d301d0399c7bd251ebc65ba9a2d35c51833ab5398721b2393ed7762477/httpunk-0.1.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef0c4e00a1bfe38ac863ec8ed0a59b106cf346d19565e80e2b1fb1f2c41a9a7a", size = 532486, upload-time = "2026-07-14T10:42:12.448Z" }, + { url = "https://files.pythonhosted.org/packages/2e/a6/ce13e0b24120ea1814457e2111e7eb284626e6e8002d658aecf545cfb4d7/httpunk-0.1.2-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5b64cfb4c9bcd5f97788e9a9759d7d5adb1cf267970f8a33e6bb21a4b4e9d2f1", size = 555937, upload-time = "2026-07-14T10:42:14.332Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a7/a333929102e57b1707586607b831aaa7f098ea52dce1c0da144c86bcba79/httpunk-0.1.2-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:00a551d20e85663b5118f35cf2809b77d3fac0de332ba389fc77c8d63b295990", size = 688717, upload-time = "2026-07-14T10:42:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/77/b6/c38cf4c71c348b6d9a119cfe46cac191c616cc45b44d7646dab4e4e70cd8/httpunk-0.1.2-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:8206d19033184b21da2bb1886421b5bf6d1f24b1451961ffff8f2a375e23921f", size = 796255, upload-time = "2026-07-14T10:42:17.504Z" }, + { url = "https://files.pythonhosted.org/packages/53/87/4793cfe6cd21ca7a723740be413849157a9c23a590661b3609324c74bb39/httpunk-0.1.2-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:89e9344f2ea05a1fa61c9e4c9be089d5052e5a9f8e1aa7b8165e28062acd629e", size = 745238, upload-time = "2026-07-14T10:42:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c7/a89d0b90b2efe1d01d7bcf8130ee7483fc186a9d8e72d0e201ff6a74b461/httpunk-0.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:794c0ed0cd89615e0a836a259bfab2bdfd9f45411d910c56dc67b317a0132637", size = 425786, upload-time = "2026-07-14T10:42:20.172Z" }, + { url = "https://files.pythonhosted.org/packages/60/d7/ce10ba649b31c78190340367c32557bd1fd3bb785a3a5e97b0e1f7458353/httpunk-0.1.2-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:9e59c698b749ae796bb9c239c3bf4f1e6c800a597e11a6f1778a6aeb4042b3db", size = 513180, upload-time = "2026-07-14T10:42:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/74/e8/b56ec8e96b8974601a7cd234f4b7a5b8197da6c016322c92cb9cbedcbfad/httpunk-0.1.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:880da256b399cf4bdf911146d042b789b1a2643d86e3c3efc8fcfbe5b486793c", size = 484831, upload-time = "2026-07-14T10:42:22.688Z" }, + { url = "https://files.pythonhosted.org/packages/3f/00/2f14bb869268b9bcb0d7466586a01956305276ac677c754041da855f3edd/httpunk-0.1.2-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e271e801ada692cc1ae11ab75167a18677547b193a536b3f72c67a5c090bcc3", size = 515104, upload-time = "2026-07-14T10:42:23.989Z" }, + { url = "https://files.pythonhosted.org/packages/43/90/b40e96cdf4f50c31bbe0b4d24f415d2f69c465fed04fa16a440489215910/httpunk-0.1.2-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:11598465eee76610a59d4fb85ec4f24870eb5e416799acc7e33c0575224d721c", size = 524732, upload-time = "2026-07-14T10:42:25.306Z" }, + { url = "https://files.pythonhosted.org/packages/bb/b8/1994a4529948fffc31220e94421b3b9a6e867f09ed349b5c95969d2a41ec/httpunk-0.1.2-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76582a59c7103e3fb850b2367179722b6cdf871034db7bcacc5e4e30596c189d", size = 535185, upload-time = "2026-07-14T10:42:26.609Z" }, + { url = "https://files.pythonhosted.org/packages/22/93/4d9891ae3d763b286d8b9998e6165dd7f4f60a1eeda9a12e9cbc397544e8/httpunk-0.1.2-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e237a814143f5d0cda835546e896d831e782eb7f5a26fd78909f20ba91c7e471", size = 558704, upload-time = "2026-07-14T10:42:27.933Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a1/f4b1e64d1ce6784f672bf683b5d47c127cb18db1352f8e5e4016909d2dcb/httpunk-0.1.2-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:d7d540e172cbfba3e1d84e21459fa6694711df9ced02630ac965328c91140414", size = 692351, upload-time = "2026-07-14T10:42:29.252Z" }, + { url = "https://files.pythonhosted.org/packages/c3/68/3e189219e2640c001cda6653331f79b0d05062d6d250eafb9e9b3d9d8022/httpunk-0.1.2-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:0202a0dcb33852961faa54a37c8f0d6dd9fed0187788d059ab905d280d7cce4f", size = 799859, upload-time = "2026-07-14T10:42:30.713Z" }, + { url = "https://files.pythonhosted.org/packages/d6/9d/58f8a71e35a5c04239c5d13070ddc1ff6416b28e09fe233b7e250b7cd97c/httpunk-0.1.2-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:a0a1ee86f741e159d9e61f559eab9750a6b33b34030f0b13f310b05474bfad56", size = 748314, upload-time = "2026-07-14T10:42:32.003Z" }, + { url = "https://files.pythonhosted.org/packages/cc/e9/a9f64e00d6848d86345635be5a7fe3767b7a37767b97e98a51abb322af1f/httpunk-0.1.2-cp315-cp315-win_amd64.whl", hash = "sha256:9aa71fd1741e67e85d3697471d05b66924eb507d740cf1d91c0d05a77c1cb655", size = 427720, upload-time = "2026-07-14T10:42:33.53Z" }, + { url = "https://files.pythonhosted.org/packages/c1/de/75de845140bd0ab07fb8938ae8aa0428171aaad6f99d959b5dfaf9de060b/httpunk-0.1.2-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:0c7234c061e03407bc735ea8f029092d049bd5b68c76a47d885774531ff1c830", size = 510739, upload-time = "2026-07-14T10:42:35.022Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c0/ebc1f7a5d446dbe1da5206144d71ff19d8f97ad3df85ce6c37fd9d855596/httpunk-0.1.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:ff6ab93cf6cf7325b60434ca3fc557cfd9fa43c2cde227b7d705c33ed5f3f1f8", size = 482155, upload-time = "2026-07-14T10:42:36.324Z" }, + { url = "https://files.pythonhosted.org/packages/a7/3f/b309ad6e860021f26970269efba0446acdc207682e1e25c5369ae77d55ae/httpunk-0.1.2-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422298eebcfa7e1fdfd76de7df863953945882d2120bbd4f59ad9fdd75f19447", size = 511933, upload-time = "2026-07-14T10:42:37.74Z" }, + { url = "https://files.pythonhosted.org/packages/63/ed/fa46fe0631c88f97eb0cbb5e71f0ed1f2971cee488a812ade067d45630c0/httpunk-0.1.2-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4894d300645e388dc2145f68e2d2777f3313fdcf475fe379bc45d12eef48314b", size = 521363, upload-time = "2026-07-14T10:42:39.081Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d2/43b3c29bd67a5a19f24b2fad4994b08e1438aa8565ec2f61581c8395c49a/httpunk-0.1.2-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e7d0bad0e799ab664f3b30387871cbf61233ec78edcac95618753b5a5aaca16", size = 532321, upload-time = "2026-07-14T10:42:40.397Z" }, + { url = "https://files.pythonhosted.org/packages/af/b3/1128bcc4e18e97df588d7960e85db6f1cde7b8551b0d78c6bd741cb9436c/httpunk-0.1.2-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d395e4b45be63c0840e645881a9c9b9e64dceefcbac61960bef7ac34bf1c705", size = 556280, upload-time = "2026-07-14T10:42:41.9Z" }, + { url = "https://files.pythonhosted.org/packages/81/db/8ef82fe263a8df986b6c87f4fc67805d0367fdc12be6ba7b59b652234836/httpunk-0.1.2-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:01f7e36a36cd855609ea001b15ba9d635059b6b747656830e98ae7a63d716178", size = 688655, upload-time = "2026-07-14T10:42:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/484bc22ecd6948f9984e4b06f87e3f85b33941277635425e14a635a7f34e/httpunk-0.1.2-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:faa7614c9786458ccb322044f1b6840b033bd1ec3c267d36894dc867abcbe9f7", size = 796984, upload-time = "2026-07-14T10:42:44.67Z" }, + { url = "https://files.pythonhosted.org/packages/e1/77/37b161d65d2d0605b34cfaceab17677c28e7c018863712728eee5ff1bf49/httpunk-0.1.2-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:27f9b79c9b001179d4043aacfc46c9469552c38e7887541d24bcaa14a9c3ee08", size = 745104, upload-time = "2026-07-14T10:42:46.031Z" }, + { url = "https://files.pythonhosted.org/packages/2f/20/f3961e4dab792b7293acc3f6b7b5ff288527a13fc32352233e95522f2469/httpunk-0.1.2-cp315-cp315t-win_amd64.whl", hash = "sha256:0f2f6c62acaa0ae3904c4ac4a3c7d60e49c2dad40e17b6c8ee8b9b2ab84263e5", size = 425863, upload-time = "2026-07-14T10:42:47.461Z" }, + { url = "https://files.pythonhosted.org/packages/ef/6f/113623390c565b489466bebad8f4840c26536ad9a82720422b1397d00f87/httpunk-0.1.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4369cf9d270a7d43a38032527a59b7a763680cd1d736904e77f04fddc29431d5", size = 518172, upload-time = "2026-07-14T10:42:48.787Z" }, + { url = "https://files.pythonhosted.org/packages/ad/e8/a5973708874f25dced9af096d40d421f1b56132029c577f688a94bcf055e/httpunk-0.1.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:50554b270516810d36d59559745b11fb9a0ba3466fdca562277d5b53b0086760", size = 494507, upload-time = "2026-07-14T10:42:50.363Z" }, + { url = "https://files.pythonhosted.org/packages/aa/8b/a0e7ecd44488d1008d86c48b56a818aa01d906b1be83fcfa51d9514819bc/httpunk-0.1.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e79785fab745262b7004e1f0ef124d8f5eee67da2b3dbe1c55f3a95ca51bb29", size = 521124, upload-time = "2026-07-14T10:42:51.738Z" }, + { url = "https://files.pythonhosted.org/packages/b9/50/60b869382299fb19ae499889627d7229abfd360830baeee5723d12f50d43/httpunk-0.1.2-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b595671be5247b787a3f965d2861a80d80320ea6df4dcb59d5a86ed070837280", size = 533467, upload-time = "2026-07-14T10:42:53.282Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/b72a63a0d9ee36349cca8b940bce99fafa1d5522f388ff25b961f7a7adb4/httpunk-0.1.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b62ce01506a5d652ffa97bc968f4f77593a5fd8a0f0babc85642bb3fef1b651", size = 540583, upload-time = "2026-07-14T10:42:54.703Z" }, + { url = "https://files.pythonhosted.org/packages/0a/54/100479a52aed316dda81cd4d944cc51dea335fe073002624a506e81418f2/httpunk-0.1.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4636ba7d8ac7675536cf6ebcae6d4e0e3118a417031800919ba6c4adf60a6432", size = 569951, upload-time = "2026-07-14T10:42:56.168Z" }, + { url = "https://files.pythonhosted.org/packages/c5/af/6365f11f4ddd662354ed6c04b8fdbf7d21acc6a464f7aef81891310d64ce/httpunk-0.1.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e8c5d903e959781248a1cff65f108f8181b81e6bbf4c52d8b58a821ff379c720", size = 698955, upload-time = "2026-07-14T10:42:57.637Z" }, + { url = "https://files.pythonhosted.org/packages/b1/82/c07dc173201c0112ff694a31f964a50e702fc6a8e2153582e10475173782/httpunk-0.1.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c29053f77e4aa424b534be889e41a7f2a382786616065e196bd0de572321af6", size = 809458, upload-time = "2026-07-14T10:42:59.052Z" }, + { url = "https://files.pythonhosted.org/packages/84/2a/e0d76b1d34786ed8e05d2a22fc479feb8ddbccced69886bf6dd30fe7ebb7/httpunk-0.1.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0e192359f9db22404d20f67df69d16ca7a17e8316fbcb883018e2f9fc38083c6", size = 753845, upload-time = "2026-07-14T10:43:00.472Z" }, + { url = "https://files.pythonhosted.org/packages/1f/c8/cd7feb2c40ba70666bf66ac1c2c9dd3608b1677e6daa955d2700ab75bc83/httpunk-0.1.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:5a4cc913242d478f4a34cf8a703fedc0fa102ad0839ac27b794319235a480dec", size = 434070, upload-time = "2026-07-14T10:43:01.851Z" }, +] + [[package]] name = "httpx2" source = { editable = "src/httpx2" } @@ -1381,6 +1479,9 @@ cli = [ http2 = [ { name = "h2" }, ] +httpunk = [ + { name = "httpunk" }, +] socks = [ { name = "socksio" }, ] @@ -1399,6 +1500,7 @@ requires-dist = [ { name = "click", marker = "extra == 'cli'", specifier = ">=8.4" }, { name = "h2", marker = "extra == 'http2'", specifier = ">=3,<5" }, { name = "httpcore2", editable = "src/httpcore2" }, + { name = "httpunk", marker = "extra == 'httpunk'", specifier = ">=0.1.2" }, { name = "idna", specifier = ">=3.18" }, { name = "pygments", marker = "extra == 'cli'", specifier = "==2.*" }, { name = "rich", marker = "extra == 'cli'", specifier = ">=10,<16" }, @@ -1408,7 +1510,7 @@ requires-dist = [ { name = "wsproto", marker = "extra == 'ws'", specifier = ">=1.2" }, { name = "zstandard", marker = "python_full_version < '3.14' and extra == 'zstd'", specifier = ">=0.18.0" }, ] -provides-extras = ["brotli", "cli", "http2", "socks", "ws", "zstd"] +provides-extras = ["brotli", "cli", "http2", "httpunk", "socks", "ws", "zstd"] [[package]] name = "hyperframe"