Skip to main content

rex-tls

rex-tls is a Python HTTP client backed by a native Rust TLS and HTTP core. It provides a requests-style API with transport profiles for Android Chrome and OkHttp, persistent sessions, cookies, proxies, streaming I/O, asynchronous requests, and bounded connection pools.

Installation

python -m pip install rex-tls

Python 3.9 or newer is required. Supported platforms are:

  • Windows x86-64
  • Linux x86-64 with manylinux 2.28 or newer

Transport profiles

Profile HTTP/1.1 HTTP/2 HTTP/3
chrome_android_149 Yes Yes Yes
chrome_android_150 Yes Yes Yes
okhttp_4.12 Yes Yes No
okhttp_5.4 Yes Yes No

chrome_android and chrome_android_latest are aliases for chrome_android_150. okhttp and okhttp_latest are aliases for okhttp_5.4. Use the versioned names when reproducibility matters.

Quick start

import rex_tls

response = rex_tls.get(
    "https://example.com/",
    profile="chrome_android_150",
    params={"page": 1},
    timeout=20,
)

response.raise_for_status()
print(response.status_code)
print(response.http_version)
print(response.text)

JSON and form requests use familiar keyword arguments:

response = rex_tls.post(
    "https://api.example.com/items",
    profile="okhttp_5.4",
    json={"name": "example", "enabled": True},
)

response = rex_tls.post(
    "https://api.example.com/form",
    profile="okhttp_4.12",
    data={"name": "example"},
)

Persistent sessions

A Session keeps cookies, connections, and TLS session state across requests.

from rex_tls import Session

with Session(
    profile="chrome_android_150",
    timeout=20,
) as session:
    session.headers.update({"accept": "application/json"})
    session.cookies.set("locale", "en-US")

    first = session.get("https://example.com/api/profile")
    second = session.get("https://example.com/api/settings")

    print(second.connection_reused)
    print(session.cookies.get_dict())

Per-request cookies are supported and do not modify the session jar:

response = session.get(
    "https://example.com/api/items",
    params={"page": 2},
    cookies={"request-only": "value"},
    headers={"accept": "application/json"},
)

Cookies

Session.cookies provides the commonly used requests-style CookieJar methods:

session.cookies.update({"theme": "dark"})
session.cookies.set(
    "api-token",
    "value",
    domain="api.example.com",
    path="/v1",
    secure=True,
)

print(session.cookies.get("theme"))
print(session.cookies.get_dict())
print(session.cookies.items())

session.cookies.clear(domain="api.example.com", path="/v1", name="api-token")

Supported helpers include set(), set_cookie(), get(), get_dict(), update(), clear(), keys(), values(), items(), list_domains(), and list_paths(). Response cookies are validated against domain, path, Secure, expiry, IP-address, and public-suffix rules before they enter the jar.

Proxies

Use proxy= as a single proxy for all supported routes:

proxy = "http://username:password@proxy.example:8080"

with Session("okhttp_5.4", proxy=proxy) as session:
    response = session.get("https://example.com/")

Or use a requests-style mapping:

proxies = {
    "http": "http://proxy.example:8080",
    "https": "http://proxy.example:8080",
    "all": "http://fallback.example:8080",
    "no_proxy": ".internal.example,localhost,127.0.0.1",
}

with Session("chrome_android_149", proxies=proxies) as session:
    response = session.get("https://example.com/")

Session.proxies is mutable. A per-request proxies= mapping overrides the session mapping. Set trust_env=True to read HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, and NO_PROXY.

Proxy URLs must use http://. HTTPS destinations use HTTP CONNECT. Basic proxy credentials are supported; percent-encode reserved characters in usernames and passwords. HTTPS proxies, SOCKS, PAC, NTLM/Digest, and MASQUE are not supported.

HTTP version selection

The negotiated protocol is available as response.http_version.

Require an actual HTTP/2 connection with http2=True:

with Session("chrome_android_150", http2=True) as session:
    response = session.get("https://example.com/")
    assert response.http_version == "HTTP/2"

http2=True preserves the profile ALPN list and rejects the response if the server does not negotiate HTTP/2. It supports HTTPS only and is mutually exclusive with HTTP/3 modes.

Chrome profiles support the following http3 values:

Value Behavior
off Disable HTTP/3. This is the default.
auto Enable the Chrome HTTP/3 path with HTTP/2 or HTTP/1.1 fallback.
only Require HTTP/3 and fail if it cannot be used.
with Session("chrome_android_150", http3="auto") as session:
    response = session.get("https://example.com/")

with Session("chrome_android_149", http3="only") as session:
    response = session.get("https://example.com/")
    assert response.http_version == "HTTP/3"

HTTP/3 requires HTTPS and a versioned Chrome profile. OkHttp profiles do not enable HTTP/3. Conventional HTTP proxies cannot carry the QUIC path, so http3="only" is rejected when such a proxy is selected.

Request headers

Headers may be supplied as a mapping or as ordered (name, value) pairs. The native core applies the selected profile's protocol-specific ordering and casing rules before transmission.

If both session and request headers are empty, the profile's default request headers are used. A non-empty caller header set is sent without adding unrelated profile defaults. Protocol-required fields and fields derived from cookies or the request body may still be generated.

HTTP/1.1 preserves the profile's observable field-name casing. HTTP/2 and HTTP/3 field names are lowercase as required by those protocols. Hop-by-hop HTTP/1.1 fields such as Connection are rejected for HTTP/2 and HTTP/3 instead of being silently removed.

Streaming downloads

Use a context manager so the connection is released when the body reaches EOF or the response is closed:

from rex_tls import Session

with Session("chrome_android_150") as session:
    with session.get("https://example.com/large.bin", stream=True) as response:
        response.raise_for_status()
        with open("large.bin", "wb") as output:
            for chunk in response.iter_content(64 * 1024):
                output.write(chunk)

iter_lines(), raw.read(), and raw.readinto() are also available. Set decode_content=False to receive the compressed response body without automatic content decoding.

Streaming uploads and multipart files

File objects and byte iterables are uploaded incrementally:

with Session("okhttp_5.4") as session:
    with open("large.bin", "rb") as source:
        response = session.post(
            "https://example.com/upload",
            content=source,
        )

    with open("image.png", "rb") as source:
        response = session.post(
            "https://example.com/form",
            data={"title": "example"},
            files={"file": ("image.png", source, "image/png")},
        )

Seekable upload sources can be replayed across 307/308 redirects. A one-shot source raises UnrewindableBodyError if a redirect requires replay.

Async API

AsyncSession provides an asyncio interface without blocking the event loop:

import asyncio
from rex_tls import AsyncSession

async def main() -> None:
    async with AsyncSession(
        "okhttp_5.4",
        max_concurrency=8,
        http2=True,
    ) as session:
        urls = [f"https://example.com/items/{item}" for item in range(10)]
        responses = await asyncio.gather(*(session.get(url) for url in urls))
        print([response.status_code for response in responses])

asyncio.run(main())

Async streaming uses aiter_content() and aiter_lines():

from rex_tls import AsyncSession

async def stream_events(session: AsyncSession) -> None:
    response = await session.get("https://example.com/events", stream=True)
    async with response:
        async for line in response.aiter_lines():
            print(line)

Bounded session pools

SessionPool and AsyncSessionPool create multiple independent native sessions with a fixed concurrency limit.

from concurrent.futures import ThreadPoolExecutor
from rex_tls import SessionPool

urls = [f"https://example.com/items/{item}" for item in range(20)]

with SessionPool(
    "okhttp_4.12",
    max_connections=8,
    session_mode="shared",
) as pool:
    pool.headers["accept"] = "application/json"
    with ThreadPoolExecutor(max_workers=16) as executor:
        responses = list(executor.map(pool.get, urls))

Pool session modes:

  • shared: members share headers, proxy configuration, and one thread-safe CookieJar. Connections remain independent.
  • isolated: each member owns independent headers, proxy configuration, cookies, connections, and TLS state.

Lease a specific member when multiple requests must keep the same isolated state:

from rex_tls import SessionPool

with SessionPool(
    "okhttp_5.4",
    max_connections=4,
    session_mode="isolated",
) as pool:
    with pool.acquire(timeout=1) as account:
        account.headers["authorization"] = "Bearer example-token"
        account.cookies.set("account", "one")
        account.get("https://example.com/step-1")
        account.get("https://example.com/step-2")

Use pool_timeout= to limit how long a request waits for a pool member. The ordinary timeout= argument continues to control the network request.

Response API

Common response attributes and methods include:

  • status_code, reason, url, and http_version
  • headers, including get_all() and the ordered raw view
  • content, text, and json()
  • ok and raise_for_status()
  • history
  • elapsed_seconds
  • local_address and remote_address
  • connection_reused and tls_session_reused
  • iter_content(), iter_lines(), close(), and aclose()

Requests compatibility

The API directly supports common requests-style arguments and attributes:

  • HTTP method helpers and Session
  • params, headers, data, json, files, and cookies
  • timeout, verify, proxy, and proxies
  • Session.headers, Session.proxies, Session.cookies, and trust_env
  • stream=True, iter_content(), iter_lines(), and raw reads
  • per-request allow_redirects
  • .content, .text, .json(), and .raise_for_status()

It is not a drop-in replacement for every requests extension point. auth, hooks, adapters, mount, and custom RequestsCookieJar policy objects are not implemented.

TLS verification

Certificate verification is enabled by default and uses the installed certifi CA bundle.

# Default CA bundle
Session("chrome_android_150", verify=True)

# Custom CA file
Session("chrome_android_150", verify="/path/to/private-ca.pem")

# Disable verification explicitly
Session("chrome_android_150", verify=False)

Disabling verification removes server identity protection and should only be used in controlled test environments.

Error handling

import rex_tls

try:
    response = rex_tls.get(
        "https://example.com/",
        profile="chrome_android_150",
        timeout=10,
    )
    response.raise_for_status()
except rex_tls.HTTPError as exc:
    print(f"HTTP error: {exc}")
except rex_tls.RequestError as exc:
    print(f"Request failed: {exc}")
except rex_tls.MobileTLSError as exc:
    print(f"Native client error: {exc}")
except (TypeError, ValueError) as exc:
    print(f"Invalid configuration: {exc}")

Additional exceptions include CookieConflictError, ContentDecodingError, InvalidHeader, InvalidURL, SessionClosedError, StreamClosedError, StreamConsumedError, and UnrewindableBodyError.

Runtime information

import rex_tls

print(rex_tls.__version__)
print(rex_tls.profiles())
print(dict(rex_tls.native_versions()))
print(rex_tls.profile_info("chrome_android_150"))

License

MIT

Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

rex_tls-2.2.1-cp39-abi3-win_amd64.whl (2.7 MB view details)

Uploaded CPython 3.9+Windows x86-64

rex_tls-2.2.1-cp39-abi3-manylinux_2_28_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.28+ x86-64

File details

Details for the file rex_tls-2.2.1-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: rex_tls-2.2.1-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.0

File hashes

Hashes for rex_tls-2.2.1-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 5a0c7f068cd1341de7235115273d22a0808647145a5738747a6edb22480587f3
MD5 6e130c00252eea71865cdcba42e81b59
BLAKE2b-256 4cde9341ebb9068b5a708a93c44fb010f8d0b10c6f53edf56d001b91e4007f18

See more details on using hashes here.

File details

Details for the file rex_tls-2.2.1-cp39-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for rex_tls-2.2.1-cp39-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 b5fc011ba968a4f17283a18d6fa46f4d760b14cb97dc03af5c07189f9cab389f
MD5 3b5ee1746a3f337b8b6fcf5140308277
BLAKE2b-256 dbc724d138643140584f1709ac55bf564a475c23dcd4ec00bcdc982aea3a9ad9

See more details on using hashes here.

Release history Release notifications | RSS feed

2.24.1

2 files

2.24.0

2 files

2.23.0

2 files

2.22.0

2 files

2.21.0

2 files

2.20.2

2 files

2.20.1

2 files

2.20.0

2 files

2.19.2

2 files

2.19.1

2 files

2.19.0

4 files

2.18.1

4 files

2.18.0

2 files

2.17.0

2 files

2.16.3

2 files

2.16.2

2 files

2.16.1

2 files

2.16.0

2 files

2.15.2

2 files

2.15.1

2 files

2.15.0

2 files

2.14.0

2 files

2.13.0

2 files

2.12.0

2 files

2.11.0

2 files

2.10.1

2 files

2.10.0

2 files

2.9.0

2 files

2.8.0

2 files

2.7.0

2 files

2.6.0

2 files

2.5.0

2 files

2.4.0

2 files

2.3.2

2 files

2.3.1

2 files

2.3.0

2 files

This release

2.2.1 This release

2 files

2.2.0

2 files

2.1.0

2 files

2.0.0

2 files

0.1.1

5 files

0.1.0

5 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page