rex-tls
rex-tls 是一个面向 Python 的原生移动端 TLS/HTTP 客户端。它提供接近
requests 的调用方式,并在 Rust 原生扩展中实现 TLS、HTTP/1.1、HTTP/2
以及 Chrome Android 的 HTTP/3 网络路径。
项目使用 vendored BoringSSL,不依赖 wreq、reqwest、curl 或系统
OpenSSL。当前版本已经发布到 PyPI:
rex-tls 0.1.1。
适用范围:协议兼容测试、移动客户端互操作、经过授权的网络研究和普通 HTTP 客户端开发。本项目不包含验证码求解、WAF 绕过或站点专用规避逻辑。
支持的 profile
| profile | 协议 | 设备验证基线 |
|---|---|---|
chrome_android_149 |
TLS + HTTP/1.1 + HTTP/2 + HTTP/3 | Pixel 4 / Android 11 / Chrome 149.0.7827.200 |
chrome_android_150 |
TLS + HTTP/1.1 + HTTP/2 + HTTP/3 | Pixel 4 / Android 11 / Chrome 150.0.7871.63 |
okhttp_4.12 |
TLS + HTTP/1.1 + HTTP/2 | Pixel 4 / Android 11 / OkHttp 4.12.0 |
三套 profile 均完成真实设备 L4 行为验证。OkHttp 4.12 不声明 HTTP/3 能力。
安装
python -m pip install rex-tls==0.1.1
要求 Python 3.9 或更高版本。0.1.1 提供以下预编译 wheel:
- Windows x86-64
- Linux manylinux 2.28 x86-64
- macOS x86-64
- macOS ARM64
首选导入名是 rex_tls;mobile_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"},
)
content、data 和 json 三者互斥。支持 get、options、head、
post、put、patch、delete 以及通用 request。
请求头顺序
底层会自动按选定 profile 的设备规则排列请求头:
- 覆盖默认头时,头部保留在真实设备对应的槽位;
- 新增的非默认头在 profile 默认头之后,按调用者传入顺序排列;
- 需要重复同名头时,可传入元组列表;
- Session 级请求头会先与单次请求头进行大小写不敏感合并;
Host、HTTP/2/HTTP/3 伪头及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"),
],
)
不需要手工复制全部 Chrome/OkHttp 默认请求头。只传入需要覆盖或追加的头即可。
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_PROXY、HTTPS_PROXY、ALL_PROXY、NO_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/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_149 或 chrome_android_150,不要使用别名。
okhttp_4.12 不支持 H3。
异步接口
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 个请求;超过上限的请求会
排队。当前并发来自多条连接,不表示在单条 HTTP/2 或 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= 仍控制网络请求。池级
headers、proxies 和 trust_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()))
与 requests 的差异
rex-tls 对齐的是常用调用方式,不是 requests 的完全兼容替代:
- 当前没有公开的
auth、hooks、adapters和mount; Session.cookies提供常用的 RequestsCookieJar 风格set/get/get_dict/update/clear接口,并支持 domain、path、Secure 和 expires;单次请求也支持cookies=, 显式Cookie请求头优先;Session.headers与Session.proxies是可修改字典;- Cookie、重定向、连接池与 TLS 会话由原生 Session 管理;
verify、代理类型和 HTTP/3 有更严格的安全约束;- 请求头最终顺序由 profile 决定,而不是简单照搬 Python 字典顺序。
完整中文手册见 中文使用指南。
开发与验证
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。
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rex_tls-0.1.1.tar.gz.
File metadata
- Download URL: rex_tls-0.1.1.tar.gz
- Upload date:
- Size: 5.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
85085f243e75eefd0a6783b71f5efe5111c0ae0863311c1bae3c2e607fa7c022
|
|
| MD5 |
d059488c08506dd3af3da70bd668ea01
|
|
| BLAKE2b-256 |
efd7e6ac59efd1d43c7a3fd46ee84116ecc6c35433ea3c361ccbf512f6db27e3
|
File details
Details for the file rex_tls-0.1.1-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: rex_tls-0.1.1-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 1.8 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a69172bad2d50aa72a050996264da06ee51ef59a3922619f478db30aca60c1ce
|
|
| MD5 |
3ab9cfa3834debdc36d4881914a37a31
|
|
| BLAKE2b-256 |
430bfa86b35db04170ef71de8e84d2223d1d60a7ebf4e6f94f0059b84898fbd9
|
File details
Details for the file rex_tls-0.1.1-cp39-abi3-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: rex_tls-0.1.1-cp39-abi3-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 2.0 MB
- Tags: CPython 3.9+, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07d0f85cb6cfa9112fc78eaec3f00078dd12aa2b9c79ec54ceb820557449d8db
|
|
| MD5 |
1229ec416837bd0e552ba8241d8d9b3b
|
|
| BLAKE2b-256 |
c472272d82ceeb93cba50383bf17af99d634d59ae7ca9666f2a92980a3cd4e64
|
File details
Details for the file rex_tls-0.1.1-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: rex_tls-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 1.8 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f898ba11ee6f039aba21c1d0f21a0e1e4012b7c2376dcc17022f72b9126f89f
|
|
| MD5 |
61086e5b64a7a559dbee8633ff8182fd
|
|
| BLAKE2b-256 |
94d5b042961bf8f9c129979ebb240a08d080c90e5b4378c0f4e42186630f0c71
|
File details
Details for the file rex_tls-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: rex_tls-0.1.1-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 1.9 MB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6ce3bb2a7ef9b3d4be3135ea6b9467692ceffc3507d511b5cfdeda6ea0695e5
|
|
| MD5 |
a50886d84417bf1a902b905a25986b40
|
|
| BLAKE2b-256 |
9f805e9e6e8d9c5341ea4881174da134de69b160b7f251a676e7f0d136fbd5e2
|