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
|
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(
|
def _relay_request(
|
||||||
url: str, method: str, headers: dict[str, str], body: bytes
|
url: str, method: str, headers: dict[str, str], body: bytes
|
||||||
) -> dict:
|
) -> dict:
|
||||||
@@ -169,23 +195,16 @@ def _relay_request(
|
|||||||
try:
|
try:
|
||||||
with _NO_REDIRECT_OPENER.open(request, timeout=_OUTBOUND_TIMEOUT) as resp:
|
with _NO_REDIRECT_OPENER.open(request, timeout=_OUTBOUND_TIMEOUT) as resp:
|
||||||
data = resp.read(_MAX_RESPONSE_BODY)
|
data = resp.read(_MAX_RESPONSE_BODY)
|
||||||
resp_headers: dict[str, str] = {}
|
|
||||||
for k, v in resp.headers.items():
|
|
||||||
resp_headers[k] = v
|
|
||||||
return {
|
return {
|
||||||
"s": resp.status,
|
"s": resp.status,
|
||||||
"h": resp_headers,
|
"h": _collect_headers(resp.headers),
|
||||||
"b": base64.b64encode(data).decode(),
|
"b": base64.b64encode(data).decode(),
|
||||||
}
|
}
|
||||||
except urllib.error.HTTPError as exc:
|
except urllib.error.HTTPError as exc:
|
||||||
data = exc.read(_MAX_RESPONSE_BODY) if exc.fp else b""
|
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 {
|
return {
|
||||||
"s": exc.code,
|
"s": exc.code,
|
||||||
"h": resp_headers,
|
"h": _collect_headers(exc.headers) if exc.headers else {},
|
||||||
"b": base64.b64encode(data).decode(),
|
"b": base64.b64encode(data).decode(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1392,12 +1392,8 @@ class DomainFronter:
|
|||||||
# Script quota usage. _relay_with_retry bypasses batching entirely.
|
# Script quota usage. _relay_with_retry bypasses batching entirely.
|
||||||
raw = await self._batch_submit(outer)
|
raw = await self._batch_submit(outer)
|
||||||
|
|
||||||
# raw is now the response from the exit node (inner relay JSON)
|
_, _, vps_relay_bytes = split_raw_response(raw)
|
||||||
# _parse_relay_response will decode it into the final HTTP response.
|
result = parse_relay_response(vps_relay_bytes, self._max_response_body_bytes)
|
||||||
# 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)
|
|
||||||
log.debug("Exit node relay OK: %s", payload.get("u", "")[:80])
|
log.debug("Exit node relay OK: %s", payload.get("u", "")[:80])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,10 @@ async def read_http_response(
|
|||||||
while b"\r\n\r\n" not in raw:
|
while b"\r\n\r\n" not in raw:
|
||||||
if len(raw) > 65536: # 64 KB header size limit
|
if len(raw) > 65536: # 64 KB header size limit
|
||||||
return 0, {}, b""
|
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:
|
if not chunk:
|
||||||
break
|
break
|
||||||
raw += chunk
|
raw += chunk
|
||||||
|
|||||||
Reference in New Issue
Block a user