7
7
//! Create a type that conforms to the [`tower::Service`] trait. This type can
8
8
//! then be passed to the the `lambda_runtime::run` function, which launches
9
9
//! and runs the Lambda runtime.
10
+ use futures:: FutureExt ;
10
11
use hyper:: {
11
12
client:: { connect:: Connection , HttpConnector } ,
12
13
http:: Request ,
@@ -146,10 +147,18 @@ where
146
147
147
148
let req = match handler. ready ( ) . await {
148
149
Ok ( handler) => {
150
+ // Catches panics outside of a `Future`
149
151
let task =
150
152
panic:: catch_unwind ( panic:: AssertUnwindSafe ( || handler. call ( LambdaEvent :: new ( body, ctx) ) ) ) ;
153
+
154
+ let task = match task {
155
+ // Catches panics inside of the `Future`
156
+ Ok ( task) => Ok ( panic:: AssertUnwindSafe ( task) . catch_unwind ( ) . await ) ,
157
+ Err ( err) => Err ( err) ,
158
+ } ;
159
+
151
160
match task {
152
- Ok ( response) => match response. await {
161
+ Ok ( Ok ( response) ) => match response {
153
162
Ok ( response) => {
154
163
trace ! ( "Ok response from handler (run loop)" ) ;
155
164
EventCompletionRequest {
@@ -160,7 +169,7 @@ where
160
169
}
161
170
Err ( err) => build_event_error_request ( request_id, err) ,
162
171
} ,
163
- Err ( err) => {
172
+ Err ( err) | Ok ( Err ( err ) ) => {
164
173
error ! ( "{:?}" , err) ;
165
174
let error_type = type_name_of_val ( & err) ;
166
175
let msg = if let Some ( msg) = err. downcast_ref :: < & str > ( ) {
@@ -261,6 +270,7 @@ mod endpoint_tests {
261
270
types:: Diagnostic ,
262
271
Error , Runtime ,
263
272
} ;
273
+ use futures:: future:: BoxFuture ;
264
274
use http:: { uri:: PathAndQuery , HeaderValue , Method , Request , Response , StatusCode , Uri } ;
265
275
use hyper:: { server:: conn:: Http , service:: service_fn, Body } ;
266
276
use lambda_runtime_api_client:: Client ;
@@ -508,4 +518,58 @@ mod endpoint_tests {
508
518
Err ( _) => unreachable ! ( "This branch shouldn't be reachable" ) ,
509
519
}
510
520
}
521
+
522
+ async fn run_panicking_handler < F > ( func : F ) -> Result < ( ) , Error >
523
+ where
524
+ F : FnMut ( crate :: LambdaEvent < serde_json:: Value > ) -> BoxFuture < ' static , Result < serde_json:: Value , Error > > ,
525
+ {
526
+ let ( client, server) = io:: duplex ( 64 ) ;
527
+ let ( _tx, rx) = oneshot:: channel ( ) ;
528
+ let base = Uri :: from_static ( "http://localhost:9001" ) ;
529
+
530
+ let server = tokio:: spawn ( async {
531
+ handle ( server, rx) . await . expect ( "Unable to handle request" ) ;
532
+ } ) ;
533
+ let conn = simulated:: Connector :: with ( base. clone ( ) , DuplexStreamWrapper :: new ( client) ) ?;
534
+
535
+ let client = Client :: builder ( )
536
+ . with_endpoint ( base)
537
+ . with_connector ( conn)
538
+ . build ( )
539
+ . expect ( "Unable to build client" ) ;
540
+
541
+ let f = crate :: service_fn ( func) ;
542
+
543
+ let config = crate :: Config {
544
+ function_name : "test_fn" . to_string ( ) ,
545
+ memory : 128 ,
546
+ version : "1" . to_string ( ) ,
547
+ log_stream : "test_stream" . to_string ( ) ,
548
+ log_group : "test_log" . to_string ( ) ,
549
+ } ;
550
+
551
+ let runtime = Runtime { client } ;
552
+ let client = & runtime. client ;
553
+ let incoming = incoming ( client) . take ( 1 ) ;
554
+ runtime. run ( incoming, f, & config) . await ?;
555
+
556
+ match server. await {
557
+ Ok ( _) => Ok ( ( ) ) ,
558
+ Err ( e) if e. is_panic ( ) => Err :: < ( ) , Error > ( e. into ( ) ) ,
559
+ Err ( _) => unreachable ! ( "This branch shouldn't be reachable" ) ,
560
+ }
561
+ }
562
+
563
+ #[ tokio:: test]
564
+ async fn panic_in_async_run ( ) -> Result < ( ) , Error > {
565
+ run_panicking_handler ( |_| Box :: pin ( async { panic ! ( "This is intentionally here" ) } ) ) . await
566
+ }
567
+
568
+ #[ tokio:: test]
569
+ async fn panic_outside_async_run ( ) -> Result < ( ) , Error > {
570
+ run_panicking_handler ( |_| {
571
+ panic ! ( "This is intentionally here" ) ;
572
+ } )
573
+ . await
574
+ }
511
575
}
0 commit comments