@@ -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