mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-17 21:24:48 +03:00
fix(exit-node): preserve raw exit-node responses
Return raw exit-node responses from Apps Script when requested and strip stale exit-node content-encoding headers after server-side decompression.
This commit is contained in:
@@ -202,6 +202,15 @@ function _doSingle(req) {
|
|||||||
try {
|
try {
|
||||||
var opts = _buildOpts(req);
|
var opts = _buildOpts(req);
|
||||||
var resp = UrlFetchApp.fetch(req.u, opts);
|
var resp = UrlFetchApp.fetch(req.u, opts);
|
||||||
|
|
||||||
|
// Raw-return mode for exit-node path.
|
||||||
|
// r:true = return destination body verbatim so Rust gets {s,h,b} unwrapped.
|
||||||
|
if (req.r === true) {
|
||||||
|
return ContentService
|
||||||
|
.createTextOutput(resp.getContentText())
|
||||||
|
.setMimeType(ContentService.MimeType.JSON);
|
||||||
|
}
|
||||||
|
|
||||||
return _json({
|
return _json({
|
||||||
s: resp.getResponseCode(),
|
s: resp.getResponseCode(),
|
||||||
h: _respHeaders(resp),
|
h: _respHeaders(resp),
|
||||||
@@ -307,7 +316,7 @@ function _buildOpts(req) {
|
|||||||
var opts = {
|
var opts = {
|
||||||
method: (req.m || "GET").toLowerCase(),
|
method: (req.m || "GET").toLowerCase(),
|
||||||
muteHttpExceptions: true,
|
muteHttpExceptions: true,
|
||||||
followRedirects: req.r !== false,
|
followRedirects: true, // ← always true; r flag now has different meaning
|
||||||
validateHttpsCertificates: true,
|
validateHttpsCertificates: true,
|
||||||
escaping: false,
|
escaping: false,
|
||||||
};
|
};
|
||||||
|
|||||||
+16
-10
@@ -2685,10 +2685,9 @@ impl DomainFronter {
|
|||||||
let app_body = self
|
let app_body = self
|
||||||
.send_prebuilt_payload_through_relay(outer_payload)
|
.send_prebuilt_payload_through_relay(outer_payload)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// exit-node's JSON envelope: {s: u16, h: {...}, b: "<base64>"} on
|
let result = parse_exit_node_response(&app_body);
|
||||||
// success, {e: "..."} on its own internal error.
|
result
|
||||||
parse_exit_node_response(&app_body)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the inner-layer payload that the exit node will execute.
|
/// Build the inner-layer payload that the exit node will execute.
|
||||||
@@ -3031,7 +3030,7 @@ impl DomainFronter {
|
|||||||
let start = text.find('{').ok_or_else(|| {
|
let start = text.find('{').ok_or_else(|| {
|
||||||
FronterError::BadResponse(format!(
|
FronterError::BadResponse(format!(
|
||||||
"no json in tunnel response: {}",
|
"no json in tunnel response: {}",
|
||||||
&text[..text.len().min(200)]
|
&text.chars().take(200).collect::<String>()
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let end = text.rfind('}').ok_or_else(|| {
|
let end = text.rfind('}').ok_or_else(|| {
|
||||||
@@ -3199,7 +3198,7 @@ impl DomainFronter {
|
|||||||
let start = text.find('{').ok_or_else(|| {
|
let start = text.find('{').ok_or_else(|| {
|
||||||
FronterError::BadResponse(format!(
|
FronterError::BadResponse(format!(
|
||||||
"no json in batch response: {}",
|
"no json in batch response: {}",
|
||||||
&text[..text.len().min(200)]
|
&text.chars().take(200).collect::<String>()
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let end = text.rfind('}').ok_or_else(|| {
|
let end = text.rfind('}').ok_or_else(|| {
|
||||||
@@ -3961,11 +3960,17 @@ fn unix_to_ymd_utc(secs: u64) -> (i64, u32, u32) {
|
|||||||
/// MITM TLS write-back path sees the same shape it gets from the regular
|
/// MITM TLS write-back path sees the same shape it gets from the regular
|
||||||
/// Apps Script relay (status line + headers + body).
|
/// Apps Script relay (status line + headers + body).
|
||||||
fn parse_exit_node_response(body: &[u8]) -> Result<Vec<u8>, FronterError> {
|
fn parse_exit_node_response(body: &[u8]) -> Result<Vec<u8>, FronterError> {
|
||||||
let v: Value = serde_json::from_slice(body).map_err(|e| {
|
let json_start = body
|
||||||
|
.windows(4)
|
||||||
|
.position(|w| w == b"\r\n\r\n")
|
||||||
|
.map(|i| i + 4)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let json_bytes = &body[json_start..];
|
||||||
|
let v: Value = serde_json::from_slice(json_bytes).map_err(|e| {
|
||||||
FronterError::Relay(format!(
|
FronterError::Relay(format!(
|
||||||
"exit-node response not valid JSON ({}): {}",
|
"exit-node response not valid JSON ({}): {}",
|
||||||
e,
|
e,
|
||||||
String::from_utf8_lossy(&body[..body.len().min(200)])
|
String::from_utf8_lossy(&json_bytes[..json_bytes.len().min(200)])
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -4001,6 +4006,7 @@ fn parse_exit_node_response(body: &[u8]) -> Result<Vec<u8>, FronterError> {
|
|||||||
"transfer-encoding",
|
"transfer-encoding",
|
||||||
"connection",
|
"connection",
|
||||||
"keep-alive",
|
"keep-alive",
|
||||||
|
"content-encoding", // exit node's fetch() auto-decompresses; header is stale
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut out = Vec::with_capacity(body_bytes.len() + 256);
|
let mut out = Vec::with_capacity(body_bytes.len() + 256);
|
||||||
@@ -4565,13 +4571,13 @@ fn parse_relay_json(body: &[u8]) -> Result<Vec<u8>, FronterError> {
|
|||||||
let start = text.find('{').ok_or_else(|| {
|
let start = text.find('{').ok_or_else(|| {
|
||||||
FronterError::BadResponse(format!(
|
FronterError::BadResponse(format!(
|
||||||
"no json in: {}",
|
"no json in: {}",
|
||||||
&text[..text.len().min(200)]
|
&text.chars().take(200).collect::<String>()
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
let end = text.rfind('}').ok_or_else(|| {
|
let end = text.rfind('}').ok_or_else(|| {
|
||||||
FronterError::BadResponse(format!(
|
FronterError::BadResponse(format!(
|
||||||
"no json end in: {}",
|
"no json end in: {}",
|
||||||
&text[..text.len().min(200)]
|
&text.chars().take(200).collect::<String>()
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
serde_json::from_str(&text[start..=end])?
|
serde_json::from_str(&text[start..=end])?
|
||||||
|
|||||||
Reference in New Issue
Block a user