重构:模块化 RRPP Lite 并更新入口
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user