@@ -13,6 +13,7 @@ use tokio_listener::{Listener, SystemOptions, UserOptions};
1313use tower:: { Service , ServiceBuilder } ;
1414use tracing:: info;
1515
16+ use crate :: directory:: V1_MAX_BUFFER_SIZE ;
1617use crate :: ohttp_relay:: SentinelTag ;
1718
1819#[ cfg( feature = "access-control" ) ]
@@ -354,8 +355,12 @@ fn build_app(services: Services) -> Router {
354355 #[ cfg( feature = "access-control" ) ]
355356 let geoip = services. geoip . clone ( ) ;
356357
358+ let v1_method_router = axum:: routing:: post ( v1_post_handler) . fallback ( route_request) ;
359+ let v1_router = Router :: new ( ) . route ( "/{id}" , v1_method_router) ;
360+
357361 #[ allow( unused_mut) ]
358362 let mut router = Router :: new ( )
363+ . merge ( v1_router)
359364 . fallback ( route_request)
360365 . layer (
361366 ServiceBuilder :: new ( )
@@ -374,6 +379,19 @@ fn build_app(services: Services) -> Router {
374379 router
375380}
376381
382+ async fn v1_post_handler (
383+ State ( mut services) : State < Services > ,
384+ req : axum:: extract:: Request ,
385+ ) -> Response {
386+ let ( parts, body) = req. into_parts ( ) ;
387+ let limited = http_body_util:: Limited :: new ( body, V1_MAX_BUFFER_SIZE ) ;
388+ let req = axum:: http:: Request :: from_parts ( parts, axum:: body:: Body :: new ( limited) ) ;
389+ match services. directory . call ( req) . await {
390+ Ok ( res) => res. into_response ( ) ,
391+ Err ( e) => ( axum:: http:: StatusCode :: INTERNAL_SERVER_ERROR , e. to_string ( ) ) . into_response ( ) ,
392+ }
393+ }
394+
377395async fn route_request (
378396 State ( mut services) : State < Services > ,
379397 req : axum:: extract:: Request ,
@@ -565,4 +583,45 @@ mod tests {
565583 assert ! ( metric_names. contains( & TOTAL_CONNECTIONS ) , "missing total_connections" ) ;
566584 assert ! ( metric_names. contains( & ACTIVE_CONNECTIONS ) , "missing active_connections" ) ;
567585 }
586+
587+ #[ tokio:: test]
588+ async fn v1_post_rejects_oversized_body ( ) {
589+ use axum:: body:: Body ;
590+ use axum:: http:: Request ;
591+ use payjoin:: directory:: ShortId ;
592+ use tower:: ServiceExt ;
593+
594+ let tempdir = tempdir ( ) . unwrap ( ) ;
595+ let config = Config :: new (
596+ "[::]:0" . parse ( ) . expect ( "valid listener address" ) ,
597+ tempdir. path ( ) . to_path_buf ( ) ,
598+ Duration :: from_secs ( 2 ) ,
599+ Some ( crate :: config:: V1Config :: default ( ) ) ,
600+ ) ;
601+
602+ let sentinel_tag = generate_sentinel_tag ( ) ;
603+ let services = Services {
604+ directory : init_directory ( & config, sentinel_tag) . await . unwrap ( ) ,
605+ relay : crate :: ohttp_relay:: Service :: new ( sentinel_tag) . await ,
606+ metrics : MetricsService :: new ( None ) ,
607+ #[ cfg( feature = "access-control" ) ]
608+ geoip : None ,
609+ } ;
610+
611+ let app = build_app ( services) ;
612+
613+ let id = ShortId ( [ 0u8 ; 8 ] ) . to_string ( ) ;
614+ let oversized = vec ! [ b'a' ; V1_MAX_BUFFER_SIZE + 1 ] ;
615+ let request = Request :: builder ( )
616+ . method ( "POST" )
617+ . uri ( format ! ( "/{id}" ) )
618+ . body ( Body :: from ( oversized) )
619+ . unwrap ( ) ;
620+ let response = ServiceExt :: < Request < Body > > :: oneshot ( app, request) . await . unwrap ( ) ;
621+ assert_eq ! (
622+ response. status( ) ,
623+ axum:: http:: StatusCode :: BAD_REQUEST ,
624+ "oversized v1 POST body should be rejected"
625+ ) ;
626+ }
568627}
0 commit comments