Skip to content

Commit d56ddc6

Browse files
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.
1 parent e362638 commit d56ddc6

2 files changed

Lines changed: 26 additions & 11 deletions

File tree

assets/apps_script/Code.gs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,15 @@ function _doSingle(req) {
202202
try {
203203
var opts = _buildOpts(req);
204204
var resp = UrlFetchApp.fetch(req.u, opts);
205+
206+
// Raw-return mode for exit-node path.
207+
// r:true = return destination body verbatim so Rust gets {s,h,b} unwrapped.
208+
if (req.r === true) {
209+
return ContentService
210+
.createTextOutput(resp.getContentText())
211+
.setMimeType(ContentService.MimeType.JSON);
212+
}
213+
205214
return _json({
206215
s: resp.getResponseCode(),
207216
h: _respHeaders(resp),
@@ -307,7 +316,7 @@ function _buildOpts(req) {
307316
var opts = {
308317
method: (req.m || "GET").toLowerCase(),
309318
muteHttpExceptions: true,
310-
followRedirects: req.r !== false,
319+
followRedirects: true, // ← always true; r flag now has different meaning
311320
validateHttpsCertificates: true,
312321
escaping: false,
313322
};

src/domain_fronter.rs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,10 +2685,9 @@ impl DomainFronter {
26852685
let app_body = self
26862686
.send_prebuilt_payload_through_relay(outer_payload)
26872687
.await?;
2688-
2689-
// exit-node's JSON envelope: {s: u16, h: {...}, b: "<base64>"} on
2690-
// success, {e: "..."} on its own internal error.
2691-
parse_exit_node_response(&app_body)
2688+
2689+
let result = parse_exit_node_response(&app_body);
2690+
result
26922691
}
26932692

26942693
/// Build the inner-layer payload that the exit node will execute.
@@ -3031,7 +3030,7 @@ impl DomainFronter {
30313030
let start = text.find('{').ok_or_else(|| {
30323031
FronterError::BadResponse(format!(
30333032
"no json in tunnel response: {}",
3034-
&text[..text.len().min(200)]
3033+
&text.chars().take(200).collect::<String>()
30353034
))
30363035
})?;
30373036
let end = text.rfind('}').ok_or_else(|| {
@@ -3199,7 +3198,7 @@ impl DomainFronter {
31993198
let start = text.find('{').ok_or_else(|| {
32003199
FronterError::BadResponse(format!(
32013200
"no json in batch response: {}",
3202-
&text[..text.len().min(200)]
3201+
&text.chars().take(200).collect::<String>()
32033202
))
32043203
})?;
32053204
let end = text.rfind('}').ok_or_else(|| {
@@ -3961,11 +3960,17 @@ fn unix_to_ymd_utc(secs: u64) -> (i64, u32, u32) {
39613960
/// MITM TLS write-back path sees the same shape it gets from the regular
39623961
/// Apps Script relay (status line + headers + body).
39633962
fn parse_exit_node_response(body: &[u8]) -> Result<Vec<u8>, FronterError> {
3964-
let v: Value = serde_json::from_slice(body).map_err(|e| {
3963+
let json_start = body
3964+
.windows(4)
3965+
.position(|w| w == b"\r\n\r\n")
3966+
.map(|i| i + 4)
3967+
.unwrap_or(0);
3968+
let json_bytes = &body[json_start..];
3969+
let v: Value = serde_json::from_slice(json_bytes).map_err(|e| {
39653970
FronterError::Relay(format!(
39663971
"exit-node response not valid JSON ({}): {}",
39673972
e,
3968-
String::from_utf8_lossy(&body[..body.len().min(200)])
3973+
String::from_utf8_lossy(&json_bytes[..json_bytes.len().min(200)])
39693974
))
39703975
})?;
39713976

@@ -4001,6 +4006,7 @@ fn parse_exit_node_response(body: &[u8]) -> Result<Vec<u8>, FronterError> {
40014006
"transfer-encoding",
40024007
"connection",
40034008
"keep-alive",
4009+
"content-encoding", // exit node's fetch() auto-decompresses; header is stale
40044010
];
40054011

40064012
let mut out = Vec::with_capacity(body_bytes.len() + 256);
@@ -4565,13 +4571,13 @@ fn parse_relay_json(body: &[u8]) -> Result<Vec<u8>, FronterError> {
45654571
let start = text.find('{').ok_or_else(|| {
45664572
FronterError::BadResponse(format!(
45674573
"no json in: {}",
4568-
&text[..text.len().min(200)]
4574+
&text.chars().take(200).collect::<String>()
45694575
))
45704576
})?;
45714577
let end = text.rfind('}').ok_or_else(|| {
45724578
FronterError::BadResponse(format!(
45734579
"no json end in: {}",
4574-
&text[..text.len().min(200)]
4580+
&text.chars().take(200).collect::<String>()
45754581
))
45764582
})?;
45774583
serde_json::from_str(&text[start..=end])?

0 commit comments

Comments
 (0)