Skip to content

Commit 1cacdd8

Browse files
authored
Adding functionality to catch panics that occur both inside the runtime handler and in the Future it returns (#521)
Co-authored-by: = <=>
1 parent 1f8c4da commit 1cacdd8

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

lambda-runtime/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ simulated = []
1818
tokio = { version = "1.0", features = ["macros", "io-util", "sync", "rt-multi-thread"] }
1919
# Hyper requires the `server` feature to work on nightly
2020
hyper = { version = "0.14.20", features = ["http1", "client", "stream", "server"] }
21+
futures = "0.3"
2122
serde = { version = "1", features = ["derive"] }
2223
serde_json = "^1"
2324
bytes = "1.0"

lambda-runtime/src/lib.rs

+65-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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;
1011
use 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) => panic::AssertUnwindSafe(task).catch_unwind().await,
157+
Err(err) => Err(err),
158+
};
159+
151160
match task {
152-
Ok(response) => match response.await {
161+
Ok(response) => match response {
153162
Ok(response) => {
154163
trace!("Ok response from handler (run loop)");
155164
EventCompletionRequest {
@@ -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

Comments
 (0)