mirror of
https://github.com/masterking32/MasterHttpRelayVPN.git
synced 2026-05-17 21:24:37 +03:00
fixed youtube on relay & chatgpt login on VPS exit node
This commit is contained in:
@@ -158,6 +158,32 @@ def _safe_url(url: str) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def _collect_headers(raw_headers) -> dict:
|
||||
"""Collect HTTP response headers, preserving all values for duplicate names.
|
||||
|
||||
Python's http.client.HTTPMessage yields duplicate header names (e.g. multiple
|
||||
Set-Cookie lines) as separate items when iterated. A plain dict assignment
|
||||
silently overwrites earlier values, so sites like auth.openai.com that set
|
||||
several Set-Cookie headers in one response would lose all but the last one.
|
||||
Accumulate duplicates into a list so every value reaches the browser.
|
||||
"""
|
||||
out: dict = {}
|
||||
key_map: dict[str, str] = {} # lowercase name → first-seen canonical case
|
||||
for k, v in raw_headers.items():
|
||||
kl = k.lower()
|
||||
if kl not in key_map:
|
||||
key_map[kl] = k
|
||||
out[k] = v
|
||||
else:
|
||||
canonical = key_map[kl]
|
||||
cur = out[canonical]
|
||||
if isinstance(cur, list):
|
||||
cur.append(v)
|
||||
else:
|
||||
out[canonical] = [cur, v]
|
||||
return out
|
||||
|
||||
|
||||
def _relay_request(
|
||||
url: str, method: str, headers: dict[str, str], body: bytes
|
||||
) -> dict:
|
||||
@@ -169,23 +195,16 @@ def _relay_request(
|
||||
try:
|
||||
with _NO_REDIRECT_OPENER.open(request, timeout=_OUTBOUND_TIMEOUT) as resp:
|
||||
data = resp.read(_MAX_RESPONSE_BODY)
|
||||
resp_headers: dict[str, str] = {}
|
||||
for k, v in resp.headers.items():
|
||||
resp_headers[k] = v
|
||||
return {
|
||||
"s": resp.status,
|
||||
"h": resp_headers,
|
||||
"h": _collect_headers(resp.headers),
|
||||
"b": base64.b64encode(data).decode(),
|
||||
}
|
||||
except urllib.error.HTTPError as exc:
|
||||
data = exc.read(_MAX_RESPONSE_BODY) if exc.fp else b""
|
||||
resp_headers = {}
|
||||
if exc.headers:
|
||||
for k, v in exc.headers.items():
|
||||
resp_headers[k] = v
|
||||
return {
|
||||
"s": exc.code,
|
||||
"h": resp_headers,
|
||||
"h": _collect_headers(exc.headers) if exc.headers else {},
|
||||
"b": base64.b64encode(data).decode(),
|
||||
}
|
||||
|
||||
|
||||
@@ -1392,12 +1392,8 @@ class DomainFronter:
|
||||
# Script quota usage. _relay_with_retry bypasses batching entirely.
|
||||
raw = await self._batch_submit(outer)
|
||||
|
||||
# raw is now the response from the exit node (inner relay JSON)
|
||||
# _parse_relay_response will decode it into the final HTTP response.
|
||||
# But we need to unwrap one level: Apps Script gives us exit node HTTP
|
||||
# response body (which is itself a relay JSON), so parse twice.
|
||||
_, _, apps_script_body = split_raw_response(raw)
|
||||
result = parse_relay_response(apps_script_body, self._max_response_body_bytes)
|
||||
_, _, vps_relay_bytes = split_raw_response(raw)
|
||||
result = parse_relay_response(vps_relay_bytes, self._max_response_body_bytes)
|
||||
log.debug("Exit node relay OK: %s", payload.get("u", "")[:80])
|
||||
return result
|
||||
|
||||
|
||||
@@ -43,7 +43,10 @@ async def read_http_response(
|
||||
while b"\r\n\r\n" not in raw:
|
||||
if len(raw) > 65536: # 64 KB header size limit
|
||||
return 0, {}, b""
|
||||
chunk = await asyncio.wait_for(reader.read(8192), timeout=8)
|
||||
# 30s per-read: exit-node chain (Apps Script → VPS → target) needs
|
||||
# time to fetch + process large responses before sending headers.
|
||||
# The outer asyncio.wait_for in _relay_single caps total time.
|
||||
chunk = await asyncio.wait_for(reader.read(8192), timeout=30)
|
||||
if not chunk:
|
||||
break
|
||||
raw += chunk
|
||||
|
||||
Reference in New Issue
Block a user