@@ -94,6 +94,7 @@ impl Rocket<Orbit> {
94
94
_token : RequestToken ,
95
95
request : & ' r Request < ' s > ,
96
96
data : Data < ' r > ,
97
+ error_ptr : & ' r mut Option < Box < dyn TypedError < ' r > + ' r > > ,
97
98
// io_stream: impl Future<Output = io::Result<IoStream>> + Send,
98
99
) -> Response < ' r > {
99
100
// Remember if the request is `HEAD` for later body stripping.
@@ -109,16 +110,24 @@ impl Rocket<Orbit> {
109
110
request. _set_method ( Method :: Get ) ;
110
111
match self . route ( request, data) . await {
111
112
Outcome :: Success ( response) => response,
112
- Outcome :: Error ( ( status, error) )
113
- => self . dispatch_error ( status, request, error) . await ,
114
- Outcome :: Forward ( ( _, status, error) )
115
- => self . dispatch_error ( status, request, error) . await ,
113
+ Outcome :: Error ( ( status, error) ) => {
114
+ * error_ptr = error;
115
+ self . dispatch_error ( status, request, error_ptr. as_ref ( ) . map ( |b| b. as_ref ( ) ) ) . await
116
+ } ,
117
+ Outcome :: Forward ( ( _, status, error) ) => {
118
+ * error_ptr = error;
119
+ self . dispatch_error ( status, request, error_ptr. as_ref ( ) . map ( |b| b. as_ref ( ) ) ) . await
120
+ } ,
116
121
}
117
122
}
118
- Outcome :: Forward ( ( _, status, error) )
119
- => self . dispatch_error ( status, request, error) . await ,
120
- Outcome :: Error ( ( status, error) )
121
- => self . dispatch_error ( status, request, error) . await ,
123
+ Outcome :: Forward ( ( _, status, error) ) => {
124
+ * error_ptr = error;
125
+ self . dispatch_error ( status, request, error_ptr. as_ref ( ) . map ( |b| b. as_ref ( ) ) ) . await
126
+ } ,
127
+ Outcome :: Error ( ( status, error) ) => {
128
+ * error_ptr = error;
129
+ self . dispatch_error ( status, request, error_ptr. as_ref ( ) . map ( |b| b. as_ref ( ) ) ) . await
130
+ } ,
122
131
} ;
123
132
124
133
// Set the cookies. Note that error responses will only include cookies
@@ -236,7 +245,7 @@ impl Rocket<Orbit> {
236
245
& ' s self ,
237
246
mut status : Status ,
238
247
req : & ' r Request < ' s > ,
239
- mut error : Option < Box < dyn TypedError < ' r > + ' r > > ,
248
+ mut error : Option < & ' r dyn TypedError < ' r > > ,
240
249
) -> Response < ' r > {
241
250
// We may wish to relax this in the future.
242
251
req. cookies ( ) . reset_delta ( ) ;
@@ -273,48 +282,64 @@ impl Rocket<Orbit> {
273
282
async fn invoke_catcher < ' s , ' r : ' s > (
274
283
& ' s self ,
275
284
status : Status ,
276
- error : Option < Box < dyn TypedError < ' r > + ' r > > ,
285
+ error : Option < & ' r dyn TypedError < ' r > > ,
277
286
req : & ' r Request < ' s >
278
287
) -> Result < Response < ' r > , Option < Status > > {
279
- let error_ty = error. as_ref ( ) . map ( |e| e. as_any ( ) . type_id ( ) ) ;
280
- println ! ( "Catching {:?}" , error. as_ref( ) . map( |e| e. name( ) ) ) ;
281
- if let Some ( catcher) = self . router . catch ( status, req, error_ty) {
282
- self . invoke_specific_catcher ( catcher, status, error. as_ref ( ) . map ( |e| e. as_ref ( ) ) , req) . await
283
- } else if let Some ( source) = error. as_ref ( ) . and_then ( |e| e. source ( ) ) {
284
- println ! ( "Catching {:?}" , source. name( ) ) ;
285
- let error_ty = source. as_any ( ) . type_id ( ) ;
286
- if let Some ( catcher) = self . router . catch ( status, req, Some ( error_ty) ) {
287
- self . invoke_specific_catcher ( catcher, status, error. as_ref ( ) . and_then ( |e| e. source ( ) ) , req) . await
288
- } else {
289
- info ! ( name: "catcher" , name = "rocket::default" , "uri.base" = "/" , code = status. code,
290
- "no registered catcher: using Rocket default" ) ;
291
- Ok ( catcher:: default_handler ( status, req) )
288
+ let mut error_copy = error;
289
+ let mut counter = 0 ;
290
+ // Matches error [.source ...] type
291
+ while error_copy. is_some ( ) && counter < 5 {
292
+ if let Some ( catcher) = self . router . catch ( status, req, error_copy. map ( |e| e. trait_obj_typeid ( ) ) ) {
293
+ return self . invoke_specific_catcher ( catcher, status, error_copy, req) . await ;
292
294
}
293
- } else {
294
- info ! ( name: "catcher" , name = "rocket::default" , "uri.base" = "/" , code = status. code,
295
- "no registered catcher: using Rocket default" ) ;
296
- Ok ( catcher:: default_handler ( status, req) )
295
+ error_copy = error_copy. and_then ( |e| e. source ( ) ) ;
296
+ counter += 1 ;
297
297
}
298
- // if let Some(catcher) = self.router.catch(status, req, error.as_ref().map(|t| t.as_any().type_id())) {
299
- // catcher.trace_info();
300
- // catch_handle(
301
- // catcher.name.as_deref(),
302
- // || catcher.handler.handle(status, req, error)
303
- // ).await
304
- // .map(|result| result.map_err(|(s, e)| (Some(s), e)))
305
- // .unwrap_or_else(|| Err((None, None)))
306
- // } else {
307
- // info!(name: "catcher", name = "rocket::default", "uri.base" = "/", code = status.code,
308
- // "no registered catcher: using Rocket default");
309
- // Ok(catcher::default_handler(status, req))
310
- // }
298
+ // Matches None type
299
+ if let Some ( catcher) = self . router . catch ( status, req, None ) {
300
+ return self . invoke_specific_catcher ( catcher, status, None , req) . await ;
301
+ }
302
+ let mut error_copy = error;
303
+ let mut counter = 0 ;
304
+ // Matches error [.source ...] type, and any status
305
+ while error_copy. is_some ( ) && counter < 5 {
306
+ if let Some ( catcher) = self . router . catch_any ( status, req, error_copy. map ( |e| e. trait_obj_typeid ( ) ) ) {
307
+ return self . invoke_specific_catcher ( catcher, status, error_copy, req) . await ;
308
+ }
309
+ error_copy = error_copy. and_then ( |e| e. source ( ) ) ;
310
+ counter += 1 ;
311
+ }
312
+ // Matches None type, and any status
313
+ if let Some ( catcher) = self . router . catch_any ( status, req, None ) {
314
+ return self . invoke_specific_catcher ( catcher, status, None , req) . await ;
315
+ }
316
+ if let Some ( error) = error {
317
+ if let Ok ( res) = error. respond_to ( req) {
318
+ return Ok ( res) ;
319
+ // TODO: this ignores the returned status.
320
+ }
321
+ }
322
+ // Rocket default catcher
323
+ info ! ( name: "catcher" , name = "rocket::default" , "uri.base" = "/" , code = status. code,
324
+ "no registered catcher: using Rocket default" ) ;
325
+ Ok ( catcher:: default_handler ( status, req) )
326
+ // TODO: document:
327
+ // Set of matching catchers, tried in order:
328
+ // - Matches error type
329
+ // - Matches error.source type
330
+ // - Matches error.source.source type
331
+ // - ... etc
332
+ // - Matches None type
333
+ // - Registered default handler
334
+ // - Rocket default handler
335
+ // At each step, the catcher with the longest path is selected
311
336
}
312
337
313
338
async fn invoke_specific_catcher < ' s , ' r : ' s > (
314
339
& ' s self ,
315
340
catcher : & Catcher ,
316
341
status : Status ,
317
- error : Option < & ' r ( dyn TypedError < ' r > + ' r ) > ,
342
+ error : Option < & ' r dyn TypedError < ' r > > ,
318
343
req : & ' r Request < ' s >
319
344
) -> Result < Response < ' r > , Option < Status > > {
320
345
catcher. trace_info ( ) ;
0 commit comments