|
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) => 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(response) => match response { |
153 | 162 | Ok(response) => {
|
154 | 163 | trace!("Ok response from handler (run loop)");
|
155 | 164 | EventCompletionRequest {
|
@@ -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