Skip to main content
 _                                   _   _     
| |__   ___  _ __ __ _  __ _        | |_| |___ 
| '_ \ / _ \| '__/ _` |/ _` | _____ | __| / __|
| | | | (_) | | | (_| | (_| ||_____|| |_| \__ \
|_| |_|\___/|_|  \__,_|\__,_|        \__|_|___/

High-performance Python HTTP client with native TLS fingerprinting, HTTP/2 framing, and browser impersonation.

CI Status PyPI Version Python Versions License: MIT


Table of Contents


Overview

horaa-tls is an advanced Python HTTP client engineered for passive network fingerprint reproduction and low-level transport control. Standard Python networking libraries (such as requests, urllib3, and httpx) rely on host OpenSSL bindings, producing cryptographic signatures (JA3, JA4, TLS extension sequences) and HTTP/2 framing characteristics that immediately identify them as automated non-browser clients to bot-management platforms (such as Cloudflare, Akamai, and Imperva).

By bridging directly into a precompiled Go-based C-shared library (bogdanfinn/tls-client v1.16.0) via ctypes FFI, horaa-tls synthesizes byte-exact TLS ClientHello packets, HTTP/2 SETTINGS frames, pseudo-header sequences (:method, :authority, :scheme, :path), and Client Hints corresponding to modern web browsers.


Key Features

  • Zero External Python Runtime Dependencies: Pure standard-library Python runtime interface—no third-party dependencies required.
  • Byte-Exact TLS Emulation: Powered by bogdanfinn/utls, reproducing browser-specific cipher suites, extension ordering, supported curves, and GREASE values.
  • Wire-Level HTTP/2 Customization: Powered by bogdanfinn/fhttp, generating browser-faithful SETTINGS sequences and HTTP/2 pseudo-header ordering.
  • Fail-Closed Security Defaults: Strict TLS X.509 certificate verification enabled by default. Rejects invalid, expired, or self-signed certificates unless explicitly bypassed.
  • Supply-Chain Integrity: Downloaded native binaries are validated against an immutable SHA-256 manifest before execution. Multi-process concurrency locking prevents install race conditions.
  • RFC 9110 Compliant Redirects: Implements strict HTTP redirect semantics (method rewriting for 301/302/303, strict method preservation for 307/308, and cross-origin credential isolation).
  • Direct-to-Disk Streaming & Iterators: Stream large payloads directly to disk without memory overhead, or iterate via iter_content() and iter_lines().
  • Synchronous & Asynchronous APIs: First-class sync interface and non-blocking asyncio methods running through a thread executor.
  • Stateful Middleware Architecture: Built-in extensible middleware pipeline supporting proxy rotation with failover recovery and exponential backoff retries.

Technology & Architecture

Component Verified Implementation
Python Interface horaa_tls high-level package with synchronous and asynchronous (asyncio) interfaces
Native Backend bogdanfinn/tls-client v1.16.0 C-shared library loaded via in-process ctypes C-ABI
TLS Implementation bogdanfinn/utls fork of Go crypto/tls with X25519MLKEM768 post-quantum key exchange support
HTTP Protocols HTTP/1.1, HTTP/2, and HTTP/3 (QUIC) via browser-aligned protocol racing
Python Compatibility Python 3.10, 3.11, 3.12, and 3.13 (confirmed across 150 regression and integration tests)
Operating Systems Windows (x86_64, ARM64), Linux (x86_64, ARM64, ARMv7), macOS (Apple Silicon ARM64, Intel x86_64)
Runtime Dependencies 0 external Python runtime packages (uses only Python standard library)
Packaging & Standards PEP 517 / PEP 621 compliant package build via setuptools>=61.0.0 and wheel
License MIT License

Platform & Runtime Support

horaa-tls natively supports the following operating systems and CPU architectures:

Operating System Architectures Shared Library Format
Windows x86_64 (amd64), ARM64 (arm64), 32-bit (386) .dll
Linux (glibc) x86_64 (amd64), aarch64 (arm64), armv7, ppc64le, s390x .so
macOS (Darwin) Apple Silicon (arm64), Intel 64-bit (amd64) .dylib

Installation

Standard Installation (PyPI)

Install the latest release from the Python Package Index:

pip install horaa-tls

Installation from Source (Development)

Clone the repository and install in editable mode with development dependencies:

git clone https://github.com/cipherwire/horaa-tls.git
cd horaa-tls
pip install -e .[dev]

Native Binary Resolution & Caching

horaa-tls does not require manual compilation of native code. On the first network request:

  1. It automatically detects the host operating system and CPU architecture.
  2. It fetches the matching precompiled tls-client v1.16.0 binary from GitHub Releases.
  3. It validates the downloaded file against an immutable SHA-256 hash manifest.
  4. It installs the library into a persistent user cache:
    • Windows: %LOCALAPPDATA%\horaa-tls\
    • Linux / macOS: ~/.cache/horaa-tls/

All subsequent requests load the verified cached library instantly with zero network overhead.

For air-gapped or offline environments, point TLS_LIBRARY_PATH directly to a pre-downloaded binary:

export TLS_LIBRARY_PATH="/opt/binaries/tls-client-xgo-1.16.0-linux-amd64.so"

Quickstart: Minimal Working Example

from horaa_tls import Session, ClientProfile

# Initialize a session emulating Google Chrome 120
with Session(profile=ClientProfile.CHROME_120) as session:
    response = session.get("https://httpbin.org/get")
    
    print(f"Status Code: {response.status_code}")
    print(f"HTTP Protocol: {response.protocol}")  # Protocol.HTTP_2
    print("Response JSON:", response.json())

Session Management & Request Lifecycle

Synchronous Requests

The Session class manages persistent connection pools, TLS sessions, cookies, and configuration across multiple requests:

from horaa_tls import Session, ClientProfile

session = Session(profile=ClientProfile.CHROME_133, timeout_seconds=15)

try:
    resp1 = session.get("https://httpbin.org/cookies/set?user=alice")
    print("Stored cookies:", session.cookies)

    resp2 = session.get("https://httpbin.org/cookies")
    print("Echoed cookies:", resp2.json())
finally:
    session.close()  # Releases underlying FFI memory

Asynchronous Requests

Every HTTP method provides an asynchronous counterpart (get_async, post_async, put_async, delete_async, patch_async, head_async, options_async, request_async). Under the hood, blocking FFI execution is dispatched to a non-blocking background thread pool executor:

import asyncio
from horaa_tls import Session, ClientProfile

async def main():
    async with Session(profile=ClientProfile.CHROME_133) as session:
        response = await session.get_async("https://httpbin.org/get")
        print(f"Async Status: {response.status_code}")
        print("Protocol:", response.protocol)

asyncio.run(main())

Resource Management & Context Managers

Using context managers (with Session() as s: or async with Session() as s:) guarantees that the underlying C-shared library session memory is deallocated promptly when the block exits.


Request Customization

Query Parameters

horaa-tls fully supports dictionaries, lists of tuples, repeated query keys, and existing query strings:

# Repeated keys produce: ?category=books&category=electronics&page=1
params = {
    "category": ["books", "electronics"],
    "page": 1,
}
response = session.get("https://httpbin.org/get", params=params)

Form Data & JSON Payloads

JSON Bodies

response = session.post(
    "https://httpbin.org/post",
    json={"username": "agent", "active": True}
)

Multi-Value Form Encoding

# Automatically sets Content-Type: application/x-www-form-urlencoded
form_data = {
    "filter": ["open", "pending"],
    "limit": "25"
}
response = session.post("https://httpbin.org/post", data=form_data)

Headers & Case-Insensitive Access

Request headers can be passed as standard dictionaries. Response headers are returned in a CaseInsensitiveDict supporting both singular and multi-valued lookup:

response = session.get(
    "https://httpbin.org/get",
    headers={"X-Custom-Client": "Horaa"}
)

# Case-insensitive singular lookup
content_type = response.headers.get("content-type")

# Multi-valued header extraction (e.g., multiple Set-Cookie headers)
set_cookies = response.headers.get_list("set-cookie")

TLS Configuration & Security Defaults

Certificate Verification & Fail-Closed Defaults

horaa-tls enforces strict TLS certificate verification by default (insecure_skip_verify=False). Untrusted, expired, or self-signed certificates fail closed with a BackendError:

from horaa_tls import Session, BackendError

session = Session()

try:
    # Fails closed: x509: certificate signed by unknown authority
    session.get("https://self-signed.badssl.com/")
except BackendError as e:
    print(f"Certificate validation failed as expected: {e}")

# Explicit opt-in for testing or debugging environments only:
with Session(insecure_skip_verify=True) as insecure_session:
    resp = insecure_session.get("https://self-signed.badssl.com/")
    assert resp.status_code == 200

ALPN & Forcing HTTP/1.1

By default, modern profiles negotiate HTTP/2 via ALPN. If an endpoint requires HTTP/1.1, set force_http1=True:

with Session(force_http1=True) as session:
    response = session.get("https://httpbin.org/get")
    print(response.protocol)  # Protocol.HTTP_1_1

HTTP/3 (QUIC) & Protocol Racing

horaa-tls supports real HTTP/3 over QUIC via browser-aligned protocol racing. When with_protocol_racing=True is enabled, the client launches HTTP/3 (QUIC/UDP) and HTTP/2 (TCP) handshakes concurrently in parallel, settling on whichever protocol completes first:

from horaa_tls import Session, ClientProfile, Protocol

with Session(profile=ClientProfile.CHROME_133, with_protocol_racing=True) as session:
    response = session.get("https://http3.is/")
    print(response.status_code)  # 200
    print(response.protocol)     # Protocol.HTTP_3

Post-Quantum Cryptography (X25519MLKEM768)

Modern profiles (such as chrome_133) automatically negotiate post-quantum hybrid key exchange (X25519MLKEM768) against servers supporting Kyber/ML-KEM, matching the cryptographic handshake of Google Chrome.

Cached Native Binary SHA-256 Verification

To mitigate local tampering or corrupted binary execution, horaa-tls verifies the SHA-256 hash of cached libraries before every execution. Corrupted binaries are refused and raise BackendError.


Browser Emulation & Client Hints

Preset Browser Profiles

The ClientProfile enum provides browser emulation presets:

Category Profiles
Google Chrome ClientProfile.CHROME_103, CHROME_110, CHROME_120, CHROME_133
Mozilla Firefox ClientProfile.FIREFOX_117, FIREFOX_123, FIREFOX_133
Apple Safari ClientProfile.SAFARI_16_0, SAFARI_IOS_17_0
Opera ClientProfile.OPERA_90

Profiles may be passed as enum instances or string identifiers:

session = Session(profile="chrome_133")

HTTP Header & Pseudo-Header Ordering

Real browsers send headers in specific sequences. For example, Chrome transmits HTTP/2 pseudo-headers in the order :method, :authority, :scheme, :path, whereas Firefox transmits :method, :path, :authority, :scheme. horaa-tls automatically applies each profile's authentic header ordering and HTTP/2 SETTINGS frames.

Passive Fingerprinting vs. Active Challenges

  • What horaa-tls handles: Passive network fingerprinting defenses (JA3/JA4 TLS signatures, extension sequences, cipher suite order, ALPN negotiation, HTTP/2 framing parameters, and Client Hints consistency).
  • What horaa-tls does NOT do: It is a socket-level protocol engine, not a headless browser. It does not execute JavaScript or render DOM elements. Active JavaScript challenges (such as Cloudflare Turnstile or reCAPTCHA v3) must be solved via browser automation or challenge-solving services, with the resulting tokens/cookies passed to horaa-tls.

Redirects, Cookies, Proxies & Retries

RFC 9110 Compliant Redirect Policies

horaa-tls implements strict RFC 9110 redirect mechanics:

Status Code Semantics Method Handling Request Body
301 Moved Permanently Permanent URL change Rewrites POST to GET; preserves other methods Dropped on POST -> GET rewrite
302 Found Temporary URL change Rewrites POST to GET; preserves other methods Dropped on POST -> GET rewrite
303 See Other Explicit redirection to resource Rewrites any method (except HEAD) to GET Stripped, along with content headers
307 Temporary Redirect Temporary redirect Preserves exact method Strictly preserved
308 Permanent Redirect Permanent redirect Preserves exact method Strictly preserved

Origin & Credential Isolation

When a redirect crosses domains, switches schemes (https -> http), or changes ports (:8443 -> :443), horaa-tls automatically strips sensitive headers:

  • Authorization
  • Proxy-Authorization
  • Cross-domain Cookie headers
# Access current session cookies
print(session.cookies)

# Query backend FFI cookie store
cookies = session.get_cookies_from_backend("https://example.com")

# Inject cookies into backend FFI store
session.add_cookies_to_backend("https://example.com", [
    {"name": "session_id", "value": "abc123xyz", "domain": ".example.com", "path": "/"}
])

Proxy Rotation & Failover

from horaa_tls import Session
from horaa_tls.middleware.proxy import ProxyRotatorMiddleware

proxies = [
    "http://user:pass@proxy1.example.com:8080",
    "http://user:pass@proxy2.example.com:8080"
]

session = Session()
# Rotate proxies automatically upon connection failure
session.middleware_pipeline.add(
    ProxyRotatorMiddleware(proxies=proxies, mode="failover", max_failovers=3)
)

Exponential Backoff Retries

from horaa_tls import Session
from horaa_tls.middleware.retry import RetryMiddleware

session = Session()
session.middleware_pipeline.add(
    RetryMiddleware(
        max_retries=3,
        backoff_factor=1.5,
        retry_on_status=(500, 502, 503, 504)
    )
)

Response Streaming & Large Downloads

Direct-to-Disk Streaming

Stream multi-gigabyte files directly to the filesystem without buffering them in memory:

with Session() as session:
    session.get("https://speed.hetzner.de/100MB.bin", download_to="100MB.bin")

Chunk & Line Iterators

Iterate through responses in memory-efficient chunks or line by line:

with Session() as session:
    with session.get("https://httpbin.org/stream/20") as response:
        # Iterate over 4KB chunks
        for chunk in response.iter_content(chunk_size=4096):
            process_chunk(chunk)

        # Or iterate line by line
        for line in response.iter_lines():
            print("Received line:", line.decode("utf-8"))

Public API Reference

The top-level horaa_tls package exports all core symbols:

from horaa_tls import (
    Session,
    ClientProfile,
    Response,
    CaseInsensitiveDict,
    Protocol,
    HoraaTLSError,
    BackendError,
    NetworkError,
    TooManyRedirectsError,
    BaseMiddleware,
    MiddlewarePipeline,
    RetryMiddleware,
    RedirectMiddleware,
    ProxyRotatorMiddleware,
    __version__,
)

Environment Variables & Configuration

Variable Default Description
TLS_LIBRARY_PATH Unset Explicit path to a native shared library (.dll, .so, .dylib), bypassing updater and cache.
HORAA_TLS_CACHE_DIR %LOCALAPPDATA%\horaa-tls / ~/.cache/horaa-tls Directory where downloaded native libraries and version markers are stored.
HORAA_TLS_TLS_CLIENT_VERSION 1.16.0 Target upstream bogdanfinn/tls-client release version.
HORAA_TLS_SKIP_CACHE_VERIFY 0 If set to 1, skips pre-load SHA-256 hash validation of cached native libraries.
GITHUB_TOKEN / GH_TOKEN Unset Optional GitHub token to raise API rate limits if the fallback asset resolver is utilized.

Error Handling

All library exceptions inherit from HoraaTLSError:

from horaa_tls import Session, HoraaTLSError, BackendError, NetworkError, TooManyRedirectsError

try:
    with Session() as session:
        response = session.get("https://example.com", timeout_seconds=5)
except TooManyRedirectsError:
    print("Redirect loop encountered!")
except NetworkError as e:
    print(f"Transport/HTTP network failure: {e}")
except BackendError as e:
    print(f"Native Go FFI or TLS error: {e}")
except HoraaTLSError as e:
    print(f"General horaa-tls error: {e}")

Development, Testing & Release Automation

Running the Test Suite

Install dev dependencies and execute the full test suite with pytest:

# Run hermetic unit and regression tests (excluding external network dependencies)
pytest -v -m "not integration"

# Run all tests, including live native TLS integration tests
pytest -v

Linting and code style checks:

ruff check .

Conventional Commits & Automated Releases

This repository adheres to the Conventional Commits specification:

  • fix: bumps patch version (e.g. 0.2.0 -> 0.2.1).
  • feat: bumps minor version (e.g. 0.2.0 -> 0.3.0).
  • chore:, docs:, test: maintain versioning without releases.

Release Please automatically tracks merged commits on main, prepares a Release Pull Request updating CHANGELOG.md and version metadata, and tags the release when merged.

PyPI Trusted Publishing

Packages are built and published securely to PyPI via GitHub Actions using OpenID Connect (OIDC) Trusted Publishing. No static PyPI API tokens or passwords are used.


Known Limitations & Troubleshooting

  1. Alpine Linux / musl: Go C-shared binaries use the initial-exec thread-local storage (TLS) model, which musl libc does not support. Use standard glibc-based Linux distributions.
  2. Interactive Challenges: Cloudflare Turnstile, hCaptcha, and reCAPTCHA checkboxes require JavaScript execution and DOM rendering. Pass session tokens solved via browser automation into horaa-tls.
  3. HTTP/3 Protocol Racing: HTTP/3 racing over HTTP proxies is disabled by default to prevent UDP packet and DNS leakage.

Contributing

Contributions, bug reports, and pull requests are welcome! Please ensure:

  1. All changes include corresponding unit tests in tests/.
  2. All commits follow Conventional Commits formatting (feat: ..., fix: ...).
  3. Code passes ruff check . and all pytest checks.

License

This project is licensed under the MIT License.

Metadata

Release files for horaa-tls 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for horaa-tls 0.3.0
File Size Uploaded Provenance
horaa_tls-0.3.0.tar.gz 57.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for horaa-tls 0.3.0
File Interpreter ABI Platform
horaa_tls-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 101.1 kB

Release files / horaa_tls-0.3.0.tar.gz

Download URL horaa_tls-0.3.0.tar.gz
Size 57.9 kB
Tags Source
SHA-256 checksum
How to use checksums
5f5b3a94a5ac203972b35d605aadb964f5f674589eaa0bc94ce54204f7c818de
BLAKE2b-256 checksum
How to use checksums
c5f90c4256e6dac6429e43f6f991976c86b2db9e336b07a564fbb966562c9ab1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.0

Release files / horaa_tls-0.3.0-py3-none-any.whl

Download URL horaa_tls-0.3.0-py3-none-any.whl
Size 43.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a0a15991aa97f4c02e1b84ca44f73274c86d83b9fedf68f229e95a5222a15cc3
BLAKE2b-256 checksum
How to use checksums
a211dc0508c89826c0830f50b885026a1cc9aedc4a60e49e67dafec1e5324306
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.10.0

Security for horaa-tls 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

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