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.0

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.0
File Actions
okhttp_tls-0.1.0-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details for okhttp_tls-0.1.0-cp38-abi3-win_amd64.whl
okhttp_tls-0.1.0-cp38-abi3-manylinux_2_28_x86_64.whl CPython 3.8 abi3 Linux glibc 2.28+ x86-64 Details for okhttp_tls-0.1.0-cp38-abi3-manylinux_2_28_x86_64.whl
okhttp_tls-0.1.0-cp38-abi3-manylinux_2_28_aarch64.whl CPython 3.8 abi3 Linux glibc 2.28+ ARM64 Details for okhttp_tls-0.1.0-cp38-abi3-manylinux_2_28_aarch64.whl
okhttp_tls-0.1.0-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details for okhttp_tls-0.1.0-cp38-abi3-macosx_11_0_arm64.whl

Total release size: 7.3 MB

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

Download URL okhttp_tls-0.1.0-cp38-abi3-win_amd64.whl
Size 1.8 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
5dec9d3331f1a73f3f959c9c434f6b65b1e5ffbfa99f0cf83f5c051f9e1b8b32
BLAKE2b-256 checksum
How to use checksums
af3e1c4d4cd900bb19330ad63b971ec17ac488368104b35e67f537f77abf0d4c
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.0-cp38-abi3-manylinux_2_28_x86_64.whl

Download URL okhttp_tls-0.1.0-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
18d143ad0d851c8a4ec56837ca425c96c17085e00bda6a08f30fe392f6b9b7d0
BLAKE2b-256 checksum
How to use checksums
c0ba729cb8a173ca8b2451b1f2c8c6f98f053ae4ecddeb4e125c6be8dff769af
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.0-cp38-abi3-manylinux_2_28_aarch64.whl

Download URL okhttp_tls-0.1.0-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
84edfb06c655acc7af72f6428abd36186dc6a9327ef013756d7eec898eeb40b2
BLAKE2b-256 checksum
How to use checksums
d4fa4a6244df7744f49afed05a28f4283661e7d4cbb1d59b2c067ba3475b09fd
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.0-cp38-abi3-macosx_11_0_arm64.whl

Download URL okhttp_tls-0.1.0-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
c6341fd69b6443afae4d5bfc73624f1960e7dc002bd01bd6db75bef56c160d8b
BLAKE2b-256 checksum
How to use checksums
e6e427e38fd2f94309ed5b7b9f2b380b1408a253b13e17c7f1dd2227128ea149
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.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.

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

Release history Release notifications | RSS feed

This release

0.1.0 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