Files
jock 3bf0b1aaa2 fix: 改善启动失败反馈、Ctrl+C 立退与 Windows 兼容性
- main: Ctrl+C 时直接 SIGKILL 所有 daemon 子进程,跳过 mitmproxy graceful shutdown
  与 mitmdump 重启循环里的 time.sleep(3),实现秒退
- main: 启动失败时透传子进程真实错误行(端口/CA 缺失/文件权限等),告别"请切换端口后重试"
  一刀切提示;Windows 下等用户按回车再退出,防止 PowerShell/双击启动窗口闪退
- mitm: start() 返回 (proxy_address, error_line);mitmdump 重启循环处理 KeyboardInterrupt
- watcher: start() 返回 (ws_address, queue, error_line);将 wss 证书生成失败纳入快速失败识别
- utils: check_system_proxy 修复 KeyError — Windows 下 getproxies() 可能返回仅含 'no' 等
  非 http/https key 的字典,旧写法 proxy_obj.keys() < {'http', 'https'} 是真子集判断,
  会漏掉这些场景继续访问 proxy_obj['http'] 引发崩溃
- ui/startup: 证书安装命令里 %userprofile% 在 PowerShell 下不会展开导致
  certutil 报 ERROR_PATH_NOT_FOUND;改为 Python 侧展开为绝对路径,cmd/PowerShell/bash/zsh
  均可直接粘贴执行,同时用双引号包住路径兼容带空格的用户名
2026-04-20 13:53:45 +08:00

77 lines
2.5 KiB
Python

import multiprocessing
import os
import queue
import re
import sys
import threading
import time
from pathlib import Path
from mitmproxy.tools.main import mitmdump
import utils
from logger import logger
SRC_PATH = Path.absolute(Path(__file__)).parent
PLUGIN_FILE = str(SRC_PATH / 'resources' / 'credential.py')
CREDENTIALS_FILE = str(SRC_PATH / 'resources' / 'data' / 'credentials.json')
def mitmproxy_process(args: list[str], output_queue: multiprocessing.Queue):
sys.stdout = sys.stderr = utils.Capture(output_queue)
# 清除继承自父进程/用户 shell 的代理环境变量,防止 mitmproxy 把自己当作上游代理导致死循环
for k in ('HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'http_proxy', 'https_proxy', 'all_proxy'):
os.environ.pop(k, None)
while True:
print(f'Run mitmdump process {args} ({os.getpid()})...', flush=True)
try:
mitmdump(args)
except KeyboardInterrupt:
# mitmdump 通常会吞掉 SIGINT 然后正常 return;这里是为了处理它透传上来的极少数情况
break
logger.info(f'mitmdump process terminated')
time.sleep(3)
logger.info(f'重启 mitm 进程')
def start(port: str, debug = False):
# 启动 mitmproxy 并加载 credentials 插件
args = ['-p', port, '-s', PLUGIN_FILE, '--set', 'credentials='+CREDENTIALS_FILE]
mitm_output_queue = multiprocessing.Queue()
mitm_process = multiprocessing.Process(target=mitmproxy_process, args=(args, mitm_output_queue), daemon=True)
mitm_process.start()
start_time = time.time()
proxy_address = None
error_line = None
while time.time() - start_time < 10:
try:
line = mitm_output_queue.get(timeout=0.1)
logger.info(line)
if "HTTP(S) proxy listening at" in line:
match = re.search(r'\*:(\d+)', line)
port = match.group(1)
proxy_address = f"http://127.0.0.1:{port}"
break
elif "address already in use" in line.lower():
error_line = line
break
except queue.Empty:
pass
threading.Thread(target=log_mitmproxy_output, args=(mitm_output_queue, debug), daemon=True).start()
return proxy_address, error_line
def log_mitmproxy_output(mitm_output_queue: multiprocessing.Queue, debug):
while True:
line = mitm_output_queue.get()
if debug:
logger.debug(line)
else:
# 忽略 mitmproxy 的日志
pass