@@ -6,12 +6,15 @@ use crate::auth::{
66} ;
77use crate :: methods:: { RpcContext , RpcRegtest } ;
88use axum:: body:: Bytes ;
9- use axum:: extract:: { DefaultBodyLimit , State } ;
9+ use axum:: extract:: { DefaultBodyLimit , FromRequest , Request , State } ;
1010use axum:: http:: { header, HeaderMap , StatusCode } ;
1111use axum:: middleware:: { from_fn_with_state, Next } ;
1212use axum:: response:: { IntoResponse , Response } ;
1313use axum:: routing:: { get, post} ;
1414use axum:: Router ;
15+ use hyper:: server:: conn:: http1;
16+ use hyper_util:: rt:: { TokioIo , TokioTimer } ;
17+ use hyper_util:: service:: TowerToHyperService ;
1518
1619/// Axum's default request-body cap, named so auth and 413 share one limit.
1720pub const RPC_MAX_HTTP_BODY : usize = 2 * 1024 * 1024 ;
@@ -20,6 +23,7 @@ pub const DEFAULT_RPC_WORK_QUEUE: usize = 16;
2023/// Accept cap, copied from the Electrum public listener. Not a knob.
2124#[ doc( hidden) ]
2225pub const RPC_MAX_CONNECTIONS : usize = 256 ;
26+ const RPC_READ_TIMEOUT : std:: time:: Duration = std:: time:: Duration :: from_secs ( 30 ) ;
2327use rbitcoin_log:: info;
2428#[ cfg( unix) ]
2529use rbitcoin_log:: warn;
@@ -28,14 +32,11 @@ use rbitcoin_primitives::Network;
2832use rbitcoin_query:: Query ;
2933use std:: net:: SocketAddr ;
3034use std:: path:: PathBuf ;
31- use std:: pin:: Pin ;
3235use std:: sync:: atomic:: { AtomicBool , AtomicU64 , Ordering } ;
3336use std:: sync:: Arc ;
34- use std:: task:: { Context , Poll } ;
3537use std:: time:: Instant ;
36- use tokio:: io:: { AsyncRead , AsyncWrite , ReadBuf } ;
3738use tokio:: net:: TcpListener ;
38- use tokio:: sync:: { OwnedSemaphorePermit , Semaphore } ;
39+ use tokio:: sync:: Semaphore ;
3940
4041#[ cfg( unix) ]
4142#[ doc( hidden) ]
@@ -48,83 +49,40 @@ pub fn bind_unix_mode(
4849 std:: fs:: set_permissions ( path, std:: fs:: Permissions :: from_mode ( mode) ) ?;
4950 Ok ( listener)
5051}
51- use tokio:: task:: JoinHandle ;
52+ use tokio:: task:: { JoinHandle , JoinSet } ;
5253
53- /// One accepted connection. The permit is the accept slot and dies with the socket.
54- #[ doc( hidden) ]
55- pub struct HeldConn < S > {
56- pub io : S ,
57- pub _permit : OwnedSemaphorePermit ,
58- }
59-
60- impl < S : AsyncRead + Unpin > AsyncRead for HeldConn < S > {
61- fn poll_read (
62- self : Pin < & mut Self > ,
63- cx : & mut Context < ' _ > ,
64- buf : & mut ReadBuf < ' _ > ,
65- ) -> Poll < std:: io:: Result < ( ) > > {
66- Pin :: new ( & mut self . get_mut ( ) . io ) . poll_read ( cx, buf)
67- }
68- }
69-
70- impl < S : AsyncWrite + Unpin > AsyncWrite for HeldConn < S > {
71- fn poll_write (
72- self : Pin < & mut Self > ,
73- cx : & mut Context < ' _ > ,
74- buf : & [ u8 ] ,
75- ) -> Poll < std:: io:: Result < usize > > {
76- Pin :: new ( & mut self . get_mut ( ) . io ) . poll_write ( cx, buf)
77- }
78-
79- fn poll_flush ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < std:: io:: Result < ( ) > > {
80- Pin :: new ( & mut self . get_mut ( ) . io ) . poll_flush ( cx)
81- }
82-
83- fn poll_shutdown ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < std:: io:: Result < ( ) > > {
84- Pin :: new ( & mut self . get_mut ( ) . io ) . poll_shutdown ( cx)
85- }
86- }
87-
88- /// Drops a new socket when [`RPC_MAX_CONNECTIONS`] are already open.
89- #[ doc( hidden) ]
90- pub struct AcceptCap < L > {
91- pub listener : L ,
92- pub sem : Arc < Semaphore > ,
93- }
94-
95- impl < L > axum:: serve:: Listener for AcceptCap < L >
54+ async fn serve_rpc < L > ( mut listener : L , app : Router , sem : Arc < Semaphore > , shutdown : Arc < AtomicBool > )
9655where
9756 L : axum:: serve:: Listener ,
98- L :: Io : AsyncRead + AsyncWrite + Unpin ,
9957 L :: Addr : std:: fmt:: Debug ,
10058{
101- type Io = HeldConn < L :: Io > ;
102- type Addr = L :: Addr ;
103-
104- async fn accept ( & mut self ) -> ( Self :: Io , Self :: Addr ) {
105- loop {
106- let ( io, addr) = axum:: serve:: Listener :: accept ( & mut self . listener ) . await ;
107- match self . sem . clone ( ) . try_acquire_owned ( ) {
108- Ok ( permit) => {
109- return (
110- HeldConn {
111- io,
112- _permit : permit,
113- } ,
114- addr,
115- )
59+ let mut connections = JoinSet :: new ( ) ;
60+ let mut shutdown_tick = tokio:: time:: interval ( std:: time:: Duration :: from_millis ( 50 ) ) ;
61+ loop {
62+ tokio:: select! {
63+ _ = shutdown_tick. tick( ) => {
64+ if shutdown. load( Ordering :: SeqCst ) {
65+ break ;
11666 }
117- Err ( _) => {
67+ }
68+ Some ( _) = connections. join_next( ) , if !connections. is_empty( ) => { }
69+ ( io, addr) = listener. accept( ) => {
70+ let Ok ( permit) = sem. clone( ) . try_acquire_owned( ) else {
11871 info!( "rpc: reject {addr:?} (at max connections={RPC_MAX_CONNECTIONS})" ) ;
119- drop ( io) ;
120- }
72+ continue ;
73+ } ;
74+ let service = TowerToHyperService :: new( app. clone( ) ) ;
75+ connections. spawn( async move {
76+ let _permit = permit;
77+ let _ = http1:: Builder :: new( )
78+ . timer( TokioTimer :: new( ) )
79+ . header_read_timeout( RPC_READ_TIMEOUT )
80+ . serve_connection( TokioIo :: new( io) , service)
81+ . await ;
82+ } ) ;
12183 }
12284 }
12385 }
124-
125- fn local_addr ( & self ) -> std:: io:: Result < Self :: Addr > {
126- self . listener . local_addr ( )
127- }
12886}
12987
13088/// RPC listen configuration.
@@ -288,22 +246,9 @@ pub async fn run_rpc(
288246 let app = rpc_app ( state) ;
289247 let shutdown_w = Arc :: clone ( & shutdown) ;
290248 let accept_sem = Arc :: clone ( & accept_sem) ;
291- tasks. push ( tokio:: spawn ( async move {
292- axum:: serve (
293- AcceptCap {
294- listener,
295- sem : accept_sem,
296- } ,
297- app,
298- )
299- . with_graceful_shutdown ( async move {
300- while !shutdown_w. load ( Ordering :: SeqCst ) {
301- tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 50 ) ) . await ;
302- }
303- } )
304- . await
305- . ok ( ) ;
306- } ) ) ;
249+ tasks. push ( tokio:: spawn ( serve_rpc (
250+ listener, app, accept_sem, shutdown_w,
251+ ) ) ) ;
307252 info ! (
308253 "rpc: HTTP JSON-RPC on {bound} (bearer token {})" ,
309254 token_path. display( )
@@ -354,22 +299,9 @@ pub async fn run_rpc(
354299 let app = rpc_app ( state) ;
355300 let shutdown_w = Arc :: clone ( & shutdown) ;
356301 let accept_sem = Arc :: clone ( & accept_sem) ;
357- tasks. push ( tokio:: spawn ( async move {
358- axum:: serve (
359- AcceptCap {
360- listener,
361- sem : accept_sem,
362- } ,
363- app,
364- )
365- . with_graceful_shutdown ( async move {
366- while !shutdown_w. load ( Ordering :: SeqCst ) {
367- tokio:: time:: sleep ( std:: time:: Duration :: from_millis ( 50 ) ) . await ;
368- }
369- } )
370- . await
371- . ok ( ) ;
372- } ) ) ;
302+ tasks. push ( tokio:: spawn ( serve_rpc (
303+ listener, app, accept_sem, shutdown_w,
304+ ) ) ) ;
373305 info ! ( "rpc: unix JSON-RPC on {}" , sock. display( ) ) ;
374306 Some ( sock. clone ( ) )
375307 } else {
@@ -422,9 +354,10 @@ pub async fn rest_entry(State(state): State<AppState>, req: axum::extract::Reque
422354 // and REST does not use the RPC work queue.
423355 let path = req. uri ( ) . path ( ) . to_string ( ) ;
424356 let query = req. uri ( ) . query ( ) . unwrap_or ( "" ) . to_string ( ) ;
425- let body = axum:: body:: to_bytes ( req. into_body ( ) , RPC_MAX_HTTP_BODY )
426- . await
427- . unwrap_or_default ( ) ;
357+ let body = match read_http_body ( req) . await {
358+ Ok ( body) => body,
359+ Err ( response) => return response,
360+ } ;
428361 let _permit = match state. rest_queue . try_acquire ( ) {
429362 Ok ( p) => p,
430363 Err ( _) => {
@@ -623,7 +556,20 @@ pub async fn recv_tip(tips: &mut Option<tokio::sync::broadcast::Receiver<rbitcoi
623556 }
624557}
625558
626- pub async fn rpc_post ( State ( state) : State < AppState > , body : Bytes ) -> Response {
559+ async fn read_http_body ( req : Request ) -> Result < Bytes , Response > {
560+ match tokio:: time:: timeout ( RPC_READ_TIMEOUT , Bytes :: from_request ( req, & ( ) ) ) . await {
561+ Ok ( body) => body. map_err ( IntoResponse :: into_response) ,
562+ Err ( _) => {
563+ Err ( ( StatusCode :: REQUEST_TIMEOUT , [ ( header:: CONNECTION , "close" ) ] ) . into_response ( ) )
564+ }
565+ }
566+ }
567+
568+ pub async fn rpc_post ( State ( state) : State < AppState > , req : Request ) -> Response {
569+ let body = match read_http_body ( req) . await {
570+ Ok ( body) => body,
571+ Err ( response) => return response,
572+ } ;
627573 let parsed: serde_json:: Value = match serde_json:: from_slice ( & body) {
628574 Ok ( v) => v,
629575 Err ( _) => {
0 commit comments