@@ -784,3 +784,107 @@ async fn tcp_keepalive_idle_time_matches_config() {
784784 "TCP_KEEPIDLE should equal keepalive_timeout_ms * TCP_IDLE_TIMEOUT_FACTOR"
785785 ) ;
786786}
787+
788+ /// Server rejects a request whose body exceeds `max_request_body_bytes` with 413 and
789+ /// `ServerError::RequestBodyTooLarge`.
790+ #[ tokio:: test]
791+ async fn request_body_too_large ( ) {
792+ let mut available_ports = available_ports_factory ( unique_u16 ! ( ) ) ;
793+ let a_socket = available_ports. get_next_local_host_socket ( ) ;
794+ let dummy_b_socket = available_ports. get_next_local_host_socket ( ) ;
795+
796+ // B client points at a non-existent server; it will never be called because the oversized
797+ // request is rejected at the HTTP layer before any component logic runs.
798+ let b_remote_client = ComponentBClient :: new (
799+ RemoteClientConfig :: default ( ) ,
800+ & dummy_b_socket. ip ( ) . to_string ( ) ,
801+ dummy_b_socket. port ( ) ,
802+ & TEST_REMOTE_CLIENT_METRICS ,
803+ ) ;
804+ let component_a = ComponentA :: new ( Box :: new ( b_remote_client) ) ;
805+
806+ let ( tx_a, rx_a) = channel :: < RequestWrapper < ComponentARequest , ComponentAResponse > > ( 32 ) ;
807+ let a_local_client = LocalComponentClient :: new ( tx_a, & TEST_LOCAL_CLIENT_METRICS ) ;
808+
809+ let mut local_server = LocalComponentServer :: new (
810+ component_a,
811+ & LocalServerConfig :: default ( ) ,
812+ rx_a,
813+ & TEST_LOCAL_SERVER_METRICS ,
814+ ) ;
815+ task:: spawn ( async move {
816+ let _ = local_server. start ( ) . await ;
817+ } ) ;
818+
819+ let server_config = RemoteServerConfig { max_request_body_bytes : 1 , ..Default :: default ( ) } ;
820+ let mut remote_server = RemoteComponentServer :: new (
821+ a_local_client,
822+ server_config,
823+ a_socket. port ( ) ,
824+ & TEST_REMOTE_SERVER_METRICS ,
825+ ) ;
826+ task:: spawn ( async move {
827+ let _ = remote_server. start ( ) . await ;
828+ } ) ;
829+ task:: yield_now ( ) . await ;
830+
831+ let uri: Uri = format ! ( "http://[{}]:{}/" , a_socket. ip( ) , a_socket. port( ) ) . parse ( ) . unwrap ( ) ;
832+ let http_request = Request :: post ( uri)
833+ . header ( CONTENT_TYPE , APPLICATION_OCTET_STREAM )
834+ . header ( REQUEST_ID_HEADER , RequestId :: generate ( ) . to_string ( ) )
835+ . body ( Full :: new ( Bytes :: from ( "x" . repeat ( 1024 ) ) ) )
836+ . unwrap ( ) ;
837+ let http_response =
838+ Client :: builder ( TokioExecutor :: new ( ) ) . build_http ( ) . request ( http_request) . await . unwrap ( ) ;
839+
840+ assert_eq ! ( http_response. status( ) , StatusCode :: PAYLOAD_TOO_LARGE ) ;
841+ let body_bytes = http_response. into_body ( ) . collect ( ) . await . unwrap ( ) . to_bytes ( ) ;
842+ let server_error = SerdeWrapper :: < ServerError > :: wrapper_deserialize ( & body_bytes) . unwrap ( ) ;
843+ assert ! ( matches!( server_error, ServerError :: RequestBodyTooLarge ( _) ) ) ;
844+ }
845+
846+ /// Client returns `ResponseParsingFailure` when the server's response body exceeds
847+ /// `max_response_body_bytes`.
848+ #[ tokio:: test]
849+ async fn response_body_too_large ( ) {
850+ let socket = available_ports_factory ( unique_u16 ! ( ) ) . get_next_local_host_socket ( ) ;
851+ task:: spawn ( async move {
852+ async fn handler (
853+ _http_request : Request < Incoming > ,
854+ ) -> Result < Response < Full < Bytes > > , Infallible > {
855+ Ok ( Response :: builder ( )
856+ . status ( StatusCode :: OK )
857+ . header ( CONTENT_TYPE , APPLICATION_OCTET_STREAM )
858+ . body ( Full :: new ( Bytes :: from ( vec ! [ 0u8 ; 1024 ] ) ) )
859+ . unwrap ( ) )
860+ }
861+
862+ let listener = TcpListener :: bind ( & socket) . await . unwrap ( ) ;
863+ loop {
864+ let Ok ( ( stream, _) ) = listener. accept ( ) . await else { continue } ;
865+ let io = TokioIo :: new ( stream) ;
866+ let service = service_fn ( |req| async move { handler ( req) . await } ) ;
867+ tokio:: spawn ( async move {
868+ let _ = Http2ServerBuilder :: new ( TokioExecutor :: new ( ) )
869+ . http2 ( )
870+ . serve_connection ( io, service)
871+ . await ;
872+ } ) ;
873+ }
874+ } ) ;
875+ task:: yield_now ( ) . await ;
876+
877+ let client_config =
878+ RemoteClientConfig { max_response_body_bytes : 1 , retries : 0 , ..FAST_FAILING_CLIENT_CONFIG } ;
879+ let client = ComponentAClient :: new (
880+ client_config,
881+ & socket. ip ( ) . to_string ( ) ,
882+ socket. port ( ) ,
883+ & TEST_REMOTE_CLIENT_METRICS ,
884+ ) ;
885+
886+ let Err ( error) = client. a_get_value ( ) . await else {
887+ panic ! ( "Expected an error" ) ;
888+ } ;
889+ assert ! ( matches!( error, ClientError :: ResponseParsingFailure ( _) ) , "unexpected error: {error}" ) ;
890+ }
0 commit comments