apppublic/aidocs/streamhttpclient.md
2025-10-05 11:23:33 +08:00

317 lines
8.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `StreamHttpClient` 技术文档
```markdown
# StreamHttpClient - 异步流式 HTTP 客户端
一个基于 `aiohttp``aiohttp_socks` 的异步、支持 SOCKS5 代理的流式 HTTP 客户端具备自动降级到代理机制、SSL 配置管理、连接失败重试等功能。
---
## 📌 概述
`StreamHttpClient` 是一个用于发起异步 HTTP 请求的 Python 类,主要特性包括:
- 支持 **直接请求****SOCKS5 代理请求**
- 自动检测连接失败并尝试切换至 SOCKS5 代理(智能回退)
- 支持流式响应处理chunked streaming适用于大文件或长响应场景
- 可配置 SSL 验证行为(如跳过证书验证)
- 支持上传文件和表单数据
- 维护需要使用代理的 URL 列表(持久化到本地文件)
---
## 🔧 环境依赖
### Python 版本
```bash
Python >= 3.7
```
> 示例中使用的是虚拟环境下的 Python 3.12 路径:
> ```python
> #!/Users/ymq/p3.12/bin/python
> ```
### 第三方库依赖
| 包 | 用途 |
|----|------|
| `aiohttp` | 异步 HTTP 客户端/服务器框架 |
| `aiohttp_socks` | 提供对 SOCKS4/SOCKS5 代理的支持 |
| `certifi` | 提供 Mozilla 的 CA 证书包,用于 SSL 验证 |
| `ssl` | Python 内置模块,用于 SSL/TLS 配置 |
### 可选日志模块
```python
from appPublic.log import exception, debug
```
> 需确保项目中存在此模块,否则需替换为标准日志工具(如 `logging`)。
---
## 📦 核心功能
### ✅ 流式行解析器:`async def liner(async_gen)`
将字节流按 `\n` 分割为每一行,并以异步生成器方式逐行输出。
#### 参数
- `async_gen`: 异步生成器,产出 `bytes` 类型的数据块
#### 返回
- `AsyncGenerator[bytes]`: 每次 `yield` 一行内容(末尾不含 `\n`
#### 示例用法
```python
async for line in liner(response.content.iter_chunked(1024)):
print(line.decode('utf-8'))
```
> 注意:原始代码中该函数未被调用,但可作为工具扩展使用。
---
### 🔐 SSL 上下文构建:`get_non_verify_ssl()`
创建一个不验证主机名和证书的 SSL 上下文,用于绕过 HTTPS 证书检查。
#### 返回
- `ssl.SSLContext`: 配置为 `CERT_NONE` 且关闭 `check_hostname` 的上下文
> ⚠️ 安全警告:仅建议在测试或可信网络中使用,生产环境慎用!
---
## 🧱 主要类:`StreamHttpClient`
### 初始化:`__init__(socks5_url="socks5://127.0.0.1:1086")`
#### 参数
| 参数 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| `socks5_url` | str | `"socks5://127.0.0.1:1086"` | 默认使用的 SOCKS5 代理地址 |
#### 动作
- 加载本地保存的需走代理的 URL 列表(从 `~/.socksurls.txt`
- 创建默认 SSL 上下文(使用 `certifi` 来源的 CA 证书)
#### 属性
| 属性 | 类型 | 描述 |
|------|------|------|
| `socks_urls_file` | Path | 存储代理 URL 列表的文件路径 |
| `socks_urls` | set[str] | 当前已知需要通过 SOCKS 访问的 URL 集合 |
| `ssl_context` | ssl.SSLContext | 默认的安全 SSL 上下文 |
---
### 🔁 私有方法
#### `_load_socks_urls() -> List[str]`
`~/.socksurls.txt` 文件读取所有需要走代理的 URL。
> 若文件不存在,则返回空列表。
#### `_save_socks_url(url: str)`
将指定 URL 添加到 `socks_urls` 集合并追加写入 `.socksurls.txt` 文件,防止重复添加。
---
## 🌐 核心请求方法
### `__call__()` 方法(主入口)
异步发起 HTTP 请求,支持自动故障转移至 SOCKS5 代理。
#### 签名
```python
async def __call__(
self,
method: str,
url: str,
*,
headers=None,
params=None,
data=None,
json=None,
files=None,
chunk_size=1024,
**kw
)
```
#### 参数说明
| 参数 | 类型 | 描述 |
|------|------|------|
| `method` | str | HTTP 方法GET, POST 等) |
| `url` | str | 请求目标 URL |
| `headers` | dict | 请求头 |
| `params` | dict | 查询参数URL query string |
| `data` | Any | 表单或原始请求体数据 |
| `json` | dict | JSON 序列化数据(会设置 `Content-Type: application/json` |
| `files` | dict | 文件上传字段,格式:`{"name": (filename, fileobj, content_type)}` |
| `chunk_size` | int | 每次读取响应内容的大小(字节) |
| `verify` | bool | 是否启用 SSL 验证(自定义关键字,非 aiohttp 原生参数) |
| `**kw` | 其他参数 | 传递给 `session.request()` 的其他参数 |
#### 工作流程
1. 判断当前 URL 是否在 `self.socks_urls` 中:
- 是 → 直接使用 SOCKS5 代理发送请求
- 否 → 尝试直连
2. 直连失败(`ClientConnectionError`)时:
- 打印错误日志
- 切换为 SOCKS5 代理重试
- 成功后将该 URL 记录进 `~/.socksurls.txt`
3. 其他异常则抛出
#### 日志说明
| 日志符号 | 含义 |
|---------|------|
| `🌐` | 尝试直连 |
| `🔁` | 使用 SOCKS5 代理 |
| `❌` | 请求失败 |
| `🧦` | 正在使用袜子SOCKS代理重试 |
---
### `request()` 方法(便捷封装)
同步等待完整响应体返回。
```python
async def request(...)
```
#### 行为
- 调用 `__call__()` 获取所有 chunk
- 合并为完整的 `bytes` 返回
> ⚠️ 对于大型响应,请优先使用 `__call__()` 进行流式处理以避免内存溢出。
---
### `_request_with_connector()` 内部实现
真正执行请求的核心方法。
#### 参数
`__call__`,额外包含:
- `use_socks`: 是否使用 SOCKS5 代理
- `ssl_context`: 使用的 SSL 上下文对象
#### 实现细节
- 使用 `ProxyConnector.from_url()` 构造代理连接器(若 `use_socks=True`
- 支持动态选择 SSL 验证模式:
-`verify=False` 在 kw 中 → 使用无验证 SSL 上下文
- 否则使用默认安全上下文
- 处理 `files``data/json` 的编码逻辑:
- 存在 `files` → 构造 `FormData`
- 否则根据是否有 `json` 参数决定使用 `json=``data=`
- 使用 `session.request()` 发起请求
- 流式读取响应内容(`iter_chunked(chunk_size)`
---
## 💡 使用示例
### 基本 GET 请求
```python
hc = StreamHttpClient()
response = await hc.request("GET", "https://httpbin.org/get")
print(response.decode())
```
### POST JSON 数据
```python
payload = {"key": "value"}
resp = await hc.request("POST", "https://httpbin.org/post", json=payload)
print(resp.decode())
```
### 文件上传
```python
with open("test.txt", "rb") as f:
files = {
"file": ("test.txt", f, "text/plain")
}
data = {"meta": "info"}
resp = await hc.request("POST", "https://example.com/upload", data=data, files=files)
```
### 忽略 SSL 验证(谨慎使用)
```python
resp = await hc.request("GET", "https://self-signed.badssl.com/", verify=False, timeout=5)
```
---
## 🖥️ CLI 主程序(测试入口)
当脚本直接运行时,执行简单测试请求。
### 用法
```bash
python stream_http_client.py "your prompt"
```
> 当前示例中未实际使用 `prompt`,而是固定请求百度首页。
#### 示例输出
```bash
b'<!DOCTYPE html>...'
```
---
## 📁 文件结构影响
### 创建的本地文件
- `~/.socksurls.txt`
- 存储曾经因直连失败而改用 SOCKS5 的 URL 列表
- 每行一个 URL自动去重
> 示例:
```
https://blocked-site.com
https://internal-api.example.org
```
---
## ⚠️ 注意事项 & 最佳实践
1. **安全性**
- `verify=False` 会禁用 SSL 验证,可能导致中间人攻击。
- 生产环境中应尽量避免使用。
2. **性能**
- 流式接口适合处理大响应;小请求推荐使用 `request()` 获取完整结果。
- `chunk_size` 可调节性能与延迟平衡。
3. **异常处理**
- 所有非连接类异常都会重新抛出。
- 建议外部捕获 `Exception` 并做适当处理。
4. **日志系统依赖**
- 依赖 `appPublic.log.debug``.exception` 函数。
- 如不可用,请替换为标准 `logging` 模块。
---
## 🛠 TODO / 改进建议
| 功能 | 建议 |
|------|------|
| 更灵活的日志注入 | 支持传入 logger 实例 |
| 支持更多代理类型 | 如 HTTP 代理、认证代理等 |
| 自动清理无效 URL | 定期验证 `.socksurls.txt` 中的链接是否仍需代理 |
| 支持超时配置 | 允许用户自定义 `timeout` 对象而非仅秒数 |
| 增加单元测试 | 覆盖直连、代理、失败回退等场景 |
---
## 📎 License
MIT假设
作者:未知
更新时间2025年4月
> 请结合实际项目规范进行调整。
```