Skip to main content

rex-tls

项目授权范围、明确排除项和“指纹一致”的可验证定义见 安全与授权边界

rex-tls 是一个面向 Python 的原生移动端 TLS/HTTP 客户端。它提供接近 requests 的调用方式,并在 Rust 原生扩展中实现 TLS、HTTP/1.1、HTTP/2 以及 Chrome Android 的 HTTP/3 网络路径。

项目使用 vendored BoringSSL,不依赖 wreqreqwestcurl 或系统 OpenSSL。当前源码与待编译候选版本为 2.2.0;PyPI 上已发布的稳定版仍是 rex-tls 2.1.0。项目仅提供预编译二进制 wheel,不发布 sdist;2.2.0 在完成 Windows/Linux CI 验收并获得发布授权前不会上传 PyPI。

适用范围:协议兼容测试、移动客户端互操作、经过授权的网络研究和普通 HTTP 客户端开发。本项目不包含验证码求解、WAF 绕过或站点专用规避逻辑。

支持的 profile

profile 协议 设备验证基线
chrome_android_149 TLS + HTTP/1.1 + HTTP/2 + HTTP/3 authorized Android test device / Android 11 / Chrome 149.0.7827.200
chrome_android_150 TLS + HTTP/1.1 + HTTP/2 + HTTP/3 authorized Android test device / Android 11 / Chrome 150.0.7871.63
okhttp_4.12 TLS + HTTP/1.1 + HTTP/2 authorized Android test device / Android 11 / OkHttp 4.12.0
okhttp_5.4 TLS + HTTP/1.1 + HTTP/2 authorized Android test device / Android 11 / OkHttp Android 5.4.0

四套 profile 均已完成真实设备行为采集,并已在同一个本机候选原生二进制上完成严格验收。 Chrome 149/150 覆盖 TLS、HTTP/2、HTTP/3 wire 与会话行为;OkHttp 4.12/5.4 覆盖冷连接、 TLS 恢复、顺序/并发 H2、POST 和大请求头。两个 OkHttp profile 均不声明 HTTP/3 能力。

安装

python -m pip install rex-tls==2.1.0

以上命令安装当前 PyPI 稳定版。2.2.0 尚处于二进制候选构建阶段,不应在 PyPI 发布前 把该命令改成一个不存在的版本。

要求 Python 3.9 或更高版本。当前构建目标仅提供以下预编译 wheel:

  • Windows x86-64
  • Linux manylinux 2.28 x86-64

2.2.0 暂不构建 macOS wheel,以控制 CI 成本;macOS 上 pip 不会回退到源码构建。

首选导入名是 rex_tlsmobile_tls 仅作为兼容入口保留。

快速开始

import rex_tls

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

response.raise_for_status()
print(response.status_code)       # 200
print(response.http_version)      # HTTP/1.1、HTTP/2 或 HTTP/3
print(response.text)

发送 JSON:

import rex_tls

response = rex_tls.post(
    "https://api.example.com/items",
    profile="okhttp_4.12",
    json={"name": "测试", "enabled": True},
)
print(response.json())

推荐:复用 Session

Session 会复用连接、Cookie 和 TLS 会话,连续请求时应优先使用:

from rex_tls import Session

with Session(
    "chrome_android_150",
    timeout=20,
    follow_redirects=True,
    max_redirects=10,
) as session:
    session.headers.update({"x-client-id": "demo"})

    first = session.get("https://example.com/start")
    second = session.get("https://example.com/next")

    print(first.connection_reused)
    print(second.connection_reused)

请求参数与请求体

from rex_tls import Session

with Session("okhttp_4.12") as session:
    # 查询参数
    r1 = session.get(
        "https://api.example.com/search",
        params={"q": "中文", "page": 2},
    )

    # application/x-www-form-urlencoded
    r2 = session.post(
        "https://api.example.com/form",
        data={"username": "demo", "remember": "1"},
    )

    # application/json; charset=utf-8
    r3 = session.post(
        "https://api.example.com/json",
        json={"message": "你好"},
    )

    # 原始内容
    r4 = session.put(
        "https://api.example.com/raw",
        content=b"raw bytes",
        headers={"content-type": "application/octet-stream"},
    )

contentdatajson 三者互斥。支持 getoptionsheadpostputpatchdelete 以及通用 request

请求头顺序

底层在 ALPN 确定最终协议后,才选择 H1、H2 或 H3 的请求头策略:

  • 合并后的用户请求头为空时,使用该 profile、协议和请求上下文的完整默认头;
  • 合并后的用户请求头非空时,只排序用户提供的字段,不注入 profile 默认头;
  • Chrome 已知字段进入实机分类槽位;未知或自定义字段保持彼此之间的调用者顺序;
  • OkHttp 的应用/拦截器决定普通字段顺序,因此保留调用者顺序;
  • 需要重复同名头时,可传入元组列表;
  • Session 级请求头会先与单次请求头进行大小写不敏感合并;
  • Host/:authority、H2/H3 伪头及请求体所需的 Content-Length 属于协议元数据, 由底层生成,不属于默认头注入。
from rex_tls import Session

with Session("chrome_android_149") as session:
    session.headers.update({"accept-language": "zh-CN,zh;q=0.9"})
    response = session.get(
        "https://example.com/",
        headers=[
            ("user-agent", "My-Mobile-Client/1.0"),
            ("accept", "application/json"),
            ("x-trace-a", "1"),
            ("x-trace-b", "2"),
        ],
    )

如果需要 profile 的整套默认头,请不要传 headers,也不要修改 Session.headers。一旦传入 任意用户头,该集合就被视为完整的用户普通头集合;例如只传 Accept 时不会再自动增加 User-AgentAccept-Language

该判定早于 CookieJar 和 json/data 自动生成的 Content-Type:只传 cookies= 或请求体 仍会保留完整 profile 默认头,再把这些派生字段放入对应协议的排序位置。

H1 字段名会使用实机观察到的大小写(例如 HostAccept-Encoding,而 sec-ch-ua 保持小写)。HTTP 字段名在语义上大小写不敏感;H2/H3 则按协议要求只能发送小写字段名。 H2/H3 中 Host 会转换为 :authorityConnectionKeep-Alive 等逐跳头是非法字段, 库会明确报错而不会静默删除。排序表覆盖内置头和常见标准请求字段;任意 X-* 名称空间 无限,无法由有限静态表枚举,因此未知字段采用稳定回退规则。

Chrome 的文档导航上下文可显式控制。下例对应“跨站、无用户激活”的主导航,请求头中的 sec-fetch-site 会是 cross-site,并省略 sec-fetch-user

with Session("chrome_android_149") as session:
    response = session.get(
        "https://example.com/",
        navigation_site="cross-site",
        user_activation=False,
    )

navigation_site 可取 nonesame-originsame-sitecross-site;这两个参数只适用于 Chrome profile,并会沿同一次原生重定向链传播。库复现显式请求本身,不额外创建浏览器进程 可能产生的预连接或 favicon 请求。

HTTP 代理

代理映射与 requests 风格相近:

from rex_tls import Session

proxies = {
    "http": "http://user:password@proxy.example:8080",
    "https": "http://user:password@proxy.example:8080",
    "no_proxy": ".internal.example,127.0.0.1",
}

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

也可在单次请求覆盖代理:

response = session.get(
    "https://example.com/",
    proxies={"https": "http://other-proxy.example:3128"},
)

# 显式为这一次请求禁用 HTTPS 代理
direct = session.get("https://example.com/", proxies={"https": None})

从环境变量读取 HTTP_PROXYHTTPS_PROXYALL_PROXYNO_PROXY

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

当前代理能力边界:

  • 支持 http:// forward proxy 和 HTTPS 目标的 HTTP CONNECT;
  • 支持代理 URL 中的 Basic 用户名/密码;
  • 不支持 https:// proxy、SOCKS、PAC、NTLM/Digest 或 MASQUE;
  • 代理失败不会静默改成直连;
  • 不要把 Proxy-Authorization 放入源站请求头,凭据应写在代理 URL 中;
  • 传统 HTTP CONNECT 不能承载 QUIC,因此代理场景不使用 HTTP/3。

强制 HTTP/2

需要服务端实际协商 H2 时使用 http2=True

from rex_tls import Session

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

该选项保留 profile 已采集的 h2,http/1.1 ALPN ClientHello 向量,在握手后验证服务端确实 选择 h2;这避免为了“强制”而改变 TLS 指纹。若服务端选择 H1、URL 是明文 HTTP(当前 不支持 h2c),请求会明确失败。http2=Truehttp3="auto"/"only" 互斥。强制 H2 时不要提供 Connection 等 H1 专用头。

顶层 rex_tls.get(..., http2=True)AsyncSessionSessionPoolAsyncSessionPool 同样支持这个构造选项。

HTTP/3

HTTP/3 只适用于两个显式 Chrome profile,并且只支持 HTTPS URL:

from rex_tls import Session

# 优先尝试 HTTP/3,并允许按协议策略回退到 H2/H1
with Session("chrome_android_149", http3="auto") as session:
    response = session.get("https://example.com/")

# 只允许 HTTP/3;无法建立 H3 时直接报错
with Session("chrome_android_150", http3="only") as session:
    response = session.get("https://example.com/")

http3 可取 "off""auto""only",默认是 "off"。使用 H3 时必须写完整的 chrome_android_149chrome_android_150,不要使用别名。 okhttp_4.12okhttp_5.4 均不支持 H3。okhttpokhttp_latest 当前解析为 okhttp_5.4;需要长期复现时应固定完整 profile 名称。

异步接口

import asyncio
from rex_tls import AsyncSession

async def main() -> None:
    async with AsyncSession("okhttp_4.12") as session:
        response = await session.get("https://example.com/", timeout=15)
        response.raise_for_status()
        print(response.status_code, response.http_version)

asyncio.run(main())

取消 asyncio Task 时,客户端会尝试取消对应的原生请求。

内置并发池

SessionPool 面向多线程,AsyncSessionPool 面向 asyncio。每个池成员拥有独立的 TLS/HTTP 连接状态,因此最多可以同时运行 max_connections 个请求;超过上限的请求会 排队。普通 Session 本身可由多线程并发调用,AsyncSession(max_concurrency=8) 默认用 有界 worker 执行并发请求;同 route 的 HTTP/2 请求可复用一条 TCP/TLS 连接上的多个 stream。HTTP/1.1 和当前 HTTP/3 驱动仍按单连接串行。

import asyncio
from rex_tls import AsyncSessionPool

async def main() -> None:
    async with AsyncSessionPool(
        "chrome_android_150",
        max_connections=16,
        session_mode="shared",
        proxy="http://user:password@proxy.example:8080",
    ) as pool:
        pool.headers["x-client"] = "pool-demo"
        responses = await asyncio.gather(
            *(pool.get(f"https://example.com/?id={index}") for index in range(100))
        )
        print([response.status_code for response in responses])

asyncio.run(main())

池的可变会话状态有两种模式:

  • session_mode="shared"(默认):所有连接共享默认 Header、代理配置和线程安全 CookieJar,整个池表现为一个登录会话;
  • session_mode="isolated":每个连接的 Header、代理配置和 Cookie 均完全独立,适合 多账号或任务隔离。需要连续使用同一身份时,用 async with pool.acquire() as session: 固定租用一个成员。
async with AsyncSessionPool(
    "okhttp_4.12", max_connections=8, session_mode="isolated"
) as pool:
    async with pool.acquire() as session:
        session.headers["authorization"] = "Bearer account-one"
        session.proxies["https"] = "http://account-one.proxy:8080"
        session.cookies.set("account", "one")
        first = await session.get("https://example.com/first")
        second = await session.get("https://example.com/second")

同步代码可以在线程中共享一个池:

from concurrent.futures import ThreadPoolExecutor
from rex_tls import SessionPool

with SessionPool("okhttp_4.12", max_connections=8) as pool:
    with ThreadPoolExecutor(max_workers=32) as executor:
        responses = list(executor.map(pool.get, urls))

pool_timeout= 只控制等待空闲池成员的时间;timeout= 仍控制网络请求。池级 headersproxiestrust_env 会应用到所有成员。

Response 对象

常用属性和方法:

response.status_code       # int
response.reason            # str
response.url               # 最终 URL
response.http_version      # HTTP/1.1、HTTP/2 或 HTTP/3
response.headers           # 大小写不敏感的响应头映射
response.headers.raw       # 保留顺序及重复项的 tuple
response.headers.get_all("set-cookie")
response.content           # bytes
response.text              # 按 charset 解码后的 str
response.json()            # JSON 解码
response.ok                # status_code < 400
response.elapsed_seconds
response.local_address
response.remote_address
response.connection_reused
response.tls_session_reused
response.raise_for_status()

TLS 证书验证

默认使用 certifi CA 包并验证证书与主机名:

from rex_tls import Session

# 默认验证
session = Session("chrome_android_149", verify=True)

# 使用自定义 CA bundle
session = Session("chrome_android_149", verify="/path/to/ca-bundle.pem")

# 仅用于受控测试环境
session = Session("chrome_android_149", verify=False)

异常处理

import rex_tls

try:
    response = rex_tls.get(
        "https://example.com/",
        profile="chrome_android_149",
        timeout=10,
    )
    response.raise_for_status()
except rex_tls.RequestError as exc:
    print(f"请求失败: {exc}")
except rex_tls.MobileTLSError as exc:
    print(f"原生客户端错误: {exc}")

查询运行时能力

import rex_tls

print(rex_tls.profiles())
print(rex_tls.profile_info("chrome_android_149"))
print(dict(rex_tls.native_versions()))

流式下载与上传

with rex_tls.Session("chrome_android_150") as session:
    with session.get(url, stream=True) as response:
        for chunk in response.iter_content(64 * 1024):
            process(chunk)

    with open("large.bin", "rb") as source:
        response = session.post(upload_url, content=source)

    with open("large.bin", "rb") as multipart_source:
        response = session.post(
            upload_url,
            data={"field": "value"},
            files={"file": ("large.bin", multipart_source)},
        )

decode_content=False 返回 gzip/br/zstd/deflate 的原始 wire representation。 文件在 307/308 重定向时会回到调用前偏移;一次性生成器需要重放时抛出 UnrewindableBodyError。H1、H2、H3 均使用原生流式核心。

与 requests 的差异

rex-tls 对齐的是常用调用方式,不是 requests 的完全兼容替代:

  • 当前没有公开的 authhooksadaptersmount
  • Session.cookies 提供常用的 RequestsCookieJar 风格 set/set_cookie/get/get_dict/update/clear/items/keys/values/list_domains/list_paths 接口,并支持 domain、path、Secure 和 expires;单次请求也支持 cookies=, 显式 Cookie 请求头优先;
  • 响应中的合法 Domain= Cookie 会自动进入原生 CookieJar 并可发送到匹配的 子域;公共后缀、跨域 Domain 和 IP Domain Cookie 会被拒绝;
  • Session.headersSession.proxies 是可修改字典;
  • Cookie、重定向、连接池与 TLS 会话由原生 Session 管理;
  • verify、代理类型和 HTTP/3 有更严格的安全约束;
  • 请求头最终顺序由 profile 决定,而不是简单照搬 Python 字典顺序。

完整中文手册见 中文使用指南。 最近一次 Windows/Linux 二进制构建记录见 CI 二进制构建记录

2.2.0 候选内容

  • 修正响应 Cookie 的 Domain= 接收与 requests 风格 CookieJar 兼容行为;
  • 完成 stream=Trueiter_content()、流式解压和取消/资源上限相关实现;
  • 补强 HTTP/3/QPACK 生命周期、请求取消与 wire 门禁;
  • 仅构建 Windows x86-64 与 manylinux 2.28 x86-64 的 CPython 3.9+ abi3 wheel;
  • 不构建 macOS wheel,不生成或上传源码包。

候选 wheel 必须由版本号已经固定为 2.2.0 的提交直接构建并安装复验;不能先编译旧版本, 再只修改 Python 元数据或文件名。

2.1.0 发布内容

  • 提供 chrome_android_149chrome_android_150okhttp_4.12okhttp_5.4 四个固定版本 profile;
  • 提供 requests 风格的同步/异步 Session、CookieJar、单次 cookies=、代理配置和 shared/isolated 并发池;
  • Chrome profile 支持 HTTP/1.1、HTTP/2 与 HTTP/3,OkHttp profile 支持 HTTP/1.1 与 HTTP/2;
  • 请求头合并与最终 wire 顺序由底层 profile 规则处理;
  • 发布 Windows x86-64 与 manylinux 2.28 x86-64 两个 CPython 3.9+ abi3 wheel, 不上传 macOS wheel 或源码包。

2.1.0 的协议核心继承已完成真实 authorized Android test device / Android 11 数据包与会话验收的 0.1.2 候选; 本次版本变更不修改 src、vendored BoringSSL 或 profile 实现。CI 会逐平台构建并直接测试 最终上传的同一份 wheel,同时检查协议实现相对已验收基线没有漂移。

0.1.2 稳定性改进(2.1.0 已包含)

  • 接受服务端合法的 HTTP/2 SETTINGS_ENABLE_PUSH=0,继续拒绝非法值 1
  • TLS 对端未发送 close_notify 时,由 HTTP framing 状态机判断完整性:完整响应可返回, 不完整的 Content-Length、chunked 或 HTTP/2 响应仍会失败,连接不会复用;
  • 空闲 H1/H2 连接默认最多 32 条、90 秒过期;TLS 与 H3 ticket 默认最多 64 个、 10 分钟过期;H3 Alt-Svc 路由最多 64 条并按 LRU 淘汰;
  • 阻塞式系统 DNS 解析使用进程级有界 worker/队列,超时或取消不会无限创建后台线程;
  • Session.close() 会同时释放空闲连接和缓存的会话票据。

这些是资源边界与协议互操作修复,不改变四个 profile 的 ClientHello、HTTP/2 客户端 SETTINGS、帧顺序或请求头 wire 顺序。Flutter/Dart profile 归入 OkHttp 5.x 之后的规划; Flutter 默认 Dart IO、Android Cronet 与 iOS URLSession 会分别建模。

开发与验证

python -m pip install -e ".[dev]"
python -m pytest -m "not network" -q

架构和证据说明位于 docs/;发布工作流见 docs/32-github-ci-and-publishing.md,本机四库性能 基准见 docs/34-performance.md

性能优化与多轮发布门禁

并发性能基准现在可以直接比较普通 Session、内置连接池和 curl_cffi:

py -3.14 tools/benchmark_rex_tls.py run `
  --clients rex_tls,rex_tls_pool,curl_cffi `
  --modes async --reuse warm `
  --concurrencies 1,8,32 --server-delay-ms 10 `
  --samples 256 --warmup 32 `
  --output .tmp/performance.json

发布前必须执行多轮门禁;任何一轮失败都会清零对应连续通过计数。重复执行器会保存每轮日志、 返回码和 SHA-256:

py tools/run_repeated_gate.py `
  --label rust-release --rounds 5 `
  --output-dir .tmp/gates/rust-release -- `
  cargo test --locked --lib --release

完整优化路线、基线和发布条件见 docs/35-performance-optimization-plan.md,本轮本机结果与 证据摘要见docs/36-performance-optimization-results-20260818.md。 流式上传、stream=Trueiter_content()与H1/H2/H3分阶段实施方案见 docs/37-streaming-implementation-plan.md

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.0-cp39-abi3-win_amd64.whl (2.7 MB view details)

Uploaded CPython 3.9+Windows x86-64

rex_tls-2.2.0-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.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: rex_tls-2.2.0-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.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 df89e9c7546721ab2b98ad1692f672def16c6bb253f998c2d1c8a2331e4cd7d2
MD5 04f7f3a91065d2b0472228b9f3f7a738
BLAKE2b-256 f81221e8fd88f24acbdfb8297574315b05e874bc71fd0368642ddd24d991c35d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for rex_tls-2.2.0-cp39-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9d7fcce2a859c221b5d5b567c1744b8c6bc3f9cf1b5a932d9e447ae9af3522ed
MD5 a10b06914f36edeb76a70f1bc24397e5
BLAKE2b-256 48192a2815c31ee6343809378323f27ddcc8c3ca9602e2d8eb0bc6335a0b1277

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

2.2.1

2 files

This release

2.2.0 This release

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