1- //! Error Handler
1+ //! Error Handler
22
33use futures_util:: future:: BoxFuture ;
44use hyper:: http:: request:: Parts ;
@@ -7,10 +7,29 @@ use super::Error;
77
88use std:: sync:: { Arc , Weak } ;
99
10- /// Trait for types that represents an error handler
11- pub trait ErrorHandler {
12- /// Calls the error handler function
13- fn call ( & self , parts : & Parts , err : Error ) -> BoxFuture < ' _ , HttpResult > ;
10+ /// Trait for types that represent a global error handler.
11+ ///
12+ /// Instead of receiving full request [`Parts`] at error time (which would
13+ /// require cloning them on every request), an `ErrorHandler` pre-extracts
14+ /// whatever arguments it needs *before* the parts are consumed and returns
15+ /// them as a type-erased [`ErasedErrorArgs`]. The extracted args are dropped
16+ /// on the happy path and invoked only if the handler returns an error.
17+ pub trait ErrorHandler : Send + Sync {
18+ /// Extracts handler arguments from the request parts before they are consumed.
19+ ///
20+ /// Called on every matched request. The returned [`ErasedErrorArgs`] is
21+ /// dropped if no error occurs, or invoked with the error if one does.
22+ fn extract ( & self , parts : & Parts ) -> Box < dyn ErasedErrorArgs + Send > ;
23+ }
24+
25+ /// Type-erased, pre-extracted error handler arguments.
26+ ///
27+ /// Produced by [`ErrorHandler::extract`] before request parts are consumed.
28+ /// Stores the handler closure and its already-extracted arguments so that
29+ /// `parts.clone()` is never needed on the hot path.
30+ pub trait ErasedErrorArgs : Send {
31+ /// Invokes the error handler with the given error.
32+ fn call ( self : Box < Self > , err : Error ) -> BoxFuture < ' static , HttpResult > ;
1433}
1534
1635/// Owns a closure that handles an error
2241 Args : FromRequestParts + Send
2342{
2443 func : F ,
25- _marker : std:: marker:: PhantomData < fn ( Args ) > ,
44+ _marker : std:: marker:: PhantomData < fn ( Args ) -> R > ,
2645}
2746
2847impl < F , R , Args > ErrorFunc < F , R , Args >
@@ -39,26 +58,70 @@ where
3958 }
4059}
4160
42- impl < F , R , Args > ErrorHandler for ErrorFunc < F , R , Args >
61+ /// Stores a pre-extracted handler invocation: the function, its arguments,
62+ /// and the request URI (for `err.instance`). Allocated once per request
63+ /// instead of cloning the full `Parts`.
64+ struct BoundErrorArgs < F , Args > {
65+ func : F ,
66+ args : Args ,
67+ uri : String ,
68+ }
69+
70+ impl < F , Args > ErasedErrorArgs for BoundErrorArgs < F , Args >
4371where
44- F : MapErrHandler < Args , Output = R > ,
45- R : IntoResponse + ' static ,
46- Args : FromRequestParts + Send + ' static ,
72+ F : MapErrHandler < Args > + Send + ' static ,
73+ F :: Output : IntoResponse + ' static ,
74+ Args : Send + ' static ,
4775{
48- #[ inline]
49- fn call ( & self , parts : & Parts , err : Error ) -> BoxFuture < ' _ , HttpResult > {
50- let Ok ( args) = Args :: from_parts ( parts) else {
51- return Box :: pin ( default_error_handler ( err) ) ;
52- } ;
76+ fn call ( self : Box < Self > , mut err : Error ) -> BoxFuture < ' static , HttpResult > {
5377 Box :: pin ( async move {
54- match self . func . call ( err, args) . await . into_response ( ) {
78+ if err. instance . is_none ( ) {
79+ err. instance = Some ( self . uri ) ;
80+ }
81+ match self . func . call ( err, self . args ) . await . into_response ( ) {
5582 Ok ( resp) => Ok ( resp) ,
5683 Err ( err) => default_error_handler ( err) . await ,
5784 }
5885 } )
5986 }
6087}
6188
89+ /// Fallback used when `Args` extraction fails; delegates to the default handler.
90+ struct DefaultErrorArgs {
91+ uri : String ,
92+ }
93+
94+ impl ErasedErrorArgs for DefaultErrorArgs {
95+ fn call ( self : Box < Self > , mut err : Error ) -> BoxFuture < ' static , HttpResult > {
96+ Box :: pin ( async move {
97+ if err. instance . is_none ( ) {
98+ err. instance = Some ( self . uri ) ;
99+ }
100+ default_error_handler ( err) . await
101+ } )
102+ }
103+ }
104+
105+ impl < F , R , Args > ErrorHandler for ErrorFunc < F , R , Args >
106+ where
107+ F : MapErrHandler < Args , Output = R > + Clone + ' static ,
108+ R : IntoResponse + ' static ,
109+ Args : FromRequestParts + Send + ' static ,
110+ {
111+ #[ inline]
112+ fn extract ( & self , parts : & Parts ) -> Box < dyn ErasedErrorArgs + Send > {
113+ let uri = parts. uri . to_string ( ) ;
114+ match Args :: from_parts ( parts) {
115+ Ok ( args) => Box :: new ( BoundErrorArgs {
116+ func : self . func . clone ( ) ,
117+ args,
118+ uri,
119+ } ) ,
120+ Err ( _) => Box :: new ( DefaultErrorArgs { uri } ) ,
121+ }
122+ }
123+ }
124+
62125impl < F , R , Args > From < ErrorFunc < F , R , Args > > for PipelineErrorHandler
63126where
64127 F : MapErrHandler < Args , Output = R > ,
@@ -74,14 +137,14 @@ where
74137/// Holds a reference to global error handler
75138pub ( crate ) type PipelineErrorHandler = Arc <
76139 dyn ErrorHandler
77- + Send
140+ + Send
78141 + Sync
79142> ;
80143
81144/// Weak version of [`crate::error::PipelineErrorHandler`]
82145pub ( crate ) type WeakErrorHandler = Weak <
83146 dyn ErrorHandler
84- + Send
147+ + Send
85148 + Sync
86149> ;
87150
@@ -91,16 +154,16 @@ pub(crate) async fn default_error_handler(err: Error) -> HttpResult {
91154 status ! ( err. status. as_u16( ) , "{err}" )
92155}
93156
157+ /// Extracts error handler arguments from request parts before they are consumed.
158+ ///
159+ /// Returns `None` if the handler `Weak` reference can no longer be upgraded
160+ /// (i.e. the server is shutting down).
94161#[ inline]
95- pub ( crate ) async fn call_weak_err_handler ( error_handler : WeakErrorHandler , parts : Parts , mut err : Error ) -> HttpResult {
96- if err. instance . is_none ( ) {
97- err. instance = Some ( parts. uri . to_string ( ) ) ;
98- }
99- error_handler
100- . upgrade ( )
101- . ok_or ( Error :: server_error ( "Server Error: error handler could not be upgraded" ) ) ?
102- . call ( & parts, err)
103- . await
162+ pub ( crate ) fn extract_error_args (
163+ handler : & WeakErrorHandler ,
164+ parts : & Parts ,
165+ ) -> Option < Box < dyn ErasedErrorArgs + Send > > {
166+ handler. upgrade ( ) . map ( |h| h. extract ( parts) )
104167}
105168
106169#[ cfg( test) ]
@@ -114,8 +177,8 @@ mod tests {
114177 ErrorFunc ,
115178 PipelineErrorHandler ,
116179 WeakErrorHandler ,
117- default_error_handler,
118- call_weak_err_handler
180+ default_error_handler,
181+ extract_error_args ,
119182 } ;
120183
121184 #[ tokio:: test]
@@ -126,7 +189,7 @@ mod tests {
126189
127190 let mut response = response. unwrap ( ) ;
128191 let body = & response. body_mut ( ) . collect ( ) . await . unwrap ( ) . to_bytes ( ) ;
129-
192+
130193 assert_eq ! ( response. status( ) , 500 ) ;
131194 assert_eq ! ( String :: from_utf8_lossy( body) , "Some error" ) ;
132195 }
@@ -153,8 +216,9 @@ mod tests {
153216
154217 let req = Request :: get ( "/foo/bar?baz" ) . body ( ( ) ) . unwrap ( ) ;
155218 let ( parts, _) = req. into_parts ( ) ;
156- let response = handler. call ( & parts, error) . await ;
157-
219+ let extracted = handler. extract ( & parts) ;
220+ let response = extracted. call ( error) . await ;
221+
158222 assert ! ( response. is_ok( ) ) ;
159223
160224 let mut response = response. unwrap ( ) ;
@@ -175,7 +239,8 @@ mod tests {
175239
176240 let req = Request :: get ( "/foo/bar?baz" ) . body ( ( ) ) . unwrap ( ) ;
177241 let ( parts, _) = req. into_parts ( ) ;
178- let response = call_weak_err_handler ( weak_handler, parts, error) . await ;
242+ let extracted = extract_error_args ( & weak_handler, & parts) . expect ( "handler alive" ) ;
243+ let response = extracted. call ( error) . await ;
179244 assert ! ( response. is_ok( ) ) ;
180245
181246 let mut response = response. unwrap ( ) ;
@@ -184,4 +249,4 @@ mod tests {
184249 assert_eq ! ( response. status( ) , 403 ) ;
185250 assert_eq ! ( body. len( ) , 0 ) ;
186251 }
187- }
252+ }
0 commit comments