Skip to main content

okhttp-tls

English | 中文


English

A Python HTTP client whose TLS ClientHello and HTTP/2 frames match Android OkHttp + Conscrypt — Rust core, synchronous and asyncio interfaces.

On Android, OkHttp's TLS layer is Conscrypt, a Java wrapper around BoringSSL. This library uses the same TLS library (BoringSSL, via the boring crate) configured the same way, so matching the configuration means matching the bytes: same cipher suites and order, same extension order, same signature algorithms and groups. Profiles built from device samples are checked field by field against those captures in CI on macOS, Linux x86_64, Linux arm64 and Windows.

Not affiliated with Square, Inc. "OkHttp" is the name of Square's HTTP client; this library reproduces its wire behaviour and ships no OkHttp code.

Features

  • OkHttp + Conscrypt TLS shape: 15 cipher suites in BoringSSL's on-device order, ALPN h2,http/1.1, OCSP status_request, and the post-quantum group X25519MLKEM768 on newer Conscrypt. The cipher order is fixed by the profile, not by the host CPU, so an x86 server emits the device shape exactly
  • Real OkHttp HTTP/2 fingerprint: SETTINGS carries only INITIAL_WINDOW_SIZE=16777216, connection WINDOW_UPDATE of 16711681, no PRIORITY frames, pseudo-header order :method :path :authority :scheme, first stream id 3, HPACK indexing and Huffman choices as OkHttp makes them (Akamai h2 fingerprint 4:16777216|16711681|0|m,p,a,s)
  • Protocol chosen by ALPN: h2 when the server selects it, otherwise HTTP/1.1; Response.http_version tells you which
  • OkHttp header order: your headers go out in the order you give them; engine headers are appended the way OkHttp's BridgeInterceptor does — Content-Type → Content-Length / Transfer-Encoding → Host → Connection → Accept-Encoding → Cookie → User-Agent — and never override ones you set. HTTP/1.1 keeps header-name case; HTTP/2 lowercases names (RFC 9113)
  • Client behaviour follows OkHttp 4.12 defaults:
    • redirects: up to 20; follows 300–303, 307, 308; Authorization is dropped on cross-host redirects
    • connection pool: at most 5 idle connections, kept for 5 minutes
    • cookies are sent in insertion order; gzip is decoded transparently; method names are sent verbatim
    • timeouts: connect_timeout / read_timeout / write_timeout default to 10 s each (read/write are the longest idle gap between two I/O operations, not a total); timeout is the overall cap (default 20 s). A timeout never triggers a resend, so a slow POST is not executed twice
  • TLS session resumption on by default, as on a real device. Tickets are kept per (scheme, host, port, proxy) and never shared across proxies. Pass tls_session_reuse=False to always send a fresh handshake
  • Proxy support per client or per request (http://user:pass@host:port); https targets go through a CONNECT tunnel with preemptive Basic auth
  • Sync + async: blocking Client and asyncio-native AsyncClient (Rust tokio runtime underneath)
  • Embedded CA store (Mozilla bundle), typed (.pyi stub, py.typed), layered exceptions
  • Prebuilt wheels for macOS (Apple Silicon), Linux (x86_64 and aarch64, manylinux_2_28) and Windows (x86_64); one wheel per platform covers Python ≥ 3.8 (abi3)

Install

pip install okhttp-tls

Usage

Synchronous:

import okhttp_tls

with okhttp_tls.Client(impersonate="okhttp_4_12_0", timeout=20.0) as client:
    resp = client.post(
        "https://example.com/api",
        headers=[("x-api-key", "K"), ("content-type", "application/json")],
        content=b'{"q":"x"}',
    )
    print(resp.status_code, resp.http_version, resp.headers.get("server"), resp.json())

Asynchronous:

import asyncio
import okhttp_tls

async def main():
    async with okhttp_tls.AsyncClient() as client:
        r1, r2 = await asyncio.gather(
            client.get("https://example.com/a"),
            client.post("https://example.com/b", json={"k": 1}),
        )

asyncio.run(main())

Per-request proxy override:

resp = client.get(url, proxy="http://user:pass@host:port")

Error handling:

try:
    resp = client.get(url)
except okhttp_tls.ProxyError:
    ...
except okhttp_tls.TimeoutException:
    ...
except okhttp_tls.OkhttpError:   # base class of all library errors
    ...

Profiles

Short name Canonical name Groups Source
okhttp_4_12_0 (default) okhttp_4_12_0_conscrypt_pq X25519MLKEM768, X25519, P-256, P-384 real device
okhttp_4_11_0 okhttp_4_11_0_conscrypt X25519, P-256, P-384 real device
okhttp_5_3_2 okhttp_5_3_2_conscrypt_pq X25519MLKEM768, X25519, P-256, P-384 inferred

impersonate= accepts either name. The default User-Agent is okhttp/<version>.

Why the canonical name carries a Conscrypt part. The fingerprint has two independent dimensions: the OkHttp version decides the cipher list, ALPN and TLS version range; the Conscrypt version decides the groups and extensions. Conscrypt is an Android mainline module updated through Google Play system updates, independently of the OS version — the same device on the same Android release went from no post-quantum group to X25519MLKEM768 after such an update. A version number alone is therefore ambiguous. A short name points to the profile with real-device evidence for that OkHttp version; profile_info()["name"] is always the canonical name.

source="inferred": okhttp_5_3_2 has no separate device sample. The OkHttp 4.12 and 5.3.2 ConnectionSpec cipher lists are identical, so it is declared an alias of the 4.12 profile (alias_of) instead of inventing a new fingerprint.

>>> okhttp_tls.profiles()
{'okhttp_4_11_0_conscrypt': 'f3e8b92336e2ffa7b31fcfb9a4bbb617',
 'okhttp_4_12_0_conscrypt_pq': 'ce7fd65a5ac2d85d0c89ee0e41a2e8c7',
 'okhttp_5_3_2_conscrypt_pq': 'ce7fd65a5ac2d85d0c89ee0e41a2e8c7'}
>>> okhttp_tls.profile_info("okhttp_4_12_0")["ja4"]
't13d1512h2_8daaf6152771_40271e0a5736'

Canonical vs. on-the-wire JA3/JA4. profiles() and profile_info() give the fingerprint of a fresh connection. A live ClientHello may additionally carry pre_shared_key (41) when a session is resumed or padding (21) when the hello length triggers it, and omits key_share / psk_key_exchange_modes / supported_versions (51/45/43) if the server only speaks TLS 1.2. These are added by BoringSSL based on connection state, exactly as on a device; they change JA3 and JA4, so compare like with like.

API

  • Constructors (Client / AsyncClient): impersonate, proxy, cookie_store (default off), timeout (overall, default 20 s), connect_timeout / read_timeout / write_timeout (default 10 s each; 0 disables the read/write limit), follow_redirects (default on), max_redirects (default 20), verify (default on), pool (keep-alive, default on), idle_timeout (seconds, default 300), auto_uncompress (gzip, default on), user_agent, tls_session_reuse (default on)
  • Methods: get / post / put / delete / patch / head / options / request(method, ...), accepting headers (list[tuple] keeps order; a dict also works), content (bytes) or json (serialized, adds content-type: application/json if absent), and proxy; close() and with / async with (AsyncClient also has aclose()). In asyncio code, use AsyncClient rather than Client
  • Response: status_code (alias status), http_version ("HTTP/1.1" / "HTTP/2"), headers, raw_headers (ordered list of tuples), content (bytes), text, json(), url (final URL after redirects)
  • Headers: case-insensitive get(name, default), headers[name], name in headers, get_all(name), keys() / values() / items(); iterating and integer indexing yield (name, value) tuples
  • profiles() → {canonical name: canonical JA3}; profile_info(name) → name, short_name, ja3, ja4, http, alpn, source, evidence, user_agent (plus alias_of for inferred profiles)
  • Exceptions, all subclasses of OkhttpError(RuntimeError): BuilderError, ConnectError, ProxyError (proxy unreachable, CONNECT refused, or 407), TLSError, TimeoutException, NetworkError, DecodeError, ClientClosedError

The Python API is a superset of the sibling library dartio-tls (Flutter dart:io fingerprint): switching between them means changing the module name and the impersonate profile.

Limitations

  • Android Conscrypt only. Desktop/server JVM OkHttp uses SunJSSE, not BoringSSL, and has a completely different ClientHello; it is not covered
  • No HTTP/3 / QUIC, no WebSocket, no Cronet/Chromium shape
  • HTTP/2 sends one request per connection (no multiplexing). This does not change the TLS or frame fingerprint. Flow control covers what is needed — windowed sending and receive-window refill — with no priority tree and no server push
  • TLS 1.3 cipher suites are BoringSSL's fixed three and cannot be changed
  • Plain-http targets reached through a proxy do not send Proxy-Authorization (only CONNECT for https carries credentials)
  • No wheel for Intel macOS yet

License

MIT


中文

TLS ClientHello 与 HTTP/2 帧形态对齐 Android OkHttp + Conscrypt 的 Python HTTP 客户端——Rust 内核,同步 + asyncio 双面。

Android 上 OkHttp 的 TLS 底座是 Conscrypt,即 BoringSSL 的 Java 封装。本库使用 同一个 TLS 库(BoringSSL,经 boring crate)并按同样方式配置,所以配置对齐即 字节对齐:cipher 套件及顺序、扩展顺序、签名算法、groups 全部一致。来自真机样本的档 都在 CI 里于 macOS、Linux x86_64、Linux arm64、Windows 上与抓包逐字段比对。

与 Square, Inc. 无关联。"OkHttp" 是 Square 的 HTTP 客户端项目名;本库复刻其线上 行为,不包含任何 OkHttp 代码。

特性

  • OkHttp + Conscrypt 的 TLS 形态:15 个 cipher 套件按 BoringSSL 真机顺序排列、 ALPN h2,http/1.1、OCSP status_request,新版 Conscrypt 档带后量子组 X25519MLKEM768。cipher 顺序由档决定、不看宿主 CPU,x86 服务器上也能精确发出 真机形态
  • 真实的 OkHttp HTTP/2 指纹:SETTINGS 只含 INITIAL_WINDOW_SIZE=16777216、 连接级 WINDOW_UPDATE 为 16711681、从不发 PRIORITY、伪头序 :method :path :authority :scheme、首流 id 为 3、HPACK 索引与 Huffman 决策与 OkHttp 一致(Akamai h2 指纹 4:16777216|16711681|0|m,p,a,s)
  • 协议由 ALPN 协商决定:服务端选 h2 就走 h2,否则 HTTP/1.1; Response.http_version 可见
  • OkHttp 头序:你的请求头按给定顺序原样发出;引擎头按 OkHttp BridgeInterceptor 的方式追加在后——Content-Type → Content-Length / Transfer-Encoding → Host → Connection → Accept-Encoding → Cookie → User-Agent—— 且不覆盖你已给的头。HTTP/1.1 保留头名大小写;HTTP/2 头名全小写(RFC 9113)
  • 客户端行为对齐 OkHttp 4.12 缺省值:
    • 重定向:上限 20 次;跟随 300–303、307、308;跨 host 跳转时剥掉 Authorization
    • 连接池:最多 5 条空闲连接,保留 5 分钟
    • Cookie 按种入顺序发送;gzip 透明解压;方法名原样发送
    • 超时:connect_timeout / read_timeout / write_timeout 各缺省 10 秒 (read/write 是两次读写之间的最长空闲,不是总时长);timeout 为整体上限 (缺省 20 秒)。超时不会触发重发,慢 POST 不会被执行两次
  • TLS 会话复用默认开启,与真机一致。票据按 (scheme, host, port, 代理) 分开保存, 不跨代理共用。传 tls_session_reuse=False 则每次都是全新握手
  • 代理支持:per-client 或 per-request(http://user:pass@host:port); https 目标走 CONNECT 隧道,预先携带 Basic 认证
  • 同步 + 异步双面:阻塞式 Client 与 asyncio 原生 AsyncClient (底层为 Rust tokio runtime)
  • 内嵌 CA store(Mozilla bundle)、带类型(.pyi 存根与 py.typed)、 分层异常
  • 预编译 wheel:macOS(Apple Silicon)、Linux(x86_64 与 aarch64,manylinux_2_28)、 Windows(x86_64);每平台一个 wheel 通吃 Python ≥ 3.8(abi3)

安装

pip install okhttp-tls

用法

同步:

import okhttp_tls

with okhttp_tls.Client(impersonate="okhttp_4_12_0", timeout=20.0) as client:
    resp = client.post(
        "https://example.com/api",
        headers=[("x-api-key", "K"), ("content-type", "application/json")],
        content=b'{"q":"x"}',
    )
    print(resp.status_code, resp.http_version, resp.headers.get("server"), resp.json())

异步:

import asyncio
import okhttp_tls

async def main():
    async with okhttp_tls.AsyncClient() as client:
        r1, r2 = await asyncio.gather(
            client.get("https://example.com/a"),
            client.post("https://example.com/b", json={"k": 1}),
        )

asyncio.run(main())

per-request 代理覆盖:

resp = client.get(url, proxy="http://user:pass@host:port")

异常处理:

try:
    resp = client.get(url)
except okhttp_tls.ProxyError:
    ...
except okhttp_tls.TimeoutException:
    ...
except okhttp_tls.OkhttpError:   # 本库所有异常的基类
    ...

档

短名 规范名 Groups 来源
okhttp_4_12_0 (缺省) okhttp_4_12_0_conscrypt_pq X25519MLKEM768, X25519, P-256, P-384 真机
okhttp_4_11_0 okhttp_4_11_0_conscrypt X25519, P-256, P-384 真机
okhttp_5_3_2 okhttp_5_3_2_conscrypt_pq X25519MLKEM768, X25519, P-256, P-384 推断

impersonate= 两种名字都收。缺省 User-Agent 为 okhttp/<版本>。

为什么规范名要带 Conscrypt 维。 指纹有两个独立维度:OkHttp 版本决定 cipher 列表、 ALPN 与 TLS 版本范围;Conscrypt 版本决定 groups 与扩展集。Conscrypt 是 Android mainline 模块,随 Google Play 系统更新独立升级,与 OS 版本解耦——同一台设备、同一个 Android 版本,经过一次这样的更新就从没有后量子组变成带 X25519MLKEM768。所以只写版本号 是有歧义的。短名指向该 OkHttp 版本有真机证据的那一档;profile_info()["name"] 恒为规范名。

source="inferred":okhttp_5_3_2 没有独立的真机样本。OkHttp 4.12 与 5.3.2 的 ConnectionSpec cipher 列表逐项相同,因此声明为 4.12 档的别名(alias_of), 而不是凭空造一个新指纹。

>>> okhttp_tls.profiles()
{'okhttp_4_11_0_conscrypt': 'f3e8b92336e2ffa7b31fcfb9a4bbb617',
 'okhttp_4_12_0_conscrypt_pq': 'ce7fd65a5ac2d85d0c89ee0e41a2e8c7',
 'okhttp_5_3_2_conscrypt_pq': 'ce7fd65a5ac2d85d0c89ee0e41a2e8c7'}
>>> okhttp_tls.profile_info("okhttp_4_12_0")["ja4"]
't13d1512h2_8daaf6152771_40271e0a5736'

规范形与线上 JA3/JA4。 profiles() 与 profile_info() 给的是全新连接的指纹。 线上 ClientHello 可能额外带 pre_shared_key(41)(会话复用时)或 padding(21) (hello 长度触发时),对端只支持 TLS 1.2 时则没有 key_share / psk_key_exchange_modes / supported_versions(51/45/43)。这些由 BoringSSL 按连接 状态自动决定,与真机行为一致;它们会改变 JA3 和 JA4,比对时两边要用同一基准。

API

  • 构造(Client / AsyncClient):impersonate、proxy、cookie_store(缺省关)、 timeout(整体上限,缺省 20 秒)、connect_timeout / read_timeout / write_timeout(各缺省 10 秒;read/write 设 0 = 不限)、follow_redirects(缺省开)、 max_redirects(缺省 20)、verify(缺省开)、pool(连接复用,缺省开)、 idle_timeout(秒,缺省 300)、auto_uncompress(gzip 解压,缺省开)、 user_agent、tls_session_reuse(缺省开)
  • 方法:get / post / put / delete / patch / head / options / request(任意方法),收 headers(list[tuple] 保序;dict 也收)、content (bytes)或 json(自动序列化,未给 content-type 时补 content-type: application/json)、proxy;close() 与 with / async with (AsyncClient 另有 aclose())。asyncio 代码里请用 AsyncClient 而不是 Client
  • Response:status_code(别名 status)、http_version("HTTP/1.1" / "HTTP/2")、headers、raw_headers(保序 tuple 列表)、content(bytes)、 text、json()、url(跳转后最终 URL)
  • Headers:大小写不敏感的 get(name, default)、headers[name]、name in headers、 get_all(name)、keys() / values() / items();迭代与整数下标得到 (name, value) tuple
  • profiles() → {规范名: 规范形 JA3};profile_info(name) → name、 short_name、ja3、ja4、http、alpn、source、evidence、user_agent (推断档另有 alias_of)
  • 异常全部继承 OkhttpError(RuntimeError):BuilderError、ConnectError、 ProxyError(代理连不上、CONNECT 被拒或 407)、TLSError、TimeoutException、 NetworkError、DecodeError、ClientClosedError

Python 接口是姊妹库 dartio-tls(Flutter dart:io 指纹)的超集:两者互换只需改模块名与 impersonate 档。

限制

  • 只做 Android Conscrypt 形态。 桌面/服务端 JVM 上的 OkHttp 走 SunJSSE 而非 BoringSSL,ClientHello 完全不同,不在覆盖范围内
  • 不做 HTTP/3 / QUIC、WebSocket、Cronet/Chromium 形态
  • HTTP/2 每条连接只发一个请求(不做多路复用),不影响 TLS 与帧指纹。流控只做必需部分—— 按窗口分片发送、回补接收窗口——不做优先级树,不做服务端推送
  • TLS 1.3 的 cipher 套件是 BoringSSL 固定的三个,不可调
  • 明文 http 目标经代理时不发 Proxy-Authorization(只有 https 的 CONNECT 携带凭据)
  • 暂无 Intel macOS 的 wheel

许可证

MIT

Metadata

Release files for okhttp-tls 0.1.1

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

Built distributions (wheels)

Table of built distributions (wheels) for okhttp-tls 0.1.1
File Actions
okhttp_tls-0.1.1-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details for okhttp_tls-0.1.1-cp38-abi3-win_amd64.whl
okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_x86_64.whl CPython 3.8 abi3 Linux glibc 2.28+ x86-64 Details for okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_x86_64.whl
okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_aarch64.whl CPython 3.8 abi3 Linux glibc 2.28+ ARM64 Details for okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_aarch64.whl
okhttp_tls-0.1.1-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details for okhttp_tls-0.1.1-cp38-abi3-macosx_11_0_arm64.whl

Total release size: 7.3 MB

Release files / okhttp_tls-0.1.1-cp38-abi3-win_amd64.whl

Download URL okhttp_tls-0.1.1-cp38-abi3-win_amd64.whl
Size 1.8 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
6145a3e783fd3f9227938b91c3455c98ff63bbc017657efef0d7bdd8dae49861
BLAKE2b-256 checksum
How to use checksums
ae2b59b15569faace8c0d819f4a195120d57a30ce6fc4afb50d5f2c0df3471ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_x86_64.whl

Download URL okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_x86_64.whl
Size 1.9 MB
Tags CPython 3.8 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
734097b69a81c1fd0f7a1dacd4208ff0788c009218535ee9396a7c50ad52deb0
BLAKE2b-256 checksum
How to use checksums
e3ef13b2ca8c24e6d64bb3be8162b67871eed08e781f214d79a43ecdef250034
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_aarch64.whl

Download URL okhttp_tls-0.1.1-cp38-abi3-manylinux_2_28_aarch64.whl
Size 1.9 MB
Tags CPython 3.8 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
e3c46baa7e0efb6002f0168112bf84707ed2aa3ae1ca6adfdc654d526b12e9e4
BLAKE2b-256 checksum
How to use checksums
351c3149d68d8e42abafc02e7980602f634822a4d65896b40577567b57e08c7f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / okhttp_tls-0.1.1-cp38-abi3-macosx_11_0_arm64.whl

Download URL okhttp_tls-0.1.1-cp38-abi3-macosx_11_0_arm64.whl
Size 1.7 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
bd1ad2ce7ae441db9104c3e6520767d9aae0db584052db2f889a27069f5e8a13
BLAKE2b-256 checksum
How to use checksums
8afac9275c5f0d33b14cd1283aac6311a4597de5aac698876a033123e6df8501
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Security for okhttp-tls 0.1.1

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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

4 release files No provenance

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