Metadata-Version: 2.4
Name: never_fox
Version: 0.3.8
Summary: Browser-level (Firefox 152) HTTP client — real NSS TLS, HTTP/2, byte-identical fingerprint
Author: neverl805
License: MIT
Project-URL: Homepage, https://github.com/neverl805/never_fox
Keywords: tls,fingerprint,firefox,ja3,ja4,http2,nss,anti-bot
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: hpack>=4.0
Requires-Dist: brotli
Requires-Dist: zstandard

# never_fox

**发出去的请求在字节层面就是 Firefox 152** —— 不是模拟,是真的。

`never_fox` 是一个 requests 风格的 Python HTTP 客户端,底层链接 **Firefox 真正的 TLS 引擎 NSS**,所以它的 TLS ClientHello 与真 Firefox 152 **逐字节相同(含 ECH、X25519MLKEM768 后量子组)**,HTTP/2 帧与 header 也与真火狐一致。专为**高并发爬虫 / 风控对抗**设计。

> 一句话原理:Chrome 有 Cronet、Firefox 的网络栈是 NSS(TLS)+ Necko。NSS 开源可独立链接 —— 我们直接链接真 NSS,用逆向出来的 Firefox 152 配置驱动它。这是"模拟"做不到的:curl_cffi 这类能对齐 JA3/JA4,但 ECH 等细节对不上,因为它们用的不是 NSS。

## 为什么它是"真的"

在本地受控 HTTPS 服务器上,让**真 Firefox 152** 和 never_fox 都访问,逐字段对比握手数据,并在 **4 个指纹站(peet.ws / browserleaks / scrapfly / howsmyssl,算法各不同)** 交叉验证:

| 维度 | 与真 Firefox 152 |
|---|---|
| TLS ClientHello 原始字节 | ✅ 0 差异 |
| JA3 / JA3N / JA4 / JA4_o / JA4_r | ✅ 全部一致 |
| HTTP/2 Akamai 指纹(SETTINGS/WINDOW_UPDATE/优先级/伪头序) | ✅ 一致 |
| 所有 HTTP header(名 / 顺序 / 值) | ✅ 一致 |
| ECH、record_size_limit、MLKEM、delegated_credentials | ✅ 一致 |

## 特性

| | |
|---|---|
| **真指纹** | 真 NSS 引擎,ClientHello 字节级 == Firefox 152(含 ECH) |
| **requests 兼容** | `get/post/put/patch/delete/head/options`;`headers=` **合并**进默认头(不替换);`Response.headers` 大小写不敏感、`.elapsed`(timedelta)、`.request`/`.links`/`.iter_lines`/`.is_redirect`/`bool(resp)`;异常层级 `RequestException`/`HTTPError`/`ConnectionError`/`Timeout`/`TooManyRedirects`;`auth=(u,p)`、`files=`(multipart)、`hooks=`、按请求 `verify` |
| **异步** | `AsyncSession`,future 驱动,**线程数≈连接数而非请求数** |
| **高并发** | 冷启动单飞建连(一个 origin 收敛到 1 条 h2,像浏览器)+ 多路复用 + 引用计数防崩 |
| **Cookie** | **RFC 6265** cookie jar:`Expires`+`Max-Age`、host-only 不泄漏子域、`/` 边界 path 匹配、default-path、`__Host-`/`__Secure-` 前缀校验;**curl_cffi 风格接口**(`get/set/delete/clear/update/get_dict/jar`、`[]`/`in`/迭代、`CookieConflict`,`Cookies` 别名) |
| **稳健** | 幂等方法才自动重试、取消/超时/超限发 RST_STREAM、`max_response_bytes` 上限 + 解压炸弹防护、跨域重定向剥离 `Authorization` |
| **代理** | HTTP CONNECT + **SOCKS5**(可认证);目标看到真 FF152,代理只见加密隧道 |
| **限流** | 每 host 限速(带抖动)+ 429/503 指数退避(尊重 `Retry-After`) |
| **HTTP/3** | Alt-Svc 感知,基于真 neqo,回读真实 status/headers(失败自动降级 h2) |

## 环境要求与构建

native 引擎是编译产物,按平台分发(像 Cronet)。**重依赖 NSS/NSPR/brotli/zstd 不用我们编译** —— 由各平台包管理器提供预编译版,我们只编 ~250 行的引擎(几秒)。

```bash
# 1) 系统依赖(预编译的 NSS 等)
brew install nss nspr brotli zstd                                  # macOS
sudo apt-get install libnss3-dev libnspr4-dev libbrotli-dev libzstd-dev zlib1g-dev patchelf  # Linux
# Windows: MSYS2 装 mingw-w64-x86_64-{nss,nspr,brotli,zstd,zlib,gcc,pkg-config}

# 2) Python 依赖
pip install hpack brotli zstandard

# 3) 跨平台编译(自动找 NSS,产出 libfxtls.{dylib,so,dll})
python native/build.py

# 4) 自检指纹 == Firefox 152(NSS 版本漂移会在这里报错)
python native/verify.py

# 5)(可选)打包自包含,拷到同 OS+架构机器免依赖运行
python native/bundle.py                                            # -> native/vendor/
```

然后把仓库目录加入 `PYTHONPATH`,`import never_fox` 即可。

### 多平台预编译(GitHub CI)

`.github/workflows/build.yml` 用矩阵在 **Linux x86_64 / Linux arm64 / macOS arm64 / Windows x86_64** 原生 runner 上自动:装预编译 NSS → `build.py` → `verify.py` 指纹门禁 → 上传各平台产物;打 `vX.Y.Z` tag 会把四平台产物附到 GitHub Release。NSS 由包管理器缓存,只在升版本时重拉。

## 快速开始

```python
import never_fox as nf

r = nf.get("https://example.com/", params={"q": "x"})
print(r.status_code, r.ok, r.http_version, r.text[:200])
r.raise_for_status()

r = nf.post("https://httpbin.org/post", json={"hello": "firefox152"})   # 或 data={...} 表单
print(r.json())
```

### Session(Cookie / 重定向 / 连接池)

```python
s = nf.Session()                      # verify=True, h3="auto"
s.get("https://site/login")           # Set-Cookie 自动保存
s.post("https://site/api", data={"a": 1})   # 自动带 Cookie、自动重定向(r.history)
print(s.cookies.as_dict())
s.put(...); s.delete(...); s.patch(...); s.head(...); s.options(...)
s.close()
```

### 异步(高并发)

```python
import asyncio, never_fox as nf

async def main():
    async with nf.AsyncSession() as s:
        # 上千并发共享连接池中的多路复用连接;响应通过 future 等待,
        # 不为每个请求占一个线程(线程数≈连接数,不随请求数增长)。
        rs = await asyncio.gather(*[s.get(f"https://site/p/{i}") for i in range(1000)])
        print(sum(r.ok for r in rs), "ok")

asyncio.run(main())
```

### 爬虫调优(代理 / 限速 / 退避)

```python
s = nf.Session(
    max_connections_per_host=16,    # 每 host 最多连接数
    rate_limit=5,                   # 每 host <= 5 请求/秒(0=不限)
    backoff_retries=3,              # 429/503 指数退避重试,尊重 Retry-After
    retries=3,                      # 连接级重试
    verify=True,                    # 用 Firefox 同款 Mozilla 根证书校验
)

# 代理:HTTP CONNECT 或 SOCKS5(可认证),按会话或按请求,可自由轮换
s = nf.Session(proxy="http://user:pass@proxy:8080")
r = nf.get(url, proxy="socks5://user:pass@10.0.0.1:1080")     # 或 proxies={"https": "..."}
```

`Response`:`.status_code .ok .reason .url .text .content .json() .headers .cookies .history .elapsed .encoding .raise_for_status() .iter_content()`

## 工作原理

```
never_fox/            Python 层
  client.py           Session / Response / 连接池 / Cookie / 重定向 / 限速 / 代理解析
  aio.py              AsyncSession(future 驱动的异步)
  h2conn.py           HTTP/2 多路复用(复刻 Firefox 的 SETTINGS/优先级/伪头序)
  http1.py            HTTP/1.1 回退
  h3.py               HTTP/3(经真 neqo,实验性)
  cookies.py          CookieJar
  _native.py          ctypes 绑定到原生引擎
native/               原生引擎(C,链接真 NSS)
  fxtls_config.h      Firefox 152 的 ClientHello 配置(密码套件/组/签名算法/ECH/证书压缩…)
  fxtls_lib.c         连接 + TLS 握手 + 收发 + 代理(CONNECT/SOCKS5)-> libfxtls.dylib
  bundle.py           把依赖 dylib 收进 vendor/ 并改 @loader_path,便于跨机
harness/              指纹验证脚本(本地抓包对比 + 多站交叉验证)
```

证书用 NSS 内置的 **Mozilla 根证书列表(libnssckbi)** 校验 —— 和 Firefox 同款信任库。

## 已知限制

- 原生库是平台相关二进制:跨 OS / 架构需在目标机重新 `build.sh`(像 Cronet 按平台分发)。Firefox/Linux 是 OS 自洽目标,适合做服务端。
- 会话复用:TLS 1.3 复用握手会带 `pre_shared_key` 扩展,JA3/JA4 与全握手不同 —— **两者都是真 Firefox 指纹**(连接池默认复用同一握手,一般不出现)。
- HTTP/3:需运行环境 UDP/443 出网;失败自动降级 h2。请求体(POST/PUT)暂走 h2。
- requests 差异:`stream=True` 收下但 body 始终全量缓冲;`cert=`(客户端证书)不支持(NSS 用 Mozilla 根),会抛 `NotImplementedError`;`verify='/ca.pem'` 自定义 CA 包不认(走 NSS 根)。
- Cookie:未内置 Public Suffix List(`Domain=co.uk` 这类多标签公共后缀不拒,只拒裸 TLD);`SameSite` 解析存储但不强制(需请求发起方 site 上下文)。

## 验证复现

```bash
# 本地起 HTTPS/h2 服务,真 Firefox + never_fox 都访问,逐字段对比
python harness/localcap/diff_h2cap.py        # 看 harness/localcap/FULL_DIFF.md
# 多站交叉验证报告
cat harness/localcap/MULTISITE.md
```

详细逆向与对比过程见 [REPORT.md](REPORT.md)。

## 免责声明

仅用于授权范围内的安全研究、风控对抗测试与合规数据采集。请遵守目标站点的条款与当地法律。
