procproxy 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {procproxy-0.1.0/src/procproxy.egg-info → procproxy-0.1.2}/PKG-INFO +162 -16
- {procproxy-0.1.0 → procproxy-0.1.2}/README.md +161 -15
- {procproxy-0.1.0 → procproxy-0.1.2}/pyproject.toml +4 -1
- {procproxy-0.1.0 → procproxy-0.1.2/src/procproxy.egg-info}/PKG-INFO +162 -16
- {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/SOURCES.txt +2 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/__init__.py +1 -1
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/__main__.py +7 -7
- procproxy-0.1.2/src/proxycore/api.py +315 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/canproxy.py +23 -18
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/checkroute.py +14 -5
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/cli.py +135 -37
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/config.py +28 -6
- procproxy-0.1.2/src/proxycore/config_source.py +154 -0
- procproxy-0.1.2/src/proxycore/data/default.yaml +15 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/service/__init__.py +11 -2
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/service/__main__.py +19 -5
- procproxy-0.1.0/src/proxycore/api.py +0 -226
- {procproxy-0.1.0 → procproxy-0.1.2}/LICENSE +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/setup.cfg +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/dependency_links.txt +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/entry_points.txt +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/requires.txt +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/procproxy.egg-info/top_level.txt +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/__init__.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/base.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/null.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/__init__.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/filter.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/pidmap.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/backend/windivert/pkt.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/__init__.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/cache.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/modes.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/dns/udp.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/forward.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/ipc.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/log.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/pathsetup.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/probe.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/proc.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/rules.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/runtime.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/__init__.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/relay.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/sessions.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/tunnel/sink.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/types.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/__init__.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/base.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/chain.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/http_connect.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/socks4.py +0 -0
- {procproxy-0.1.0 → procproxy-0.1.2}/src/proxycore/upstream/socks5.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: procproxy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: ProcProxy —— 进程级代理:按进程决定哪条连接走哪个上游代理
|
|
5
5
|
Author: Longersking
|
|
6
6
|
License-Expression: MIT
|
|
@@ -68,8 +68,26 @@ python procproxy.py doctor | rules | sessions | ... # 子命令(等价于 pyt
|
|
|
68
68
|
|
|
69
69
|
需要 **Python 3.10+**。
|
|
70
70
|
|
|
71
|
+
### 从 PyPI 安装(推荐)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install procproxy
|
|
75
|
+
procproxy init # 生成 ~/.procproxy/config.yaml
|
|
76
|
+
procproxy doctor # 环境自检
|
|
77
|
+
procproxy forward WeChat.exe --port 8848 # 真实转发(需管理员)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
安装后直接运行 `procproxy` 会显示首次使用指引,不依赖任何样例文件。
|
|
81
|
+
Python SDK 也一样:`start_proxy()` 不传配置即可启动(内置 `backend=null`),
|
|
82
|
+
不需要先跑 `init`;`init` 只用于生成可编辑配置。
|
|
83
|
+
|
|
84
|
+
非管理员:WinDivert 抓包必须管理员;可以用 `--backend null` 做转发链路联调,
|
|
85
|
+
或在服务已启动、且配置了 `service.ipc_allow_group` 时读取状态。
|
|
86
|
+
|
|
87
|
+
### 源码开发安装
|
|
88
|
+
|
|
71
89
|
```bash
|
|
72
|
-
pip install -e .
|
|
90
|
+
pip install -e ".[dev]"
|
|
73
91
|
# 或者不装(零依赖方式,往 site-packages 写一个 .pth,让 python -m proxycore 全局可用)
|
|
74
92
|
python procproxy.py install-path
|
|
75
93
|
```
|
|
@@ -77,7 +95,7 @@ python procproxy.py install-path
|
|
|
77
95
|
数据面依赖(要用真抓包就必需):
|
|
78
96
|
|
|
79
97
|
```bash
|
|
80
|
-
pip install
|
|
98
|
+
pip install "procproxy[windivert]"
|
|
81
99
|
```
|
|
82
100
|
|
|
83
101
|
> `pydivert` 自带**已签名**的 `WinDivert64.sys` —— 所以**不需要**开测试签名、不需要自己签驱动。
|
|
@@ -328,24 +346,151 @@ rules: # 自上而下,**首个命中生
|
|
|
328
346
|
|
|
329
347
|
## 编程接口
|
|
330
348
|
|
|
349
|
+
`proxycore.api` 是与 CLI 共用同一套配置、规则引擎和会话表的 SDK。
|
|
350
|
+
完整教程见 [`docs/15-编程接口与SDK.md`](docs/15-编程接口与SDK.md)。
|
|
351
|
+
|
|
352
|
+
### 30 秒:零配置启动
|
|
353
|
+
|
|
331
354
|
```python
|
|
332
|
-
from proxycore import
|
|
355
|
+
from proxycore.api import start_proxy
|
|
333
356
|
|
|
334
|
-
#
|
|
335
|
-
|
|
336
|
-
print(
|
|
357
|
+
with start_proxy() as ppx: # 不需要任何配置文件
|
|
358
|
+
print(ppx.config_source) # "packaged"
|
|
359
|
+
print(ppx.config_path) # .../proxycore/data/default.yaml
|
|
360
|
+
print(ppx.status()) # 后端 / 规则 / 会话 / UDP / 配置来源
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
不传配置时的来源优先级:
|
|
337
364
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
365
|
+
| 优先级 | 来源 | 说明 |
|
|
366
|
+
|---|---|---|
|
|
367
|
+
| 1 | `start_proxy(path)` | 显式路径(也接受内存中的 `Config` 对象) |
|
|
368
|
+
| 2 | `PROCPROXY_CONFIG` | 指向不存在文件会直接报错,不回退 |
|
|
369
|
+
| 3 | `~/.procproxy/config.yaml` | `procproxy init` 生成的用户配置 |
|
|
370
|
+
| 4 | 包内 `data/default.yaml` | `backend=null`,不抓包、不改道 |
|
|
371
|
+
|
|
372
|
+
内置配置**不会**监听 sink,也**不会**创建任何文件。它适合装配、规则决策、
|
|
373
|
+
状态查询和联调;要真实抓包请用 `backend="windivert"`(需管理员)或
|
|
374
|
+
`procproxy init` 生成自己的配置。
|
|
375
|
+
|
|
376
|
+
### 显式配置与测试转发
|
|
377
|
+
|
|
378
|
+
```python
|
|
379
|
+
from proxycore.api import start_proxy
|
|
380
|
+
|
|
381
|
+
# 显式路径 + 真实抓包(需要管理员)
|
|
382
|
+
with start_proxy(r"D:\cfg\procproxy.yaml", backend="windivert") as ppx:
|
|
383
|
+
print(ppx.sink) # sink 监听地址
|
|
384
|
+
print(ppx.sessions(limit=20))
|
|
385
|
+
|
|
386
|
+
# 联调模式:backend=null + 把 sink 收到的连接固定转发到目标
|
|
387
|
+
with start_proxy(backend="null", sink_test_target="127.0.0.1:8080",
|
|
388
|
+
upstream="local-mock") as ppx:
|
|
341
389
|
...
|
|
390
|
+
# curl -x http://127.0.0.1:19080 http://any/ → 127.0.0.1:8080
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### 在代码里构建配置
|
|
394
|
+
|
|
395
|
+
可以从字典构造 `Config`,也可以先落盘再按路径加载:
|
|
342
396
|
|
|
343
|
-
|
|
344
|
-
|
|
397
|
+
```python
|
|
398
|
+
from proxycore.config import Config, atomic_write
|
|
399
|
+
from proxycore.api import start_proxy
|
|
400
|
+
|
|
401
|
+
doc = {
|
|
402
|
+
"version": 1,
|
|
403
|
+
"service": {"sink": {"host": "127.0.0.1", "port": 19080}, "backend": "null"},
|
|
404
|
+
"profile": {
|
|
405
|
+
"name": "myapp",
|
|
406
|
+
"default_action": "direct",
|
|
407
|
+
"dns_mode": "remote",
|
|
408
|
+
"rules": [
|
|
409
|
+
{"id": "r1", "action": "proxy", "upstream": "office",
|
|
410
|
+
"targets": "chrome.exe;msedge.exe", "dst_port": "443,80"},
|
|
411
|
+
],
|
|
412
|
+
},
|
|
413
|
+
"upstreams": [
|
|
414
|
+
{"alias": "office", "kind": "socks5", "host": "127.0.0.1", "port": 1080},
|
|
415
|
+
],
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
cfg = Config.from_dict(doc)
|
|
419
|
+
cfg.validate() # from_dict 只解析,不校验
|
|
420
|
+
atomic_write(cfg, "myconfig.yaml") # 临时文件 → 校验 → 原子替换
|
|
421
|
+
|
|
422
|
+
with start_proxy("myconfig.yaml") as ppx:
|
|
423
|
+
...
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
> `Config.from_dict()` 不会解析 `password_ref`;需要凭据时请用
|
|
427
|
+
> `Config.load()` / `start_proxy(path)`,或自己给 `upstream.password` 赋值。
|
|
428
|
+
|
|
429
|
+
### 只做决策,不启动数据面
|
|
430
|
+
|
|
431
|
+
```python
|
|
432
|
+
from proxycore.api import make_runtime
|
|
433
|
+
|
|
434
|
+
runtime = make_runtime() # 默认用内置配置,backend=null
|
|
345
435
|
decision = runtime.decide(conn_request)
|
|
436
|
+
print(decision.action, decision.reason)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### 生命周期与句柄
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
ppx = start_proxy()
|
|
443
|
+
try:
|
|
444
|
+
print(ppx.status())
|
|
445
|
+
finally:
|
|
446
|
+
ppx.stop() # 幂等;with 退出时也会自动调用
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
| 成员 | 说明 |
|
|
450
|
+
|---|---|
|
|
451
|
+
| `status()` | 后端、规则、会话、熔断、DNS、UDP、sink、`config_path`、`config_source` |
|
|
452
|
+
| `sessions(limit=50)` / `active_sessions()` | 会话明细 / 活跃数 |
|
|
453
|
+
| `reload(path="")` | 热重载;不传路径则重读当前文件。`sink.port`/`backend` 变更需重启 |
|
|
454
|
+
| `decide(conn)` | 只问规则引擎,不抓包、不改道 |
|
|
455
|
+
| `profile` | 当前生效的 `Profile` |
|
|
456
|
+
| `config_path` / `config_source` | 当前配置的绝对路径与来源 |
|
|
457
|
+
| `running` | 是否仍在运行 |
|
|
458
|
+
|
|
459
|
+
`start_proxy()` 会启动后台维护线程;`run_forever()` 会阻塞到 Ctrl+C。
|
|
460
|
+
请在主线程使用,并在退出时 `stop()`(或使用 `with`)。
|
|
461
|
+
|
|
462
|
+
### 错误处理
|
|
463
|
+
|
|
464
|
+
配置 / 后端 / 启动失败统一抛 `ProxyStartError`,`code` 是稳定的机器可读标识:
|
|
465
|
+
|
|
466
|
+
```python
|
|
467
|
+
from proxycore.api import ProxyStartError, start_proxy
|
|
468
|
+
|
|
469
|
+
try:
|
|
470
|
+
start_proxy("missing.yaml")
|
|
471
|
+
except ProxyStartError as exc:
|
|
472
|
+
print(exc.code) # config_not_found
|
|
346
473
|
```
|
|
347
474
|
|
|
348
|
-
|
|
475
|
+
| code | 含义 |
|
|
476
|
+
|---|---|
|
|
477
|
+
| `config_not_found` | 配置文件不存在(显式路径 / 环境变量不会回退) |
|
|
478
|
+
| `config_unreadable` | 文件存在但读不了(权限、目录等) |
|
|
479
|
+
| `config_parse_invalid` | YAML 语法错误 / 根节点非映射 / 非 UTF-8 |
|
|
480
|
+
| `config_version_unsupported` | `version != 1` |
|
|
481
|
+
| `rule_upstream_missing` 等 | 规则或上游校验失败(完整列表见文档) |
|
|
482
|
+
| `E_BACKEND_UNAVAILABLE` | 后端不可用(未知后端、WinDivert 不可加载等) |
|
|
483
|
+
|
|
484
|
+
`api.load_config()` 同样抛 `ProxyStartError`,不是 `ConfigError`。
|
|
485
|
+
|
|
486
|
+
### 环境变量
|
|
487
|
+
|
|
488
|
+
| 变量 | 作用 |
|
|
489
|
+
|---|---|
|
|
490
|
+
| `PROCPROXY_CONFIG` | 配置文件路径;显式路径之后、用户配置之前生效 |
|
|
491
|
+
| `PROCPROXY_RUNTIME_DIR` | 运行时状态/日志目录(CLI 与服务使用) |
|
|
492
|
+
|
|
493
|
+
跑 `python tests/tools/api_demo.py` 看完整演示。
|
|
349
494
|
|
|
350
495
|
---
|
|
351
496
|
|
|
@@ -390,12 +535,13 @@ src/proxycore/ 核心包
|
|
|
390
535
|
├── tunnel/ sink + relay(本地落地与双向转发)
|
|
391
536
|
├── upstream/ SOCKS5 / SOCKS4 / HTTP CONNECT / 跳链
|
|
392
537
|
├── dns/ DNS 三模式与 UDP
|
|
538
|
+
├── config_source.py 配置来源解析(显式 / env / 用户 / 样例 / 内置)
|
|
393
539
|
├── forward.py ← procproxy.py <进程名> 的实现
|
|
394
540
|
├── canproxy.py / checkroute.py / probe.py / pathsetup.py ← 其余子命令
|
|
395
541
|
└── service/ ipc.py Windows 服务 + 命名管道
|
|
396
542
|
samples/ mock 服务 + 示例配置
|
|
397
|
-
docs/ 文档(00
|
|
398
|
-
tests/ 测试套件(
|
|
543
|
+
docs/ 文档(00 架构 / 07 规则 / 11 排错 / 14 抓包 / 15 SDK)
|
|
544
|
+
tests/ 测试套件(619 passed / 4 skipped)
|
|
399
545
|
└── tools/ 验证脚本(run_demo / run_e2e / bench / …)
|
|
400
546
|
```
|
|
401
547
|
|
|
@@ -426,7 +572,7 @@ tests/ 测试套件(555 passed)
|
|
|
426
572
|
|
|
427
573
|
```bash
|
|
428
574
|
pip install -e ".[dev]"
|
|
429
|
-
python -m pytest -q tests/ #
|
|
575
|
+
python -m pytest -q tests/ # 619 passed / 4 skipped(非管理员 Windows)
|
|
430
576
|
python -m ruff check src tests
|
|
431
577
|
python -m mypy src/proxycore
|
|
432
578
|
```
|
|
@@ -29,8 +29,26 @@ python procproxy.py doctor | rules | sessions | ... # 子命令(等价于 pyt
|
|
|
29
29
|
|
|
30
30
|
需要 **Python 3.10+**。
|
|
31
31
|
|
|
32
|
+
### 从 PyPI 安装(推荐)
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install procproxy
|
|
36
|
+
procproxy init # 生成 ~/.procproxy/config.yaml
|
|
37
|
+
procproxy doctor # 环境自检
|
|
38
|
+
procproxy forward WeChat.exe --port 8848 # 真实转发(需管理员)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
安装后直接运行 `procproxy` 会显示首次使用指引,不依赖任何样例文件。
|
|
42
|
+
Python SDK 也一样:`start_proxy()` 不传配置即可启动(内置 `backend=null`),
|
|
43
|
+
不需要先跑 `init`;`init` 只用于生成可编辑配置。
|
|
44
|
+
|
|
45
|
+
非管理员:WinDivert 抓包必须管理员;可以用 `--backend null` 做转发链路联调,
|
|
46
|
+
或在服务已启动、且配置了 `service.ipc_allow_group` 时读取状态。
|
|
47
|
+
|
|
48
|
+
### 源码开发安装
|
|
49
|
+
|
|
32
50
|
```bash
|
|
33
|
-
pip install -e .
|
|
51
|
+
pip install -e ".[dev]"
|
|
34
52
|
# 或者不装(零依赖方式,往 site-packages 写一个 .pth,让 python -m proxycore 全局可用)
|
|
35
53
|
python procproxy.py install-path
|
|
36
54
|
```
|
|
@@ -38,7 +56,7 @@ python procproxy.py install-path
|
|
|
38
56
|
数据面依赖(要用真抓包就必需):
|
|
39
57
|
|
|
40
58
|
```bash
|
|
41
|
-
pip install
|
|
59
|
+
pip install "procproxy[windivert]"
|
|
42
60
|
```
|
|
43
61
|
|
|
44
62
|
> `pydivert` 自带**已签名**的 `WinDivert64.sys` —— 所以**不需要**开测试签名、不需要自己签驱动。
|
|
@@ -289,24 +307,151 @@ rules: # 自上而下,**首个命中生
|
|
|
289
307
|
|
|
290
308
|
## 编程接口
|
|
291
309
|
|
|
310
|
+
`proxycore.api` 是与 CLI 共用同一套配置、规则引擎和会话表的 SDK。
|
|
311
|
+
完整教程见 [`docs/15-编程接口与SDK.md`](docs/15-编程接口与SDK.md)。
|
|
312
|
+
|
|
313
|
+
### 30 秒:零配置启动
|
|
314
|
+
|
|
292
315
|
```python
|
|
293
|
-
from proxycore import
|
|
316
|
+
from proxycore.api import start_proxy
|
|
294
317
|
|
|
295
|
-
#
|
|
296
|
-
|
|
297
|
-
print(
|
|
318
|
+
with start_proxy() as ppx: # 不需要任何配置文件
|
|
319
|
+
print(ppx.config_source) # "packaged"
|
|
320
|
+
print(ppx.config_path) # .../proxycore/data/default.yaml
|
|
321
|
+
print(ppx.status()) # 后端 / 规则 / 会话 / UDP / 配置来源
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
不传配置时的来源优先级:
|
|
298
325
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
326
|
+
| 优先级 | 来源 | 说明 |
|
|
327
|
+
|---|---|---|
|
|
328
|
+
| 1 | `start_proxy(path)` | 显式路径(也接受内存中的 `Config` 对象) |
|
|
329
|
+
| 2 | `PROCPROXY_CONFIG` | 指向不存在文件会直接报错,不回退 |
|
|
330
|
+
| 3 | `~/.procproxy/config.yaml` | `procproxy init` 生成的用户配置 |
|
|
331
|
+
| 4 | 包内 `data/default.yaml` | `backend=null`,不抓包、不改道 |
|
|
332
|
+
|
|
333
|
+
内置配置**不会**监听 sink,也**不会**创建任何文件。它适合装配、规则决策、
|
|
334
|
+
状态查询和联调;要真实抓包请用 `backend="windivert"`(需管理员)或
|
|
335
|
+
`procproxy init` 生成自己的配置。
|
|
336
|
+
|
|
337
|
+
### 显式配置与测试转发
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
from proxycore.api import start_proxy
|
|
341
|
+
|
|
342
|
+
# 显式路径 + 真实抓包(需要管理员)
|
|
343
|
+
with start_proxy(r"D:\cfg\procproxy.yaml", backend="windivert") as ppx:
|
|
344
|
+
print(ppx.sink) # sink 监听地址
|
|
345
|
+
print(ppx.sessions(limit=20))
|
|
346
|
+
|
|
347
|
+
# 联调模式:backend=null + 把 sink 收到的连接固定转发到目标
|
|
348
|
+
with start_proxy(backend="null", sink_test_target="127.0.0.1:8080",
|
|
349
|
+
upstream="local-mock") as ppx:
|
|
302
350
|
...
|
|
351
|
+
# curl -x http://127.0.0.1:19080 http://any/ → 127.0.0.1:8080
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 在代码里构建配置
|
|
355
|
+
|
|
356
|
+
可以从字典构造 `Config`,也可以先落盘再按路径加载:
|
|
303
357
|
|
|
304
|
-
|
|
305
|
-
|
|
358
|
+
```python
|
|
359
|
+
from proxycore.config import Config, atomic_write
|
|
360
|
+
from proxycore.api import start_proxy
|
|
361
|
+
|
|
362
|
+
doc = {
|
|
363
|
+
"version": 1,
|
|
364
|
+
"service": {"sink": {"host": "127.0.0.1", "port": 19080}, "backend": "null"},
|
|
365
|
+
"profile": {
|
|
366
|
+
"name": "myapp",
|
|
367
|
+
"default_action": "direct",
|
|
368
|
+
"dns_mode": "remote",
|
|
369
|
+
"rules": [
|
|
370
|
+
{"id": "r1", "action": "proxy", "upstream": "office",
|
|
371
|
+
"targets": "chrome.exe;msedge.exe", "dst_port": "443,80"},
|
|
372
|
+
],
|
|
373
|
+
},
|
|
374
|
+
"upstreams": [
|
|
375
|
+
{"alias": "office", "kind": "socks5", "host": "127.0.0.1", "port": 1080},
|
|
376
|
+
],
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
cfg = Config.from_dict(doc)
|
|
380
|
+
cfg.validate() # from_dict 只解析,不校验
|
|
381
|
+
atomic_write(cfg, "myconfig.yaml") # 临时文件 → 校验 → 原子替换
|
|
382
|
+
|
|
383
|
+
with start_proxy("myconfig.yaml") as ppx:
|
|
384
|
+
...
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
> `Config.from_dict()` 不会解析 `password_ref`;需要凭据时请用
|
|
388
|
+
> `Config.load()` / `start_proxy(path)`,或自己给 `upstream.password` 赋值。
|
|
389
|
+
|
|
390
|
+
### 只做决策,不启动数据面
|
|
391
|
+
|
|
392
|
+
```python
|
|
393
|
+
from proxycore.api import make_runtime
|
|
394
|
+
|
|
395
|
+
runtime = make_runtime() # 默认用内置配置,backend=null
|
|
306
396
|
decision = runtime.decide(conn_request)
|
|
397
|
+
print(decision.action, decision.reason)
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 生命周期与句柄
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
ppx = start_proxy()
|
|
404
|
+
try:
|
|
405
|
+
print(ppx.status())
|
|
406
|
+
finally:
|
|
407
|
+
ppx.stop() # 幂等;with 退出时也会自动调用
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
| 成员 | 说明 |
|
|
411
|
+
|---|---|
|
|
412
|
+
| `status()` | 后端、规则、会话、熔断、DNS、UDP、sink、`config_path`、`config_source` |
|
|
413
|
+
| `sessions(limit=50)` / `active_sessions()` | 会话明细 / 活跃数 |
|
|
414
|
+
| `reload(path="")` | 热重载;不传路径则重读当前文件。`sink.port`/`backend` 变更需重启 |
|
|
415
|
+
| `decide(conn)` | 只问规则引擎,不抓包、不改道 |
|
|
416
|
+
| `profile` | 当前生效的 `Profile` |
|
|
417
|
+
| `config_path` / `config_source` | 当前配置的绝对路径与来源 |
|
|
418
|
+
| `running` | 是否仍在运行 |
|
|
419
|
+
|
|
420
|
+
`start_proxy()` 会启动后台维护线程;`run_forever()` 会阻塞到 Ctrl+C。
|
|
421
|
+
请在主线程使用,并在退出时 `stop()`(或使用 `with`)。
|
|
422
|
+
|
|
423
|
+
### 错误处理
|
|
424
|
+
|
|
425
|
+
配置 / 后端 / 启动失败统一抛 `ProxyStartError`,`code` 是稳定的机器可读标识:
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
from proxycore.api import ProxyStartError, start_proxy
|
|
429
|
+
|
|
430
|
+
try:
|
|
431
|
+
start_proxy("missing.yaml")
|
|
432
|
+
except ProxyStartError as exc:
|
|
433
|
+
print(exc.code) # config_not_found
|
|
307
434
|
```
|
|
308
435
|
|
|
309
|
-
|
|
436
|
+
| code | 含义 |
|
|
437
|
+
|---|---|
|
|
438
|
+
| `config_not_found` | 配置文件不存在(显式路径 / 环境变量不会回退) |
|
|
439
|
+
| `config_unreadable` | 文件存在但读不了(权限、目录等) |
|
|
440
|
+
| `config_parse_invalid` | YAML 语法错误 / 根节点非映射 / 非 UTF-8 |
|
|
441
|
+
| `config_version_unsupported` | `version != 1` |
|
|
442
|
+
| `rule_upstream_missing` 等 | 规则或上游校验失败(完整列表见文档) |
|
|
443
|
+
| `E_BACKEND_UNAVAILABLE` | 后端不可用(未知后端、WinDivert 不可加载等) |
|
|
444
|
+
|
|
445
|
+
`api.load_config()` 同样抛 `ProxyStartError`,不是 `ConfigError`。
|
|
446
|
+
|
|
447
|
+
### 环境变量
|
|
448
|
+
|
|
449
|
+
| 变量 | 作用 |
|
|
450
|
+
|---|---|
|
|
451
|
+
| `PROCPROXY_CONFIG` | 配置文件路径;显式路径之后、用户配置之前生效 |
|
|
452
|
+
| `PROCPROXY_RUNTIME_DIR` | 运行时状态/日志目录(CLI 与服务使用) |
|
|
453
|
+
|
|
454
|
+
跑 `python tests/tools/api_demo.py` 看完整演示。
|
|
310
455
|
|
|
311
456
|
---
|
|
312
457
|
|
|
@@ -351,12 +496,13 @@ src/proxycore/ 核心包
|
|
|
351
496
|
├── tunnel/ sink + relay(本地落地与双向转发)
|
|
352
497
|
├── upstream/ SOCKS5 / SOCKS4 / HTTP CONNECT / 跳链
|
|
353
498
|
├── dns/ DNS 三模式与 UDP
|
|
499
|
+
├── config_source.py 配置来源解析(显式 / env / 用户 / 样例 / 内置)
|
|
354
500
|
├── forward.py ← procproxy.py <进程名> 的实现
|
|
355
501
|
├── canproxy.py / checkroute.py / probe.py / pathsetup.py ← 其余子命令
|
|
356
502
|
└── service/ ipc.py Windows 服务 + 命名管道
|
|
357
503
|
samples/ mock 服务 + 示例配置
|
|
358
|
-
docs/ 文档(00
|
|
359
|
-
tests/ 测试套件(
|
|
504
|
+
docs/ 文档(00 架构 / 07 规则 / 11 排错 / 14 抓包 / 15 SDK)
|
|
505
|
+
tests/ 测试套件(619 passed / 4 skipped)
|
|
360
506
|
└── tools/ 验证脚本(run_demo / run_e2e / bench / …)
|
|
361
507
|
```
|
|
362
508
|
|
|
@@ -387,7 +533,7 @@ tests/ 测试套件(555 passed)
|
|
|
387
533
|
|
|
388
534
|
```bash
|
|
389
535
|
pip install -e ".[dev]"
|
|
390
|
-
python -m pytest -q tests/ #
|
|
536
|
+
python -m pytest -q tests/ # 619 passed / 4 skipped(非管理员 Windows)
|
|
391
537
|
python -m ruff check src tests
|
|
392
538
|
python -m mypy src/proxycore
|
|
393
539
|
```
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "procproxy"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "ProcProxy —— 进程级代理:按进程决定哪条连接走哪个上游代理"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -51,6 +51,9 @@ package-dir = { "" = "src" }
|
|
|
51
51
|
[tool.setuptools.packages.find]
|
|
52
52
|
where = ["src"]
|
|
53
53
|
|
|
54
|
+
[tool.setuptools.package-data]
|
|
55
|
+
"proxycore" = ["data/*.yaml"]
|
|
56
|
+
|
|
54
57
|
# ---------------------------------------------------------------- pytest ----
|
|
55
58
|
[tool.pytest.ini_options]
|
|
56
59
|
minversion = "7.4"
|