fixed youtube on relay & chatgpt login on VPS exit node

This commit is contained in:
Abolfazl
2026-05-07 21:49:34 +03:30
parent cb4ae98bee
commit b4e23486d1
3 changed files with 34 additions and 16 deletions
+28 -9
View File
@@ -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(),
}
+2 -6
View File
@@ -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
+4 -1
View File
@@ -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