Skip to main content

Async HTTP/1.1 client whose TLS handshake is identical to the `requests` library.

Project description

async-requests-tls

Async Python HTTP/1.1 client whose TLS ClientHello is byte-for-byte identical to what the requests library sends — same JA3, same JA4. Useful when a site fingerprints the TLS handshake and rejects modern async clients (httpx, niquests, …) while happily accepting plain requests.

The trick: requests builds its ssl.SSLContext through urllib3.util.ssl_.create_urllib3_context(). So do we. The context is then fed into asyncio.open_connection(..., ssl=ctx), so both stacks drive the same OpenSSL build with the same cipher list, options, ALPN, curves, and sigalgs.

Install

pip install async-requests-tls
# or with uv:
uv add async-requests-tls

The PyPI distribution name is async-requests-tls but the Python import name is async_requests_tls.

For development from a clone of this repo:

pip install -e .[dev]
# or:
uv sync --extra dev

Requires Python 3.13+ and urllib3>=2.0.

Quickstart

import asyncio
from async_requests_tls import AsyncSession

async def main():
    async with AsyncSession() as s:
        # GET
        r = await s.get("https://httpbin.org/get", params={"hello": "world"})
        print(r.status_code, r.json())

        # POST with JSON
        r = await s.post("https://httpbin.org/post", json={"x": 1})
        print(r.json())

        # Session-wide base_url, headers, and cookie jar
        async with AsyncSession(
            base_url="https://api.example.com",
            headers={"X-Token": "secret"},
        ) as api:
            await api.get("/users")          # GET https://api.example.com/users
            await api.post("/orders", data={"sku": "abc"})

asyncio.run(main())

AsyncSession supports get / post / put / patch / delete / head / options, with a requests-style API: params, headers, data, json, cookies, allow_redirects. Responses expose status_code, headers, content, text, json(), ok, and cookies.

Caveats

  • HTTP/1.1 only. ALPN defaults to ["http/1.1"] so the server never upgrades us to h2. If you pass alpn=alpn_protocols_like_urllib3() (to match the exact ALPN bytes requests sends when urllib3-future is installed) and the server picks h2, the session raises NotImplementedError — the TLS handshake still matched, the wire protocol did not.
  • No connection pooling, no proxies, no streaming, no multipart, no HTTP/2. This is a small, focused library.
  • JA3 parity ≠ detection-evasion parity. Modern detectors also look at HTTP/2 SETTINGS frames, header order (JA4_H), and Akamai-style timing. This library only matches the TLS layer.

Testing

uv run pytest        # or: pytest

All tests are unit tests — they don't open real sockets. The wire-level tests drive asyncio.StreamReader with canned bytes; the session tests patch _send_once with a fake.

Public API

from async_requests_tls import (
    AsyncSession,
    Response,
    build_ssl_context,            # if you want the SSLContext directly
    alpn_protocols_like_urllib3,  # exact ALPN list urllib3 would send
)

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

async_requests_tls-0.1.0.tar.gz (16.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

async_requests_tls-0.1.0-py3-none-any.whl (11.5 kB view details)

Uploaded Python 3

File details

Details for the file async_requests_tls-0.1.0.tar.gz.

File metadata

  • Download URL: async_requests_tls-0.1.0.tar.gz
  • Upload date:
  • Size: 16.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.2

File hashes

Hashes for async_requests_tls-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b248cf0f9aa726d7a92173eb585cb467ec561c252088a6099516a544023754b1
MD5 fd7137bdbdebcae88823f3abdbc879c4
BLAKE2b-256 02b644fd89894889e9d4edb607bd8d342eb29ad9610cceb1168b0e0853b4d2a3

See more details on using hashes here.

File details

Details for the file async_requests_tls-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for async_requests_tls-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 accd43546258a371227a1a313fa74ebb5fb60735f131b0781b80463d7c664dba
MD5 b51b4c4a30a3919bea9c96de35baaa69
BLAKE2b-256 a4a1ed4d7aeb7bf86d27e24ce6e6eccc6a1ee9fd042cd4dffee68ab6ff05ebb0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page