77//! Create a type that conforms to the [`tower::Service`] trait. This type can
88//! then be passed to the the `lambda_runtime::run` function, which launches
99//! and runs the Lambda runtime.
10+ use futures:: FutureExt ;
1011use hyper:: {
1112 client:: { connect:: Connection , HttpConnector } ,
1213 http:: Request ,
@@ -146,10 +147,18 @@ where
146147
147148 let req = match handler. ready ( ) . await {
148149 Ok ( handler) => {
150+ // Catches panics outside of a `Future`
149151 let task =
150152 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+
151160 match task {
152- Ok ( response) => match response. await {
161+ Ok ( Ok ( response) ) => match response {
153162 Ok ( response) => {
154163 trace ! ( "Ok response from handler (run loop)" ) ;
155164 EventCompletionRequest {
@@ -160,7 +169,7 @@ where
160169 }
161170 Err ( err) => build_event_error_request ( request_id, err) ,
162171 } ,
163- Err ( err) => {
172+ Err ( err) | Ok ( Err ( err ) ) => {
164173 error ! ( "{:?}" , err) ;
165174 let error_type = type_name_of_val ( & err) ;
166175 let msg = if let Some ( msg) = err. downcast_ref :: < & str > ( ) {
@@ -261,6 +270,7 @@ mod endpoint_tests {
261270 types:: Diagnostic ,
262271 Error , Runtime ,
263272 } ;
273+ use futures:: future:: BoxFuture ;
264274 use http:: { uri:: PathAndQuery , HeaderValue , Method , Request , Response , StatusCode , Uri } ;
265275 use hyper:: { server:: conn:: Http , service:: service_fn, Body } ;
266276 use lambda_runtime_api_client:: Client ;
@@ -508,4 +518,58 @@ mod endpoint_tests {
508518 Err ( _) => unreachable ! ( "This branch shouldn't be reachable" ) ,
509519 }
510520 }
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+ }
511575}
0 commit comments