Skip to content

Commit 787599e

Browse files
committed
Log malformed grpc-web response body previews
1 parent 11e180e commit 787599e

1 file changed

Lines changed: 32 additions & 8 deletions

File tree

libsql/src/replication/client.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -231,25 +231,49 @@ where
231231
.to_string();
232232
let is_grpc = content_type.contains("grpc");
233233

234-
if status != http::StatusCode::OK || !is_grpc {
235-
// Buffer the body to log it, then re-create the response
236-
let (parts, body) = resp.into_parts();
237-
let body_bytes = hyper::body::to_bytes(body).await.unwrap_or_default();
234+
// Diagnostic branch: buffer every response before tonic-web parses
235+
// grpc-web frames, then re-create the body for normal consumption.
236+
// This lets us catch malformed 200 OK grpc-looking responses whose
237+
// first frame byte would otherwise only surface as "Invalid header bit".
238+
let (parts, body) = resp.into_parts();
239+
let body_bytes = hyper::body::to_bytes(body).await.unwrap_or_default();
240+
let first_body_byte = body_bytes.first().copied();
241+
let first_body_byte_hex = first_body_byte.map(|byte| format!("0x{byte:02x}"));
242+
let first_body_byte_ascii = first_body_byte.map(|byte| {
243+
if byte.is_ascii_graphic() || byte == b' ' {
244+
(byte as char).to_string()
245+
} else {
246+
format!("\\x{byte:02x}")
247+
}
248+
});
249+
let valid_grpc_web_first_byte = matches!(first_body_byte, None | Some(0 | 1 | 128));
250+
251+
if status != http::StatusCode::OK || !is_grpc || !valid_grpc_web_first_byte {
238252
let preview_len = std::cmp::min(body_bytes.len(), 1024);
239253
let body_preview = String::from_utf8_lossy(&body_bytes[..preview_len]);
240254
tracing::warn!(
241255
status = %status,
242256
uri = %uri,
243257
content_type = %content_type,
244258
body_len = body_bytes.len(),
259+
first_body_byte = ?first_body_byte,
260+
first_body_byte_hex = ?first_body_byte_hex,
261+
first_body_byte_ascii = ?first_body_byte_ascii,
245262
body_preview = %body_preview,
246-
"[libsql diagnostic] non-gRPC HTTP response — will cause 'Invalid header bit' error"
263+
"[libsql diagnostic] raw HTTP response may fail grpc-web framing"
247264
);
248-
Ok(http::Response::from_parts(parts, hyper::Body::from(body_bytes)))
249265
} else {
250-
tracing::trace!(status = %status, uri = %uri, "[libsql diagnostic] gRPC response OK");
251-
Ok(resp)
266+
tracing::trace!(
267+
status = %status,
268+
uri = %uri,
269+
content_type = %content_type,
270+
body_len = body_bytes.len(),
271+
first_body_byte = ?first_body_byte,
272+
"[libsql diagnostic] raw HTTP response has valid grpc-web first byte"
273+
);
252274
}
275+
276+
Ok(http::Response::from_parts(parts, hyper::Body::from(body_bytes)))
253277
})
254278
}
255279
}

0 commit comments

Comments
 (0)