@@ -73,6 +73,10 @@ type StreamingClient = HyperClient<HttpsConnector<hyper::client::HttpConnector>,
7373
7474const GRPC_FRAME_HEADER_LEN : usize = 5 ;
7575
76+ // Applies to complete unary gRPC responses. The server applies the same cap to unary request
77+ // bodies before protobuf decoding.
78+ const MAX_GRPC_UNARY_RESPONSE_LEN : usize = 10 * 1024 * 1024 ;
79+
7680// Applies to each server-streaming gRPC message. Graph RPCs use the unary client path and are not
7781// constrained by this limit.
7882const MAX_GRPC_STREAM_MESSAGE_LEN : usize = 4 * 1024 * 1024 ;
@@ -456,10 +460,7 @@ impl LdkServerClient {
456460 return Err ( error) ;
457461 }
458462
459- // Read the response body
460- let payload = response. bytes ( ) . await . map_err ( |e| {
461- LdkServerError :: new ( InternalError , format ! ( "Failed to read response body: {}" , e) )
462- } ) ?;
463+ let payload = read_grpc_unary_response_body ( response) . await ?;
463464
464465 let proto_bytes = decode_grpc_body ( & payload)
465466 . map_err ( |e| LdkServerError :: new ( InternalError , e. message ) ) ?;
@@ -514,6 +515,42 @@ impl LdkServerClient {
514515 }
515516}
516517
518+ async fn read_grpc_unary_response_body (
519+ mut response : reqwest:: Response ,
520+ ) -> Result < Vec < u8 > , LdkServerError > {
521+ let capacity = if let Some ( content_length) = response. content_length ( ) {
522+ check_grpc_unary_response_len ( content_length) ?;
523+ content_length as usize
524+ } else {
525+ 0
526+ } ;
527+
528+ let mut payload = Vec :: with_capacity ( capacity) ;
529+ while let Some ( chunk) = response. chunk ( ) . await . map_err ( |e| {
530+ LdkServerError :: new ( InternalError , format ! ( "Failed to read response body: {}" , e) )
531+ } ) ? {
532+ let len = payload. len ( ) . checked_add ( chunk. len ( ) ) . ok_or_else ( || {
533+ LdkServerError :: new ( InternalError , "gRPC unary response body length overflow" )
534+ } ) ?;
535+ check_grpc_unary_response_len ( len as u64 ) ?;
536+ payload. extend_from_slice ( & chunk) ;
537+ }
538+ Ok ( payload)
539+ }
540+
541+ fn check_grpc_unary_response_len ( len : u64 ) -> Result < ( ) , LdkServerError > {
542+ if len > MAX_GRPC_UNARY_RESPONSE_LEN as u64 {
543+ return Err ( LdkServerError :: new (
544+ InternalError ,
545+ format ! (
546+ "gRPC unary response exceeds maximum size of {} bytes" ,
547+ MAX_GRPC_UNARY_RESPONSE_LEN
548+ ) ,
549+ ) ) ;
550+ }
551+ Ok ( ( ) )
552+ }
553+
517554/// Map a gRPC status code to an LdkServerError.
518555fn grpc_code_to_error ( code : u32 , message : String ) -> LdkServerError {
519556 match code {
@@ -721,6 +758,25 @@ mod tests {
721758 assert_eq ! ( err. message, "gRPC stream became unavailable: server shutting down" ) ;
722759 }
723760
761+ #[ test]
762+ fn test_grpc_unary_response_len_allows_limit ( ) {
763+ assert ! ( check_grpc_unary_response_len( MAX_GRPC_UNARY_RESPONSE_LEN as u64 ) . is_ok( ) ) ;
764+ }
765+
766+ #[ test]
767+ fn test_grpc_unary_response_len_rejects_oversized ( ) {
768+ let err =
769+ check_grpc_unary_response_len ( MAX_GRPC_UNARY_RESPONSE_LEN as u64 + 1 ) . unwrap_err ( ) ;
770+ assert_eq ! ( err. error_code, InternalError ) ;
771+ assert_eq ! (
772+ err. message,
773+ format!(
774+ "gRPC unary response exceeds maximum size of {} bytes" ,
775+ MAX_GRPC_UNARY_RESPONSE_LEN
776+ )
777+ ) ;
778+ }
779+
724780 #[ tokio:: test]
725781 async fn test_event_stream_surfaces_terminal_grpc_status ( ) {
726782 let ( mut sender, body) = Body :: channel ( ) ;
0 commit comments