@@ -35,12 +35,13 @@ use crate::{
3535 registry_helper:: RegistryHelper ,
3636 utils:: https_endpoint_to_url,
3737} ;
38- use http_body_util:: { BodyExt , Full } ;
38+ use http_body_util:: { BodyExt , Full , Limited } ;
3939use hyper:: { Method , Request , StatusCode , body:: Bytes } ;
4040use hyper_rustls:: HttpsConnectorBuilder ;
4141use hyper_util:: { client:: legacy:: Client , rt:: TokioExecutor } ;
4242use ic_crypto_tls_interfaces:: TlsConfig ;
4343use ic_interfaces:: crypto:: ThresholdSigVerifierByPublicKey ;
44+ use ic_limits:: MAX_MESSAGE_SIZE_BYTES ;
4445use ic_logger:: { ReplicaLogger , info, warn} ;
4546use ic_protobuf:: { registry:: node:: v1:: NodeRecord , types:: v1 as pb} ;
4647use ic_sys:: fs:: write_protobuf_using_tmp_file;
@@ -113,6 +114,7 @@ pub(crate) struct CatchUpPackageProvider {
113114 node_id : NodeId ,
114115 backoff : Duration ,
115116 initial_backoff : Duration ,
117+ max_response_size_bytes : usize ,
116118 local_cup_reader : LocalCUPReader ,
117119}
118120
@@ -154,6 +156,7 @@ impl CatchUpPackageProvider {
154156 logger,
155157 backoff : initial_backoff,
156158 initial_backoff,
159+ max_response_size_bytes : MAX_MESSAGE_SIZE_BYTES ,
157160 local_cup_reader,
158161 }
159162 }
@@ -346,7 +349,10 @@ impl CatchUpPackageProvider {
346349 . map_err ( |e| format ! ( "Failed to query CUP endpoint at {url}: {e:?}" ) ) ?;
347350
348351 let status = res. status ( ) ;
349- let body_req = timeout ( self . backoff , res. into_body ( ) . collect ( ) ) ;
352+ let body_req = timeout (
353+ self . backoff ,
354+ Limited :: new ( res. into_body ( ) , self . max_response_size_bytes ) . collect ( ) ,
355+ ) ;
350356
351357 let bytes = match body_req. await {
352358 Ok ( result) => {
@@ -831,6 +837,32 @@ pub(crate) mod tests {
831837 assert_eq ! ( cup_provider. backoff, initial_backoff) ;
832838 }
833839
840+ #[ tokio:: test]
841+ async fn test_fetch_catch_up_package_body_exceeds_size_limit ( ) {
842+ // The server responds with a full CUP body.
843+ let server_addr =
844+ start_server ( TestService :: SendBodyOrStall ( Arc :: new ( Mutex :: new ( true ) ) ) ) . await ;
845+ let url = format ! ( "https://{server_addr}" ) ;
846+ let tmp_dir = tempfile:: tempdir ( ) . unwrap ( ) ;
847+ let node_id = node_test_id ( 1 ) ;
848+
849+ let mut cup_provider = make_cup_provider (
850+ tmp_dir. path ( ) . to_path_buf ( ) ,
851+ node_id,
852+ Duration :: from_secs ( 5 ) ,
853+ ) ;
854+ // Set a size limit far below the size of the CUP body the server sends, so that reading
855+ // the body is aborted rather than buffered in full.
856+ cup_provider. max_response_size_bytes = 4 ;
857+
858+ let err = cup_provider
859+ . fetch_catch_up_package ( & node_id, url, None )
860+ . await
861+ . expect_err ( "Expected an error when the CUP body exceeds the size limit" ) ;
862+
863+ assert ! ( err. contains( "LengthLimitError" ) , "Unexpected error: {err}" ) ;
864+ }
865+
834866 #[ tokio:: test]
835867 async fn test_fetch_catch_up_package_unresponsive_times_out ( ) {
836868 let server_addr = start_server ( TestService :: Unresponsive ) . await ;
0 commit comments