From 96329c334a8f8a6a6c68221680ecd5318c0d8624 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 13 Apr 2023 17:58:01 -0700 Subject: [PATCH 1/4] Provide context when deserialization fails. Use serde_path_to_error to provide a little bit more information about the serde failure. Signed-off-by: David Calavera --- lambda-runtime/Cargo.toml | 1 + lambda-runtime/src/deserializer.rs | 40 ++++++++++++++++++++++++++++++ lambda-runtime/src/lib.rs | 8 +++--- lambda-runtime/src/streaming.rs | 11 ++++---- 4 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 lambda-runtime/src/deserializer.rs diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 788b3c89..96506022 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -41,3 +41,4 @@ tracing = { version = "0.1.37", features = ["log"] } tower = { version = "0.4", features = ["util"] } tokio-stream = "0.1.2" lambda_runtime_api_client = { version = "0.8", path = "../lambda-runtime-api-client" } +serde_path_to_error = "0.1.11" diff --git a/lambda-runtime/src/deserializer.rs b/lambda-runtime/src/deserializer.rs new file mode 100644 index 00000000..9b3caecb --- /dev/null +++ b/lambda-runtime/src/deserializer.rs @@ -0,0 +1,40 @@ +use std::{error::Error, fmt}; + +use serde::Deserialize; + +use crate::{Context, LambdaEvent}; + +/// Event payload deserialization error. +/// Returned when the data sent to the function cannot be deserialized +/// into the type that the function receives. +#[derive(Debug)] +pub(crate) struct DeserializeError { + inner: serde_path_to_error::Error, +} + +impl fmt::Display for DeserializeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "failed to deserialize the incoming data into the function payload's type: {}", + self.inner + ) + } +} + +impl Error for DeserializeError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.inner) + } +} + +/// Deserialize the data sent to the function into the type that the function receives. +pub(crate) fn deserialize(body: &[u8], context: Context) -> Result, DeserializeError> +where + T: for<'de> Deserialize<'de>, +{ + let jd = &mut serde_json::Deserializer::from_slice(body); + serde_path_to_error::deserialize(jd) + .map(|payload| LambdaEvent::new(payload, context)) + .map_err(|inner| DeserializeError { inner }) +} diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 31c9297c..86a0848b 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -28,6 +28,7 @@ pub use tower::{self, service_fn, Service}; use tower::{util::ServiceFn, ServiceExt}; use tracing::{error, trace, Instrument}; +mod deserializer; mod requests; #[cfg(test)] mod simulated; @@ -149,8 +150,8 @@ where return Err(parts.status.to_string().into()); } - let body = match serde_json::from_slice(&body) { - Ok(body) => body, + let lambda_event = match deserializer::deserialize(&body, ctx) { + Ok(lambda_event) => lambda_event, Err(err) => { let req = build_event_error_request(request_id, err)?; client.call(req).await.expect("Unable to send response to Runtime APIs"); @@ -161,8 +162,7 @@ where let req = match handler.ready().await { Ok(handler) => { // Catches panics outside of a `Future` - let task = - panic::catch_unwind(panic::AssertUnwindSafe(|| handler.call(LambdaEvent::new(body, ctx)))); + let task = panic::catch_unwind(panic::AssertUnwindSafe(|| handler.call(lambda_event))); let task = match task { // Catches panics inside of the `Future` diff --git a/lambda-runtime/src/streaming.rs b/lambda-runtime/src/streaming.rs index 15cd210f..b9cf978b 100644 --- a/lambda-runtime/src/streaming.rs +++ b/lambda-runtime/src/streaming.rs @@ -1,6 +1,6 @@ use crate::{ - build_event_error_request, incoming, type_name_of_val, Config, Context, Error, EventErrorRequest, IntoRequest, - LambdaEvent, Runtime, + build_event_error_request, deserializer, incoming, type_name_of_val, Config, Context, Error, EventErrorRequest, + IntoRequest, LambdaEvent, Runtime, }; use bytes::Bytes; use futures::FutureExt; @@ -142,8 +142,8 @@ where return Err(parts.status.to_string().into()); } - let body = match serde_json::from_slice(&body) { - Ok(body) => body, + let lambda_event = match deserializer::deserialize(&body, ctx) { + Ok(lambda_event) => lambda_event, Err(err) => { let req = build_event_error_request(request_id, err)?; client.call(req).await.expect("Unable to send response to Runtime APIs"); @@ -154,8 +154,7 @@ where let req = match handler.ready().await { Ok(handler) => { // Catches panics outside of a `Future` - let task = - panic::catch_unwind(panic::AssertUnwindSafe(|| handler.call(LambdaEvent::new(body, ctx)))); + let task = panic::catch_unwind(panic::AssertUnwindSafe(|| handler.call(lambda_event))); let task = match task { // Catches panics inside of the `Future` From 526ebab8f8842a17cb127f4bbee33c245d9e2a17 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 13 Apr 2023 18:40:56 -0700 Subject: [PATCH 2/4] Include the error path in the message when the problem is not the root element "." Signed-off-by: David Calavera --- lambda-runtime/src/deserializer.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lambda-runtime/src/deserializer.rs b/lambda-runtime/src/deserializer.rs index 9b3caecb..5d874b9d 100644 --- a/lambda-runtime/src/deserializer.rs +++ b/lambda-runtime/src/deserializer.rs @@ -14,11 +14,20 @@ pub(crate) struct DeserializeError { impl fmt::Display for DeserializeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!( - f, - "failed to deserialize the incoming data into the function payload's type: {}", - self.inner - ) + let path = self.inner.path().to_string(); + if path == "." { + writeln!( + f, + "failed to deserialize the incoming data into the function payload's type: {}", + self.inner + ) + } else { + writeln!( + f, + "failed to deserialize the incoming data into the function payload's type: [{}] {}", + path, self.inner + ) + } } } From cfefec580e593e1202039d767ddd0c2111b89c2a Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 13 Apr 2023 19:04:44 -0700 Subject: [PATCH 3/4] Cleanup formatting Signed-off-by: David Calavera --- lambda-runtime/src/deserializer.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lambda-runtime/src/deserializer.rs b/lambda-runtime/src/deserializer.rs index 5d874b9d..a227ed11 100644 --- a/lambda-runtime/src/deserializer.rs +++ b/lambda-runtime/src/deserializer.rs @@ -4,6 +4,8 @@ use serde::Deserialize; use crate::{Context, LambdaEvent}; +const ERROR_CONTEXT: &str = "failed to deserialize the incoming data into the function payload's type"; + /// Event payload deserialization error. /// Returned when the data sent to the function cannot be deserialized /// into the type that the function receives. @@ -16,17 +18,9 @@ impl fmt::Display for DeserializeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let path = self.inner.path().to_string(); if path == "." { - writeln!( - f, - "failed to deserialize the incoming data into the function payload's type: {}", - self.inner - ) + writeln!(f, "{ERROR_CONTEXT}: {}", self.inner) } else { - writeln!( - f, - "failed to deserialize the incoming data into the function payload's type: [{}] {}", - path, self.inner - ) + writeln!(f, "{ERROR_CONTEXT}: [{path}] {}", self.inner) } } } From 8fb0e71a337a035b1d6e0c9f13b22fb0b92125ea Mon Sep 17 00:00:00 2001 From: David Calavera Date: Thu, 13 Apr 2023 20:15:40 -0700 Subject: [PATCH 4/4] Fix typo Signed-off-by: David Calavera --- lambda-runtime/src/deserializer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda-runtime/src/deserializer.rs b/lambda-runtime/src/deserializer.rs index a227ed11..1841c050 100644 --- a/lambda-runtime/src/deserializer.rs +++ b/lambda-runtime/src/deserializer.rs @@ -4,7 +4,7 @@ use serde::Deserialize; use crate::{Context, LambdaEvent}; -const ERROR_CONTEXT: &str = "failed to deserialize the incoming data into the function payload's type"; +const ERROR_CONTEXT: &str = "failed to deserialize the incoming data into the function's payload type"; /// Event payload deserialization error. /// Returned when the data sent to the function cannot be deserialized