重构:模块化 RRPP Lite 并更新入口

This commit is contained in:
RoyirinRosmontis
2026-09-16 23:13:28 +08:00
parent c37d8af821
commit 02d36c72f7
35 changed files with 2286 additions and 1079 deletions
+91
View File
@@ -0,0 +1,91 @@
"""本地转发器的无状态报文解析与改写函数。"""
from urllib.parse import urlsplit
HTTP_METHODS = (
b"CONNECT ", b"GET ", b"POST ", b"PUT ", b"DELETE ",
b"HEAD ", b"OPTIONS ", b"PATCH ",
)
def starts_with_http_method(data: bytes) -> bool:
return any(data.startswith(method) for method in HTTP_METHODS)
def inject_proxy_auth(data: bytes, basic_auth: str) -> bytes:
"""为完整 HTTP 代理请求注入上游 Basic Authorization。"""
if not starts_with_http_method(data):
return data
end = data.find(b"\r\n\r\n")
if end == -1:
return data
head = data[:end]
first_end = head.find(b"\r\n")
request_line = head if first_end == -1 else head[:first_end]
headers = b"" if first_end == -1 else head[first_end + 2:]
for line in headers.split(b"\r\n"):
if b":" not in line:
continue
name = line[:line.index(b":")].strip().lower()
if name == b"authorization":
return data
auth_line = f"Authorization: Basic {basic_auth}\r\n".encode()
return request_line + b"\r\n" + auth_line + headers + data[end:]
def parse_connect_authority(first_line: str):
"""从 CONNECT 首行提取 ``(host, port)``,失败时返回 ``None``。"""
parts = first_line.split(" ")
if len(parts) < 2:
return None
authority = parts[1]
if authority.startswith("[") and "]" in authority:
try:
host = authority[1:authority.index("]")]
tail = authority[authority.index("]") + 1:]
if tail.startswith(":") and tail[1:].isdigit():
return host, int(tail[1:])
except ValueError:
pass
if ":" not in authority:
return None
host, _, port_s = authority.rpartition(":")
if not host or not port_s.isdigit():
return None
return host, int(port_s)
def parse_http_absolute(first_line: str):
"""从明文 HTTP 代理首行提取 ``(host, port)``,失败时返回 ``None``。"""
parts = first_line.split(" ")
if len(parts) < 2 or not parts[1].lower().startswith(("http://", "https://")):
return None
parsed = urlsplit(parts[1])
if not parsed.hostname:
return None
port = parsed.port or (443 if parsed.scheme == "https" else 80)
return parsed.hostname, port
def force_http_close(buffer: bytes) -> bytes:
"""直连明文 HTTP 时强制 Connection: close,避免错复用连接。"""
end = buffer.find(b"\r\n\r\n")
if end == -1:
return buffer
head, tail = buffer[:end], buffer[end:]
lines = head.split(b"\r\n")
out = [lines[0]]
conn_seen = False
for line in lines[1:]:
low = line.lower()
if low.startswith(b"connection:"):
out.append(b"Connection: close")
conn_seen = True
elif low.startswith(b"proxy-connection:"):
continue
else:
out.append(line)
if not conn_seen:
out.append(b"Connection: close")
return b"\r\n".join(out) + tail