From a77abb1a0df8512120a5cb7875ec1c514ece227d Mon Sep 17 00:00:00 2001 From: sapessi Date: Sun, 2 Dec 2018 15:50:25 -0800 Subject: [PATCH 01/43] Fixed typo for UNHANDLED error constant --- lambda-runtime-client/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 5a2bc573..13fc0531 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -13,7 +13,7 @@ use serde_json; pub const ERROR_TYPE_HANDLED: &str = "Handled"; /// Error type description for the `ErrorResponse` event. This type is used for unhandled, /// unexpcted errors. -pub const ERROR_TYPE_UNHANDLED: &str = "Handled"; +pub const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; /// This object is used to generate requests to the Lambda Runtime APIs. /// It is used for both the error response APIs and fail init calls. From 36bcc8fbb41a2bf9276b696b021e0dd5577a3fb4 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 10:00:52 -0800 Subject: [PATCH 02/43] Removed RuntimeApiError trait form the client and changed it to receive ErrorReponse directly --- lambda-runtime-client/src/client.rs | 18 +++++++++--------- lambda-runtime-client/src/error.rs | 29 ++++++++++++----------------- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 95471dc2..2aa3cc49 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,4 +1,4 @@ -use error::{ApiError, ErrorResponse, RuntimeApiError}; +use error::{ApiError, ErrorResponse}; use hyper::{ client::HttpConnector, header::{self, HeaderMap, HeaderValue}, @@ -250,7 +250,7 @@ impl RuntimeClient { /// /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. - pub fn event_error(&self, request_id: &str, e: &RuntimeApiError) -> Result<(), ApiError> { + pub fn event_error(&self, request_id: &str, e: ErrorResponse) -> Result<(), ApiError> { let uri: Uri = format!( "http://{}/{}/runtime/invocation/{}/error", self.endpoint, RUNTIME_API_VERSION, request_id @@ -259,9 +259,9 @@ impl RuntimeClient { trace!( "Posting error to runtime API for request {}: {}", request_id, - e.to_response().error_message + e.error_message ); - let req = self.get_runtime_error_request(&uri, &e.to_response()); + let req = self.get_runtime_error_request(&uri, e); match self.http_client.request(req).wait() { Ok(resp) => { @@ -297,12 +297,12 @@ impl RuntimeClient { /// # Panics /// If it cannot send the init error. In this case we panic to force the runtime /// to restart. - pub fn fail_init(&self, e: &RuntimeApiError) { + pub fn fail_init(&self, e: ErrorResponse) { let uri: Uri = format!("http://{}/{}/runtime/init/error", self.endpoint, RUNTIME_API_VERSION) .parse() .expect("Could not generate Runtime URI"); - error!("Calling fail_init Runtime API: {}", e.to_response().error_message); - let req = self.get_runtime_error_request(&uri, &e.to_response()); + error!("Calling fail_init Runtime API: {}", e.error_message); + let req = self.get_runtime_error_request(&uri, e); self.http_client .request(req) @@ -343,8 +343,8 @@ impl RuntimeClient { .unwrap() } - fn get_runtime_error_request(&self, uri: &Uri, e: &ErrorResponse) -> Request { - let body = serde_json::to_vec(e).expect("Could not turn error object into response JSON"); + fn get_runtime_error_request(&self, uri: &Uri, e: ErrorResponse) -> Request { + let body = serde_json::to_vec(&e).expect("Could not turn error object into response JSON"); Request::builder() .method(Method::POST) .uri(uri.clone()) diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 13fc0531..086e9b8a 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -69,16 +69,10 @@ impl ErrorResponse { } } -/// Custom errors for the framework should implement this trait. The client calls -/// the `to_response()` method automatically to produce an object that can be serialized -/// and sent to the Lambda Runtime APIs. -pub trait RuntimeApiError { - /// Creates a `RuntimeError` object for the current error. This is - /// then serialized and sent to the Lambda runtime APIs. - /// - /// # Returns - /// A populated `RuntimeError` object. - fn to_response(&self) -> ErrorResponse; +impl From> for ErrorResponse { + fn from(e: Box) -> Self { + Self::handled(e.description().to_owned()) + } } /// Represents an error generated by the Lambda Runtime API client. @@ -171,13 +165,14 @@ impl From for ApiError { } } -impl RuntimeApiError for ApiError { - fn to_response(&self) -> ErrorResponse { - let backtrace = format!("{:?}", self.backtrace); - let trace_vec = backtrace.lines().map(|s| s.to_string()).collect::>(); - let mut err = ErrorResponse::unhandled(self.msg.clone()); - err.stack_trace = Option::from(trace_vec); - +impl Into for ApiError { + fn into(self) -> ErrorResponse { + let mut err = ErrorResponse::unhandled(self.description().to_owned()); + if self.backtrace.is_some() { + let backtrace = format!("{:?}", self.backtrace); + let trace_vec = backtrace.lines().map(|s| s.to_string()).collect::>(); + err.stack_trace = Option::from(trace_vec); + } err } } From f53bd302774a90306e29f62e876dd1a10f60a4eb Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 10:01:40 -0800 Subject: [PATCH 03/43] Changed HandlerError type to alias Box to allow the runtime to handle any error type --- lambda-runtime/src/context.rs | 18 -------- lambda-runtime/src/error.rs | 85 +++++++---------------------------- lambda-runtime/src/runtime.rs | 17 ++++--- 3 files changed, 25 insertions(+), 95 deletions(-) diff --git a/lambda-runtime/src/context.rs b/lambda-runtime/src/context.rs index 88e699a6..75ea16e3 100644 --- a/lambda-runtime/src/context.rs +++ b/lambda-runtime/src/context.rs @@ -1,10 +1,6 @@ -use std::env; - use chrono::Utc; -use backtrace; use env as lambda_env; -use error::HandlerError; use lambda_runtime_client; /// The Lambda function execution context. The values in this struct @@ -86,20 +82,6 @@ impl Context { } } - /// We use the context for each event to store the stack trace. This is the methods - /// clients should use to retrieve an initialized `RuntimeError` with the populated - /// stack trace. - pub fn new_error(&self, msg: &str) -> HandlerError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } - HandlerError::new(msg, trace) - } - /// Returns the remaining time in the execution in milliseconds. This is based on the /// deadline header passed by Lambda's Runtime APIs. pub fn get_time_remaining_millis(&self) -> u128 { diff --git a/lambda-runtime/src/error.rs b/lambda-runtime/src/error.rs index 2e00dcf4..a8eeed8e 100644 --- a/lambda-runtime/src/error.rs +++ b/lambda-runtime/src/error.rs @@ -3,9 +3,12 @@ use std::{env, error::Error, fmt}; use backtrace; -use lambda_runtime_client::error; +use lambda_runtime_client::error::{ApiError, ErrorResponse}; use serde_json; +/// Abstration for the handler error +pub type HandlerError = Box; + /// The `RuntimeError` object is returned by the custom runtime as it polls /// for new events and tries to execute the handler function. The error /// is primarily used by other methods within this crate and should not be relevant @@ -14,7 +17,7 @@ use serde_json; #[derive(Debug, Clone)] pub struct RuntimeError { msg: String, - stack_trace: Option, + backtrace: Option, /// The request id that generated this error pub(crate) request_id: Option, /// Whether the error is recoverable or not. @@ -58,21 +61,22 @@ impl RuntimeError { } RuntimeError { msg: String::from(msg), - stack_trace: trace, + backtrace: trace, recoverable: true, request_id: None, } } } -impl error::RuntimeApiError for RuntimeError { - fn to_response(&self) -> error::ErrorResponse { - let backtrace = format!("{:?}", self.stack_trace); - error::ErrorResponse { - error_message: String::from(self.description()), - error_type: String::from(error::ERROR_TYPE_HANDLED), - stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::>()), +impl Into for RuntimeError { + fn into(self) -> ErrorResponse { + let mut err = ErrorResponse::unhandled(self.description().to_owned()); + if self.backtrace.is_some() { + let backtrace = format!("{:?}", self.backtrace); + let trace_vec = backtrace.lines().map(|s| s.to_string()).collect::>(); + err.stack_trace = Option::from(trace_vec); } + err } } @@ -106,66 +110,11 @@ impl From for RuntimeError { } } -impl From for RuntimeError { - fn from(e: error::ApiError) -> Self { +impl From for RuntimeError { + fn from(e: ApiError) -> Self { let mut err = RuntimeError::new(e.description()); err.recoverable = e.recoverable; - err.stack_trace = e.backtrace; + err.backtrace = e.backtrace; err } } - -/// The error type for functions that are used as the `Handler` type. New errors -/// should be instantiated using the `new_error()` method of the `runtime::Context` -/// object passed to the handler function. -#[derive(Debug, Clone)] -pub struct HandlerError { - msg: String, - backtrace: Option, -} - -impl fmt::Display for HandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg) - } -} - -// This is important for other errors to wrap this one. -impl Error for HandlerError { - fn description(&self) -> &str { - &self.msg - } - - fn cause(&self) -> Option<&Error> { - // Generic error, underlying cause isn't tracked. - None - } -} - -impl HandlerError { - /// Creates a new handler error. This method is used by the `new_error()` method - /// of the `runtime::Context` object. - /// - /// # Arguments - /// - /// * `msg` The error message for the new error - /// * `trace` A `Backtrace` object to generate the stack trace for the error - /// response. This is provided by the `Context` object. - pub(crate) fn new(msg: &str, trace: Option) -> HandlerError { - HandlerError { - msg: msg.to_string(), - backtrace: trace, - } - } -} - -impl error::RuntimeApiError for HandlerError { - fn to_response(&self) -> error::ErrorResponse { - let backtrace = format!("{:?}", self.backtrace); - error::ErrorResponse { - error_message: String::from(self.description()), - error_type: String::from(error::ERROR_TYPE_HANDLED), - stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::>()), - } - } -} diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime/src/runtime.rs index 770788f4..07979df5 100644 --- a/lambda-runtime/src/runtime.rs +++ b/lambda-runtime/src/runtime.rs @@ -1,4 +1,4 @@ -use std::{error::Error, marker::PhantomData, result}; +use std::{marker::PhantomData, result}; use serde; use serde_json; @@ -6,7 +6,7 @@ use serde_json; use context::Context; use env::{ConfigProvider, EnvConfigProvider, FunctionSettings}; use error::{HandlerError, RuntimeError}; -use lambda_runtime_client::RuntimeClient; +use lambda_runtime_client::{error::ErrorResponse, RuntimeClient}; use tokio::runtime::Runtime as TokioRuntime; const MAX_RETRIES: i8 = 3; @@ -215,7 +215,7 @@ where "Error for {} is not recoverable, sending fail_init signal and panicking.", request_id ); - self.runtime_client.fail_init(&e); + self.runtime_client.fail_init(ErrorResponse::from(Box::from(e))); panic!("Could not send response"); } } @@ -226,8 +226,7 @@ where "Could not marshal output object to Vec JSON represnetation for request {}: {}", request_id, e ); - self.runtime_client - .fail_init(&RuntimeError::unrecoverable(e.description())); + self.runtime_client.fail_init(ErrorResponse::from(Box::from(e))); panic!("Failed to marshal handler output, panic"); } } @@ -235,7 +234,7 @@ where Err(e) => { debug!("Handler returned an error for {}: {}", request_id, e); debug!("Attempting to send error response to Runtime API for {}", request_id); - match self.runtime_client.event_error(&request_id, &e) { + match self.runtime_client.event_error(&request_id, ErrorResponse::from(e)) { Ok(_) => info!("Error response for {} accepted by Runtime API", request_id), Err(e) => { error!("Unable to send error response for {} to Runtime API: {}", request_id, e); @@ -244,7 +243,7 @@ where "Error for {} is not recoverable, sending fail_init signal and panicking", request_id ); - self.runtime_client.fail_init(&e); + self.runtime_client.fail_init(ErrorResponse::from(Box::from(e))); panic!("Could not send error response"); } } @@ -272,11 +271,11 @@ where match err.request_id.clone() { Some(req_id) => { self.runtime_client - .event_error(&req_id, &err) + .event_error(&req_id, ErrorResponse::from(Box::from(err))) .expect("Could not send event error response"); } None => { - self.runtime_client.fail_init(&err); + self.runtime_client.fail_init(ErrorResponse::from(Box::from(err))); } } From 8b6d8fc01cbfaa1736707aaa7ffdaa776d3eb17b Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 10:02:17 -0800 Subject: [PATCH 04/43] Added custom error examples and updated existing examples --- Cargo.lock | 9 +++ lambda-runtime/Cargo.toml | 3 + lambda-runtime/examples/basic.rs | 4 +- lambda-runtime/examples/custom_error.rs | 70 +++++++++++++++++++ .../examples/custom_error_failure.rs | 47 +++++++++++++ .../examples/with_custom_runtime.rs | 4 +- 6 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 lambda-runtime/examples/custom_error.rs create mode 100644 lambda-runtime/examples/custom_error_failure.rs diff --git a/Cargo.lock b/Cargo.lock index 2bb0f9f3..4a6e4ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -411,6 +411,8 @@ version = "0.1.0" dependencies = [ "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime_client 0.1.0", @@ -420,6 +422,7 @@ dependencies = [ "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "simple-error 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -909,6 +912,11 @@ dependencies = [ "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "simple-error" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "simple_logger" version = "1.0.1" @@ -1377,6 +1385,7 @@ dependencies = [ "checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" "checksum serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "bb47a3d5c84320222f66d7db21157c4a7407755de41798f9b4c1c40593397b1a" "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" +"checksum simple-error 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "01c1c2ededd95f93b1d65e7f8b5b17670e926bf9cbb55f8b91b26b0bd40d3259" "checksum simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25111f1d77db1ac3ee11b62ba4b7a162e6bb3be43e28273f0d3935cc8d3ff7fb" "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 5acd7778..59167d47 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -23,5 +23,8 @@ simple_logger = "^1" [dev-dependencies] hyper-tls = "^0.3" +simple-error = "^0.1" +failure = "^0.1" +failure_derive = "^0.1" rusoto_core = "^0.35" rusoto_dynamodb = "^0.35" diff --git a/lambda-runtime/examples/basic.rs b/lambda-runtime/examples/basic.rs index 83736a45..c1c01a4b 100644 --- a/lambda-runtime/examples/basic.rs +++ b/lambda-runtime/examples/basic.rs @@ -1,6 +1,8 @@ extern crate lambda_runtime as lambda; extern crate log; extern crate serde_derive; +#[macro_use] +extern crate simple_error; extern crate simple_logger; use lambda::{error::HandlerError, lambda}; @@ -29,7 +31,7 @@ fn main() -> Result<(), Box> { fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(c.new_error("Empty first name")); + bail!("Empty first name"); } Ok(CustomOutput { diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs new file mode 100644 index 00000000..54377c06 --- /dev/null +++ b/lambda-runtime/examples/custom_error.rs @@ -0,0 +1,70 @@ +extern crate lambda_runtime as lambda; +extern crate log; +extern crate serde_derive; +extern crate simple_logger; + +use lambda::{error::HandlerError, lambda}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use std::{error::Error, fmt}; + +#[derive(Debug)] +struct CustomError { + msg: String, +} + +impl fmt::Display for CustomError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} + +impl Error for CustomError {} + +impl CustomError { + fn new(message: &str) -> CustomError { + CustomError { + msg: message.to_owned(), + } + } + + fn boxed(self) -> Box { + Box::from(self) + } +} + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + // in this case, we explicitly initialize and box our custom error type. + // the HandlerError type is an alias to Box/ + return Err(CustomError::new("Empty first name").boxed()); + } + + // For errors simply want to return, because the HandlerError is an alias to any + // generic error type, we can propapgate with the standard "?" syntax. + let _age_num: u8 = e.age.parse()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs new file mode 100644 index 00000000..3464b79b --- /dev/null +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -0,0 +1,47 @@ +extern crate lambda_runtime as lambda; +extern crate log; +extern crate serde_derive; +extern crate simple_logger; +#[macro_use] +extern crate failure; + +use lambda::{error::HandlerError, lambda}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use failure::Error; + +#[derive(Fail, Debug)] +#[fail(display = "Custom Error")] +struct CustomError; + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + return Err(CustomError{}); + } + + let _age_num: u8 = e.age.parse()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/examples/with_custom_runtime.rs b/lambda-runtime/examples/with_custom_runtime.rs index 599e0556..48101ddf 100644 --- a/lambda-runtime/examples/with_custom_runtime.rs +++ b/lambda-runtime/examples/with_custom_runtime.rs @@ -2,6 +2,8 @@ extern crate lambda_runtime as lambda; extern crate log; extern crate serde_derive; extern crate simple_logger; +#[macro_use] +extern crate simple_error; extern crate tokio; use lambda::{error::HandlerError, lambda}; @@ -33,7 +35,7 @@ fn main() -> Result<(), Box> { fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(c.new_error("Empty first name")); + bail!("Empty first name"); } Ok(CustomOutput { From 2e034d280eeaadd8b9b2903ba9dbefe23f84b700 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 10:20:11 -0800 Subject: [PATCH 05/43] Fixed failure example --- lambda-runtime/examples/custom_error_failure.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index 3464b79b..b625c64c 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -5,9 +5,10 @@ extern crate simple_logger; #[macro_use] extern crate failure; -use lambda::{error::HandlerError, lambda}; +use lambda::lambda; use log::error; use serde_derive::{Deserialize, Serialize}; +use std::error::Error as StdError; use failure::Error; #[derive(Fail, Debug)] @@ -26,7 +27,7 @@ struct CustomOutput { message: String, } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { simple_logger::init_with_level(log::Level::Debug).unwrap(); lambda!(my_handler); From 4f20464f72cc78feb9a8c6b7f2de5c4934c0e9c1 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 10:25:02 -0800 Subject: [PATCH 06/43] Updated comment on HandlerError type --- lambda-runtime/src/error.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lambda-runtime/src/error.rs b/lambda-runtime/src/error.rs index a8eeed8e..2dd1c4ff 100644 --- a/lambda-runtime/src/error.rs +++ b/lambda-runtime/src/error.rs @@ -6,7 +6,10 @@ use backtrace; use lambda_runtime_client::error::{ApiError, ErrorResponse}; use serde_json; -/// Abstration for the handler error +/// The `HandlerError` type can be use to abstract any error in the handler method. +/// This allows handler functions to return any error using the `?` syntax. For example +/// `let _age_num: u8 = e.age.parse()?;` will return the `::Err` from the +/// handler function. pub type HandlerError = Box; /// The `RuntimeError` object is returned by the custom runtime as it polls From d17b7c8cdde793a0bf187eddc52521ff5f36d1be Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 10:25:32 -0800 Subject: [PATCH 07/43] Exported HandlerError in root of the crate --- lambda-runtime/examples/basic.rs | 2 +- lambda-runtime/examples/custom_error.rs | 2 +- lambda-runtime/examples/with_custom_runtime.rs | 2 +- lambda-runtime/src/lib.rs | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lambda-runtime/examples/basic.rs b/lambda-runtime/examples/basic.rs index c1c01a4b..879a5675 100644 --- a/lambda-runtime/examples/basic.rs +++ b/lambda-runtime/examples/basic.rs @@ -5,7 +5,7 @@ extern crate serde_derive; extern crate simple_error; extern crate simple_logger; -use lambda::{error::HandlerError, lambda}; +use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error; diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs index 54377c06..1eee9157 100644 --- a/lambda-runtime/examples/custom_error.rs +++ b/lambda-runtime/examples/custom_error.rs @@ -3,7 +3,7 @@ extern crate log; extern crate serde_derive; extern crate simple_logger; -use lambda::{error::HandlerError, lambda}; +use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::{error::Error, fmt}; diff --git a/lambda-runtime/examples/with_custom_runtime.rs b/lambda-runtime/examples/with_custom_runtime.rs index 48101ddf..666f56ec 100644 --- a/lambda-runtime/examples/with_custom_runtime.rs +++ b/lambda-runtime/examples/with_custom_runtime.rs @@ -6,7 +6,7 @@ extern crate simple_logger; extern crate simple_error; extern crate tokio; -use lambda::{error::HandlerError, lambda}; +use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error; diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 36448d00..0c5b1ea3 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -51,8 +51,9 @@ extern crate tokio; mod context; mod env; -pub mod error; +mod error; mod runtime; pub use context::*; +pub use error::HandlerError; pub use runtime::*; From 08772e9046260755acc21c0e49a2c2915c5842d6 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 11:31:33 -0800 Subject: [PATCH 08/43] Added error code to response structure and centralized backtrace generation to the new() function on the ErrorResponse --- lambda-runtime-client/src/client.rs | 2 +- lambda-runtime-client/src/error.rs | 72 ++++++++++++++++------------- lambda-runtime/src/error.rs | 25 +--------- 3 files changed, 41 insertions(+), 58 deletions(-) diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 2aa3cc49..0b89e990 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -352,7 +352,7 @@ impl RuntimeClient { header::CONTENT_TYPE, header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) - .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static("RuntimeError")) // TODO: We should add this code to the error object. + .header(RUNTIME_ERROR_HEADER, e.error_code) .body(Body::from(body)) .unwrap() } diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 086e9b8a..0c92d12f 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -21,6 +21,10 @@ pub const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; /// this object to be compatible with the APIs. #[derive(Serialize)] pub struct ErrorResponse { + /// Internal error code to be sent to the Lambda Runtime APIs in the + /// `Lambda-Runtime-Function-Error-Type` header. + #[serde(skip)] + pub error_code: String, /// The error message generated by the application. #[serde(rename = "errorMessage")] pub error_message: String, @@ -36,6 +40,39 @@ pub struct ErrorResponse { } impl ErrorResponse { + /// Creates a new instance of the `ErrorResponse` object with the given parameters. If the + /// `RUST_BACKTRACE` env variable is `1` the `ErrorResponse` is populated with the backtrace + /// collected through the [`backtrace` craete](https://crates.io/crates/backtrace). + /// + /// # Arguments + /// + /// * `message` The error message to be returned to the APIs. Normally the error description() + /// * `err_type` The error type. Use the `ERROR_TYPE_HANDLED` and `ERROR_TYPE_UNHANDLED`. + /// * `code` A custom error code + /// + /// # Return + /// A new instance of the `ErrorResponse` object. + fn new(message: String, err_type: String, code: String) -> ErrorResponse { + let mut err = ErrorResponse { + error_message: message, + error_type: err_type, + error_code: code, + stack_trace: Option::default(), + }; + let is_backtrace = env::var("RUST_BACKTRACE"); + if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { + trace!("Begin backtrace collection"); + let trace = Option::from(backtrace::Backtrace::new()); + let trace_string = format!("{:?}", trace) + .lines() + .map(|s| s.to_string()) + .collect::>(); + trace!("Completed backtrace collection"); + err.stack_trace = Option::from(trace_string); + } + err + } + /// Creates a new `RuntimeError` object with the handled error type. /// /// # Arguments @@ -45,11 +82,7 @@ impl ErrorResponse { /// # Return /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. pub fn handled(message: String) -> ErrorResponse { - ErrorResponse { - error_message: message, - error_type: String::from(ERROR_TYPE_HANDLED), - stack_trace: Option::default(), - } + ErrorResponse::new(message, ERROR_TYPE_HANDLED.to_owned(), "RuntimeError".to_owned()) } /// Creates a new `RuntimeError` object with the unhandled error type. @@ -61,11 +94,7 @@ impl ErrorResponse { /// # Return /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. pub fn unhandled(message: String) -> ErrorResponse { - ErrorResponse { - error_message: message, - error_type: String::from(ERROR_TYPE_UNHANDLED), - stack_trace: Option::default(), - } + ErrorResponse::new(message, ERROR_TYPE_UNHANDLED.to_owned(), "RuntimeError".to_owned()) } } @@ -79,9 +108,6 @@ impl From> for ErrorResponse { #[derive(Debug, Clone)] pub struct ApiError { msg: String, - /// The `Backtrace` object from the `backtrace` crate used to store - /// the stack trace of the error. - pub backtrace: Option, /// Whether the current error is recoverable. If the error is not /// recoverable a runtime should panic to force the Lambda service /// to restart the execution environment. @@ -90,16 +116,8 @@ pub struct ApiError { impl ApiError { pub(crate) fn new(description: &str) -> ApiError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } ApiError { msg: String::from(description), - backtrace: trace, recoverable: true, } } @@ -164,15 +182,3 @@ impl From for ApiError { ApiError::new(e.description()) } } - -impl Into for ApiError { - fn into(self) -> ErrorResponse { - let mut err = ErrorResponse::unhandled(self.description().to_owned()); - if self.backtrace.is_some() { - let backtrace = format!("{:?}", self.backtrace); - let trace_vec = backtrace.lines().map(|s| s.to_string()).collect::>(); - err.stack_trace = Option::from(trace_vec); - } - err - } -} diff --git a/lambda-runtime/src/error.rs b/lambda-runtime/src/error.rs index 2dd1c4ff..9af05745 100644 --- a/lambda-runtime/src/error.rs +++ b/lambda-runtime/src/error.rs @@ -2,8 +2,7 @@ //! by custom handlers as well as the runtime itself. use std::{env, error::Error, fmt}; -use backtrace; -use lambda_runtime_client::error::{ApiError, ErrorResponse}; +use lambda_runtime_client::error::ApiError; use serde_json; /// The `HandlerError` type can be use to abstract any error in the handler method. @@ -20,7 +19,6 @@ pub type HandlerError = Box; #[derive(Debug, Clone)] pub struct RuntimeError { msg: String, - backtrace: Option, /// The request id that generated this error pub(crate) request_id: Option, /// Whether the error is recoverable or not. @@ -55,34 +53,14 @@ impl RuntimeError { /// # Returns /// A new `RuntimeError` instance. pub(crate) fn new(msg: &str) -> RuntimeError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } RuntimeError { msg: String::from(msg), - backtrace: trace, recoverable: true, request_id: None, } } } -impl Into for RuntimeError { - fn into(self) -> ErrorResponse { - let mut err = ErrorResponse::unhandled(self.description().to_owned()); - if self.backtrace.is_some() { - let backtrace = format!("{:?}", self.backtrace); - let trace_vec = backtrace.lines().map(|s| s.to_string()).collect::>(); - err.stack_trace = Option::from(trace_vec); - } - err - } -} - impl fmt::Display for RuntimeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.msg) @@ -117,7 +95,6 @@ impl From for RuntimeError { fn from(e: ApiError) -> Self { let mut err = RuntimeError::new(e.description()); err.recoverable = e.recoverable; - err.backtrace = e.backtrace; err } } From a6d2c5b8668d68fd388774c22d62baa961f6d579 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 11:38:17 -0800 Subject: [PATCH 09/43] Updated example in lib docs --- lambda-runtime/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 0c5b1ea3..beeb291e 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -11,8 +11,10 @@ //! extern crate serde_derive; //! #[macro_use] //! extern crate lambda_runtime; +//! #[macro_use] +//! extern crate simple_error; //! -//! use lambda_runtime::error::HandlerError; +//! use lambda_runtime::HandlerError; //! //! //! #[derive(Deserialize, Clone)] @@ -32,7 +34,7 @@ //! //! fn my_handler(e: CustomEvent, ctx: lambda_runtime::Context) -> Result { //! if e.first_name == "" { -//! return Err(ctx.new_error("Missing first name!")); +//! bail!("Empty first name"); //! } //! Ok(CustomOutput{ //! message: format!("Hello, {}!", e.first_name), From fee02e84df6a3d86848d5b79c2a03161c5354cc1 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 11:38:35 -0800 Subject: [PATCH 10/43] Temporary fix for failure example --- lambda-runtime/examples/custom_error_failure.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index b625c64c..c7a66bb6 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -5,11 +5,11 @@ extern crate simple_logger; #[macro_use] extern crate failure; -use lambda::lambda; +use failure::Error; +use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error as StdError; -use failure::Error; #[derive(Fail, Debug)] #[fail(display = "Custom Error")] @@ -34,10 +34,10 @@ fn main() -> Result<(), Box> { Ok(()) } -fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { +fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(CustomError{}); + return Err(Box::from(Error::from(CustomError {}).compat())); } let _age_num: u8 = e.age.parse()?; From 4969624e8d6920ddb0f8f1168ba8b78fe93f5c7b Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 3 Dec 2018 21:16:11 -0800 Subject: [PATCH 11/43] Switched to HandlerError struct that can be created from any Display + Send + Sync + Debug. Updated samples to reflect this --- lambda-runtime-client/src/error.rs | 17 +++++++--- lambda-runtime/examples/custom_error.rs | 6 +--- .../examples/custom_error_failure.rs | 3 +- lambda-runtime/src/error.rs | 31 +++++++++++++++---- lambda-runtime/src/runtime.rs | 17 +++++----- 5 files changed, 50 insertions(+), 24 deletions(-) diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 0c92d12f..422e6179 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -1,7 +1,14 @@ //! This module defines the `RuntimeApiError` trait that developers should implement //! to send their custom errors to the AWS Lambda Runtime Client SDK. The module also //! defines the `ApiError` type returned by the `RuntimeClient` implementations. -use std::{env, error::Error, fmt, io, num::ParseIntError, option::Option}; +use std::{ + env, + error::Error, + fmt::{self, Display}, + io, + num::ParseIntError, + option::Option, +}; use backtrace; use http::{header::ToStrError, uri::InvalidUri}; @@ -98,9 +105,9 @@ impl ErrorResponse { } } -impl From> for ErrorResponse { - fn from(e: Box) -> Self { - Self::handled(e.description().to_owned()) +impl From> for ErrorResponse { + fn from(e: Box) -> Self { + Self::handled(format!("{}", e)) } } @@ -146,6 +153,8 @@ impl Error for ApiError { None } } +unsafe impl Send for ApiError {} +unsafe impl Sync for ApiError {} impl From for ApiError { fn from(e: serde_json::Error) -> Self { diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs index 1eee9157..d9e18783 100644 --- a/lambda-runtime/examples/custom_error.rs +++ b/lambda-runtime/examples/custom_error.rs @@ -27,10 +27,6 @@ impl CustomError { msg: message.to_owned(), } } - - fn boxed(self) -> Box { - Box::from(self) - } } #[derive(Deserialize)] @@ -57,7 +53,7 @@ fn my_handler(e: CustomEvent, c: lambda::Context) -> Result/ - return Err(CustomError::new("Empty first name").boxed()); + return Err(CustomError::new("Empty first name").into()); } // For errors simply want to return, because the HandlerError is an alias to any diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index c7a66bb6..f9bfa164 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -5,7 +5,6 @@ extern crate simple_logger; #[macro_use] extern crate failure; -use failure::Error; use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; @@ -37,7 +36,7 @@ fn main() -> Result<(), Box> { fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(Box::from(Error::from(CustomError {}).compat())); + return Err((CustomError {}).into()); } let _age_num: u8 = e.age.parse()?; diff --git a/lambda-runtime/src/error.rs b/lambda-runtime/src/error.rs index 9af05745..ebbcd504 100644 --- a/lambda-runtime/src/error.rs +++ b/lambda-runtime/src/error.rs @@ -1,15 +1,34 @@ //! The error module defines the error types that can be returned //! by custom handlers as well as the runtime itself. -use std::{env, error::Error, fmt}; +use std::{ + env, + error::Error, + fmt::{self, Debug, Display}, +}; use lambda_runtime_client::error::ApiError; use serde_json; -/// The `HandlerError` type can be use to abstract any error in the handler method. -/// This allows handler functions to return any error using the `?` syntax. For example -/// `let _age_num: u8 = e.age.parse()?;` will return the `::Err` from the -/// handler function. -pub type HandlerError = Box; +/// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`. +/// The `HandlerError` object can be generated `From` any object that supports `Display`, +/// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using +/// the `?` syntax. For example `let _age_num: u8 = e.age.parse()?;` will return the +/// `::Err` from the handler function. +pub struct HandlerError { + msg: String, +} + +impl From for HandlerError { + fn from(e: E) -> HandlerError { + HandlerError { msg: format!("{}", e) } + } +} + +impl Display for HandlerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} /// The `RuntimeError` object is returned by the custom runtime as it polls /// for new events and tries to execute the handler function. The error diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime/src/runtime.rs index 07979df5..6b56a690 100644 --- a/lambda-runtime/src/runtime.rs +++ b/lambda-runtime/src/runtime.rs @@ -215,7 +215,7 @@ where "Error for {} is not recoverable, sending fail_init signal and panicking.", request_id ); - self.runtime_client.fail_init(ErrorResponse::from(Box::from(e))); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); panic!("Could not send response"); } } @@ -226,7 +226,7 @@ where "Could not marshal output object to Vec JSON represnetation for request {}: {}", request_id, e ); - self.runtime_client.fail_init(ErrorResponse::from(Box::from(e))); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); panic!("Failed to marshal handler output, panic"); } } @@ -234,7 +234,10 @@ where Err(e) => { debug!("Handler returned an error for {}: {}", request_id, e); debug!("Attempting to send error response to Runtime API for {}", request_id); - match self.runtime_client.event_error(&request_id, ErrorResponse::from(e)) { + match self + .runtime_client + .event_error(&request_id, ErrorResponse::from(Box::new(e))) + { Ok(_) => info!("Error response for {} accepted by Runtime API", request_id), Err(e) => { error!("Unable to send error response for {} to Runtime API: {}", request_id, e); @@ -243,7 +246,7 @@ where "Error for {} is not recoverable, sending fail_init signal and panicking", request_id ); - self.runtime_client.fail_init(ErrorResponse::from(Box::from(e))); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); panic!("Could not send error response"); } } @@ -271,11 +274,11 @@ where match err.request_id.clone() { Some(req_id) => { self.runtime_client - .event_error(&req_id, ErrorResponse::from(Box::from(err))) + .event_error(&req_id, ErrorResponse::from(Box::new(err))) .expect("Could not send event error response"); } None => { - self.runtime_client.fail_init(ErrorResponse::from(Box::from(err))); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(err))); } } @@ -355,7 +358,7 @@ pub(crate) mod tests { "Handler threw an unexpected error: {}", output.err().unwrap() ); - let output_string = output.unwrap(); + let output_string = output.ok().unwrap(); assert_eq!(output_string, "hello", "Unexpected output message: {}", output_string); } } From 0bce533f72dda9d13bf039d783c1dc3ae711c558 Mon Sep 17 00:00:00 2001 From: sapessi Date: Tue, 4 Dec 2018 09:47:45 -0800 Subject: [PATCH 12/43] Addressed code review changes --- Cargo.lock | 1 - lambda-runtime/Cargo.toml | 3 +-- lambda-runtime/examples/basic.rs | 2 +- lambda-runtime/examples/custom_error_failure.rs | 5 +++-- lambda-runtime/src/lib.rs | 8 +++----- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a6e4ae1..7c3ed972 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,7 +412,6 @@ dependencies = [ "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime_client 0.1.0", diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 59167d47..d2fb6e72 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -25,6 +25,5 @@ simple_logger = "^1" hyper-tls = "^0.3" simple-error = "^0.1" failure = "^0.1" -failure_derive = "^0.1" rusoto_core = "^0.35" -rusoto_dynamodb = "^0.35" +rusoto_dynamodb = "^0.35" \ No newline at end of file diff --git a/lambda-runtime/examples/basic.rs b/lambda-runtime/examples/basic.rs index 879a5675..c1fbc29f 100644 --- a/lambda-runtime/examples/basic.rs +++ b/lambda-runtime/examples/basic.rs @@ -1,13 +1,13 @@ extern crate lambda_runtime as lambda; extern crate log; extern crate serde_derive; -#[macro_use] extern crate simple_error; extern crate simple_logger; use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; +use simple_error::bail; use std::error::Error; #[derive(Deserialize)] diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index f9bfa164..fa6c01bf 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -1,10 +1,11 @@ +#![feature(custom_attribute)] +extern crate failure; extern crate lambda_runtime as lambda; extern crate log; extern crate serde_derive; extern crate simple_logger; -#[macro_use] -extern crate failure; +use failure::Fail; use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index beeb291e..3b776f7c 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -7,15 +7,13 @@ //! package must be called `bootstrap`. //! //! ```rust,no_run -//! #[macro_use] //! extern crate serde_derive; -//! #[macro_use] //! extern crate lambda_runtime; -//! #[macro_use] //! extern crate simple_error; //! -//! use lambda_runtime::HandlerError; -//! +//! use lambda_runtime::{HandlerError, lambda}; +//! use simple_error::bail; +//! use serde_derive::{Serialize, Deserialize}; //! //! #[derive(Deserialize, Clone)] //! struct CustomEvent { From bd3cb34b46a647c359f54ff723772b1653958744 Mon Sep 17 00:00:00 2001 From: sapessi Date: Tue, 4 Dec 2018 14:33:02 -0800 Subject: [PATCH 13/43] Changed error handling codes according to latest specs clarified in #23 --- lambda-runtime-client/src/client.rs | 4 ++-- lambda-runtime-client/src/error.rs | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 0b89e990..30f1673e 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,4 +1,4 @@ -use error::{ApiError, ErrorResponse}; +use error::{ApiError, ErrorResponse, ERROR_TYPE_UNHANDLED}; use hyper::{ client::HttpConnector, header::{self, HeaderMap, HeaderValue}, @@ -352,7 +352,7 @@ impl RuntimeClient { header::CONTENT_TYPE, header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) - .header(RUNTIME_ERROR_HEADER, e.error_code) + .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static(ERROR_TYPE_UNHANDLED)) .body(Body::from(body)) .unwrap() } diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 422e6179..3a85bbb1 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -17,10 +17,14 @@ use serde_json; /// Error type description for the `ErrorResponse` event. This type should be returned /// for errors that were handled by the function code or framework. -pub const ERROR_TYPE_HANDLED: &str = "Handled"; +#[allow(dead_code)] +pub(crate) const ERROR_TYPE_HANDLED: &str = "Handled"; /// Error type description for the `ErrorResponse` event. This type is used for unhandled, /// unexpcted errors. -pub const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; +pub(crate) const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; +/// Error type for the error responses to the Runtime APIs. In the future, this library +/// should use a customer-generated error code +pub const RUNTIME_ERROR_TYPE: &str = "RustRuntimeError"; /// This object is used to generate requests to the Lambda Runtime APIs. /// It is used for both the error response APIs and fail init calls. @@ -28,10 +32,6 @@ pub const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; /// this object to be compatible with the APIs. #[derive(Serialize)] pub struct ErrorResponse { - /// Internal error code to be sent to the Lambda Runtime APIs in the - /// `Lambda-Runtime-Function-Error-Type` header. - #[serde(skip)] - pub error_code: String, /// The error message generated by the application. #[serde(rename = "errorMessage")] pub error_message: String, @@ -59,11 +59,10 @@ impl ErrorResponse { /// /// # Return /// A new instance of the `ErrorResponse` object. - fn new(message: String, err_type: String, code: String) -> ErrorResponse { + fn new(message: String, err_type: String) -> ErrorResponse { let mut err = ErrorResponse { error_message: message, error_type: err_type, - error_code: code, stack_trace: Option::default(), }; let is_backtrace = env::var("RUST_BACKTRACE"); @@ -89,7 +88,7 @@ impl ErrorResponse { /// # Return /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. pub fn handled(message: String) -> ErrorResponse { - ErrorResponse::new(message, ERROR_TYPE_HANDLED.to_owned(), "RuntimeError".to_owned()) + ErrorResponse::new(message, RUNTIME_ERROR_TYPE.to_owned()) } /// Creates a new `RuntimeError` object with the unhandled error type. @@ -101,7 +100,7 @@ impl ErrorResponse { /// # Return /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. pub fn unhandled(message: String) -> ErrorResponse { - ErrorResponse::new(message, ERROR_TYPE_UNHANDLED.to_owned(), "RuntimeError".to_owned()) + ErrorResponse::new(message, RUNTIME_ERROR_TYPE.to_owned()) } } From 5b9bad9d7ab362f94a3fea233ad6d7062d280537 Mon Sep 17 00:00:00 2001 From: sapessi Date: Tue, 4 Dec 2018 14:33:39 -0800 Subject: [PATCH 14/43] Removed custom_attribute feature to fix build --- lambda-runtime/examples/custom_error_failure.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index fa6c01bf..cfa9a47a 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -1,4 +1,3 @@ -#![feature(custom_attribute)] extern crate failure; extern crate lambda_runtime as lambda; extern crate log; From 9ed9a43f0411e3043aa9e8035ec8d7ab23df9148 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 12:59:09 -0800 Subject: [PATCH 15/43] Cleaned up lifetimes/borrows --- lambda-runtime-client/Cargo.toml | 1 + lambda-runtime-client/src/client.rs | 32 ++++++++++++++++------------- lambda-runtime-client/src/error.rs | 11 +++++----- lambda-runtime-client/src/lib.rs | 15 ++------------ 4 files changed, 27 insertions(+), 32 deletions(-) diff --git a/lambda-runtime-client/Cargo.toml b/lambda-runtime-client/Cargo.toml index fcc63367..447f8cc1 100644 --- a/lambda-runtime-client/Cargo.toml +++ b/lambda-runtime-client/Cargo.toml @@ -8,6 +8,7 @@ license = "Apache-2.0" homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" repository = "https://github.com/awslabs/aws-lambda-rust-runtime" documentation = "https://docs.rs/lambda_runtime_client" +edition = "2018" [dependencies] hyper = "0.12" diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 30f1673e..170d2cfe 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,10 +1,12 @@ -use error::{ApiError, ErrorResponse, ERROR_TYPE_UNHANDLED}; +use crate::error::{ApiError, ErrorResponse, ERROR_TYPE_UNHANDLED}; use hyper::{ client::HttpConnector, header::{self, HeaderMap, HeaderValue}, rt::{Future, Stream}, Body, Client, Method, Request, Uri, }; +use log::*; +use serde_derive::*; use serde_json; use std::{collections::HashMap, fmt}; use tokio::runtime::Runtime; @@ -13,6 +15,8 @@ const RUNTIME_API_VERSION: &str = "2018-06-01"; const API_CONTENT_TYPE: &str = "application/json"; const API_ERROR_CONTENT_TYPE: &str = "application/vnd.aws.lambda.error+json"; const RUNTIME_ERROR_HEADER: &str = "Lambda-Runtime-Function-Error-Type"; +// TODO: Perhaps use a macro to generate this +const CLIENT_USER_AGENT: &str = "AWS_Lambda_Rust/0.1.0"; /// Enum of the headers returned by Lambda's `/next` API call. pub enum LambdaHeaders { @@ -116,7 +120,7 @@ pub struct RuntimeClient { endpoint: String, } -impl RuntimeClient { +impl<'ev> RuntimeClient { /// Creates a new instance of the Runtime APIclient SDK. The http client has timeouts disabled and /// will always send a `Connection: keep-alive` header. pub fn new(endpoint: String, runtime: Option) -> Result { @@ -137,7 +141,7 @@ impl RuntimeClient { } } -impl RuntimeClient { +impl<'ev> RuntimeClient { /// Polls for new events to the Runtime APIs. pub fn next_event(&self) -> Result<(Vec, EventContext), ApiError> { let uri = format!( @@ -174,7 +178,7 @@ impl RuntimeClient { } let ctx = self.get_event_context(&resp.headers())?; let out = resp.into_body().concat2().wait()?; - let buf: Vec = out.into_bytes().to_vec(); + let buf = out.into_bytes().to_vec(); trace!( "Received new event for request id {}. Event length {} bytes", @@ -202,7 +206,7 @@ impl RuntimeClient { /// /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. - pub fn event_response(&self, request_id: &str, output: Vec) -> Result<(), ApiError> { + pub fn event_response(&self, request_id: &str, output: &[u8]) -> Result<(), ApiError> { let uri: Uri = format!( "http://{}/{}/runtime/invocation/{}/response", self.endpoint, RUNTIME_API_VERSION, request_id @@ -250,7 +254,7 @@ impl RuntimeClient { /// /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. - pub fn event_error(&self, request_id: &str, e: ErrorResponse) -> Result<(), ApiError> { + pub fn event_error(&self, request_id: &str, e: &ErrorResponse) -> Result<(), ApiError> { let uri: Uri = format!( "http://{}/{}/runtime/invocation/{}/error", self.endpoint, RUNTIME_API_VERSION, request_id @@ -261,7 +265,7 @@ impl RuntimeClient { request_id, e.error_message ); - let req = self.get_runtime_error_request(&uri, e); + let req = self.get_runtime_error_request(&uri, &e); match self.http_client.request(req).wait() { Ok(resp) => { @@ -297,12 +301,12 @@ impl RuntimeClient { /// # Panics /// If it cannot send the init error. In this case we panic to force the runtime /// to restart. - pub fn fail_init(&self, e: ErrorResponse) { + pub fn fail_init(&self, e: &ErrorResponse) { let uri: Uri = format!("http://{}/{}/runtime/init/error", self.endpoint, RUNTIME_API_VERSION) .parse() .expect("Could not generate Runtime URI"); error!("Calling fail_init Runtime API: {}", e.error_message); - let req = self.get_runtime_error_request(&uri, e); + let req = self.get_runtime_error_request(&uri, &e); self.http_client .request(req) @@ -321,9 +325,7 @@ impl RuntimeClient { pub fn get_endpoint(&self) -> String { self.endpoint.clone() } -} -impl RuntimeClient { /// Creates a Hyper `Request` object for the given `Uri` and `Body`. Sets the /// HTTP method to `POST` and the `Content-Type` header value to `application/json`. /// @@ -334,16 +336,17 @@ impl RuntimeClient { /// /// # Returns /// A Populated Hyper `Request` object. - fn get_runtime_post_request(&self, uri: &Uri, body: Vec) -> Request { + fn get_runtime_post_request(&self, uri: &Uri, body: &[u8]) -> Request { Request::builder() .method(Method::POST) .uri(uri.clone()) .header(header::CONTENT_TYPE, header::HeaderValue::from_static(API_CONTENT_TYPE)) - .body(Body::from(body)) + .header(header::USER_AGENT, header::HeaderValue::from_static(CLIENT_USER_AGENT)) + .body(Body::from(body.to_owned())) .unwrap() } - fn get_runtime_error_request(&self, uri: &Uri, e: ErrorResponse) -> Request { + fn get_runtime_error_request(&self, uri: &Uri, e: &ErrorResponse) -> Request { let body = serde_json::to_vec(&e).expect("Could not turn error object into response JSON"); Request::builder() .method(Method::POST) @@ -352,6 +355,7 @@ impl RuntimeClient { header::CONTENT_TYPE, header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) + .header(header::USER_AGENT, header::HeaderValue::from_static(CLIENT_USER_AGENT)) .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static(ERROR_TYPE_UNHANDLED)) .body(Body::from(body)) .unwrap() diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 3a85bbb1..750dcb75 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -1,6 +1,12 @@ //! This module defines the `RuntimeApiError` trait that developers should implement //! to send their custom errors to the AWS Lambda Runtime Client SDK. The module also //! defines the `ApiError` type returned by the `RuntimeClient` implementations. +use backtrace; +use http::{header::ToStrError, uri::InvalidUri}; +use hyper; +use log::*; +use serde_derive::*; +use serde_json; use std::{ env, error::Error, @@ -10,11 +16,6 @@ use std::{ option::Option, }; -use backtrace; -use http::{header::ToStrError, uri::InvalidUri}; -use hyper; -use serde_json; - /// Error type description for the `ErrorResponse` event. This type should be returned /// for errors that were handled by the function code or framework. #[allow(dead_code)] diff --git a/lambda-runtime-client/src/lib.rs b/lambda-runtime-client/src/lib.rs index 70bcdc3e..6ab0daef 100644 --- a/lambda-runtime-client/src/lib.rs +++ b/lambda-runtime-client/src/lib.rs @@ -1,5 +1,6 @@ #![warn(missing_docs)] #![deny(warnings)] +#![allow(clippy::new_ret_no_self)] //! Rust client SDK for the AWS Lambda Runtime APIs. This crate defines //! a `RuntimeClient` that encapsulates interactions with AWS Lambda's Runtime //! APIs. @@ -56,19 +57,7 @@ //! } //! ``` -#[macro_use] -extern crate log; -#[macro_use] -extern crate serde_derive; - -extern crate backtrace; -extern crate http; -extern crate hyper; -extern crate serde; -extern crate serde_json; -extern crate tokio; - mod client; pub mod error; -pub use client::*; +pub use crate::client::*; From 7fa1167ca0d7f8c369c5cf69a75c6d249fc30110 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 12:59:53 -0800 Subject: [PATCH 16/43] New runtime core crate that implements the runtime main loop and defines a Vec based handler --- lambda-runtime-core/Cargo.toml | 23 +++ lambda-runtime-core/src/context.rs | 134 ++++++++++++++ lambda-runtime-core/src/env.rs | 184 ++++++++++++++++++ lambda-runtime-core/src/error.rs | 87 +++++++++ lambda-runtime-core/src/handler.rs | 38 ++++ lambda-runtime-core/src/lib.rs | 21 +++ lambda-runtime-core/src/runtime.rs | 288 +++++++++++++++++++++++++++++ 7 files changed, 775 insertions(+) create mode 100644 lambda-runtime-core/Cargo.toml create mode 100644 lambda-runtime-core/src/context.rs create mode 100644 lambda-runtime-core/src/env.rs create mode 100644 lambda-runtime-core/src/error.rs create mode 100644 lambda-runtime-core/src/handler.rs create mode 100644 lambda-runtime-core/src/lib.rs create mode 100644 lambda-runtime-core/src/runtime.rs diff --git a/lambda-runtime-core/Cargo.toml b/lambda-runtime-core/Cargo.toml new file mode 100644 index 00000000..99870625 --- /dev/null +++ b/lambda-runtime-core/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "lambda_runtime_core" +version = "0.1.0" +authors = ["Stefano Buliani", "David Barsky"] +description = "Rust runtime for AWS Lambda" +keywords = ["AWS", "Lambda", "Runtime", "Rust"] +license = "Apache-2.0" +homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" +repository = "https://github.com/awslabs/aws-lambda-rust-runtime" +documentation = "https://docs.rs/lambda_runtime" +edition = "2018" + +[dependencies] +log = "^0.4" +hyper = "^0.12" +tokio = "^0.1" +backtrace = "^0.3" +lambda_runtime_client = { path = "../lambda-runtime-client", version = "^0.1" } +chrono = "^0.4" + +[dev-dependencies] +failure = "^0.1" +simple_logger = "^1" \ No newline at end of file diff --git a/lambda-runtime-core/src/context.rs b/lambda-runtime-core/src/context.rs new file mode 100644 index 00000000..70532169 --- /dev/null +++ b/lambda-runtime-core/src/context.rs @@ -0,0 +1,134 @@ +use chrono::Utc; + +use crate::env as lambda_env; +use lambda_runtime_client; + +/// The Lambda function execution context. The values in this struct +/// are populated using the [Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html) +/// and the headers returned by the poll request to the Runtime APIs. +/// A new instance of the `Context` object is passed to each handler invocation. +#[derive(Default, Clone)] +pub struct Context { + /// The amount of memory allocated to the Lambda function in Mb. + /// This value is extracted from the `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` + /// environment variable set by the Lambda service. + pub memory_limit_in_mb: i32, + /// The name of the Lambda function as registered with the Lambda + /// service. The value is extracted from the `AWS_LAMBDA_FUNCTION_NAME` + /// environment variable set by the Lambda service. + pub function_name: String, + /// The version of the function being invoked. This value is extracted + /// from the `AWS_LAMBDA_FUNCTION_VERSION` environment variable set + /// by the Lambda service. + pub function_version: String, + /// The fully qualified ARN (Amazon Resource Name) for the function + /// invocation event. This value is returned by the Lambda Runtime APIs + /// as a header. + pub invoked_function_arn: String, + /// The AWS request ID for the current invocation event. This value + /// is returned by the Lambda Runtime APIs as a header. + pub aws_request_id: String, + /// The X-Ray trace ID for the current invocation. This value is returned + /// by the Lambda Runtime APIs as a header. Developers can use this value + /// with the AWS SDK to create new, custom sub-segments to the current + /// invocation. + pub xray_trace_id: String, + /// The name of the CloudWatch log stream for the current execution + /// environment. This value is extracted from the `AWS_LAMBDA_LOG_STREAM_NAME` + /// environment variable set by the Lambda service. + pub log_stream_name: String, + /// The name of the CloudWatch log group for the current execution + /// environment. This value is extracted from the `AWS_LAMBDA_LOG_GROUP_NAME` + /// environment variable set by the Lambda service. + pub log_group_name: String, + + /// The client context sent by the AWS Mobile SDK with the invocation + /// request. This value is returned by the Lambda Runtime APIs as a + /// header. This value is populated only if the invocation request + /// originated from an AWS Mobile SDK or an SDK that attached the client + /// context information to the request. + pub client_context: Option, + /// The information of the Cognito identity that sent the invocation + /// request to the Lambda service. This value is returned by the Lambda + /// Runtime APIs in a header and it's only populated if the invocation + /// request was performed with AWS credentials federated through the Cognito + /// identity service. + pub identity: Option, + + /// The deadline for the current handler execution in nanoseconds based + /// on a unix `MONOTONIC` clock. + pub(super) deadline: u128, +} + +impl Context { + /// Generates a new `Context` object for an event. Uses the responses headers alongside the + /// environment variable values from the `FunctionSettings` object to populate the data. + /// + /// # Arguments + /// + /// * `local_settings` A populated environment settings object + /// + /// # Return + /// A new, populated `Context` object. + pub(super) fn new(local_settings: lambda_env::FunctionSettings) -> Context { + Context { + xray_trace_id: String::from(""), + memory_limit_in_mb: local_settings.memory_size, + function_name: local_settings.function_name, + function_version: local_settings.version, + log_stream_name: local_settings.log_stream, + log_group_name: local_settings.log_group, + ..Default::default() + } + } + + /// Returns the remaining time in the execution in milliseconds. This is based on the + /// deadline header passed by Lambda's Runtime APIs. + pub fn get_time_remaining_millis(&self) -> u128 { + let millis = Utc::now().timestamp_millis(); + self.deadline - millis as u128 + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::env::{self, ConfigProvider}; + use std::{thread::sleep, time}; + + fn get_deadline(secs: u8) -> u128 { + Utc::now().timestamp_millis() as u128 + (u128::from(secs) * 1_000) + } + + pub(crate) fn test_context(deadline: u8) -> Context { + Context { + memory_limit_in_mb: 128, + function_name: "test_func".to_string(), + function_version: "$LATEST".to_string(), + invoked_function_arn: "arn:aws:lambda".to_string(), + aws_request_id: "123".to_string(), + xray_trace_id: "123".to_string(), + log_stream_name: "logStream".to_string(), + log_group_name: "logGroup".to_string(), + client_context: Option::default(), + identity: Option::default(), + deadline: get_deadline(deadline), + } + } + + #[test] + fn verify_time_remaining() { + let config = env::tests::MockConfigProvider { error: false }; + let mut ctx = Context::new(config.get_function_settings().unwrap()); + ctx.deadline = get_deadline(10); + println!("Set deadline to: {}", ctx.deadline); + sleep(time::Duration::new(2, 0)); + + let remaining = ctx.get_time_remaining_millis(); + assert!( + remaining > 7800 && remaining < 8200, + "Remaining time in millis outside the expected range: {}", + remaining + ); + } +} diff --git a/lambda-runtime-core/src/env.rs b/lambda-runtime-core/src/env.rs new file mode 100644 index 00000000..b707e7d6 --- /dev/null +++ b/lambda-runtime-core/src/env.rs @@ -0,0 +1,184 @@ +use std::env; + +use crate::error::RuntimeError; +use log::*; + +/// The name of the environment variable in the Lambda execution +/// environment for the Runtime APIs endpoint. The valie of this +/// variable is read once as the runtime starts. +pub const RUNTIME_ENDPOINT_VAR: &str = "AWS_LAMBDA_RUNTIME_API"; + +/// Clone-able generic function settings object. The data is loaded +/// from environment variables during the init process. The data +/// for the object is cloned in the `Context` for each invocation. +#[derive(Clone)] +pub struct FunctionSettings { + pub function_name: String, + pub memory_size: i32, + pub version: String, + pub log_stream: String, + pub log_group: String, +} + +/// Trait used by the `RustRuntime` module to retrieve configuration information +/// about the environement. This is implemented by the `EnvConfigProvider` using +/// the environment variables. We also have a mock implementation for the unit tests +pub trait ConfigProvider { + /// Loads the function settings such as name, arn, memory amount, version, etc. + /// + /// # Return + /// A `Result` of `FunctionSettings` or a `RuntimeError`. The runtime + /// fails the init process if this function returns an error. + fn get_function_settings(&self) -> Result; + + /// Returns the endpoint (hostname:port) for the Runtime API endpoint + fn get_runtime_api_endpoint(&self) -> Result; +} + +/// Implementation of the `ConfigProvider` trait that reads the settings from +/// environment variables in the Lambda execution environment. This is the config +/// used by the `start()` method of this module. +pub struct EnvConfigProvider; + +impl std::default::Default for EnvConfigProvider { + fn default() -> Self { + EnvConfigProvider {} + } +} + +impl ConfigProvider for EnvConfigProvider { + /// Loads the function settings from the Lambda environment variables: + /// https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html + fn get_function_settings(&self) -> Result { + let function_name = env::var("AWS_LAMBDA_FUNCTION_NAME")?; + let version = env::var("AWS_LAMBDA_FUNCTION_VERSION")?; + let log_stream = env::var("AWS_LAMBDA_LOG_STREAM_NAME")?; + let log_group = env::var("AWS_LAMBDA_LOG_GROUP_NAME")?; + let memory_str = env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")?; + let parsed_memory_str = memory_str.parse::(); + let memory_size: i32; + match parsed_memory_str { + Ok(int_value) => memory_size = int_value, + Err(_parse_err) => { + error!( + "Memory value from environment is not i32: {}", + env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE").unwrap() + ); + return Err(RuntimeError::unrecoverable(&format!( + "Could not parse memory value: {}", + memory_str + ))); + } + }; + + Ok(FunctionSettings { + function_name, + memory_size, + version, + log_stream, + log_group, + }) + } + + /// Loads the endpoint from Lambda's default environment variable: AWS_LAMBDA_RUNTIME_API + fn get_runtime_api_endpoint(&self) -> Result { + let endpoint = env::var(RUNTIME_ENDPOINT_VAR)?; + Ok(endpoint) + } +} + +#[cfg(test)] +pub(crate) mod tests { + use crate::env::*; + use crate::error; + use std::{env, error::Error}; + + pub(crate) struct MockConfigProvider { + pub(crate) error: bool, + } + + impl ConfigProvider for MockConfigProvider { + fn get_function_settings(&self) -> Result { + if self.error { + return Err(error::RuntimeError::unrecoverable("Mock error")); + } + + Ok(FunctionSettings { + function_name: String::from("MockFunction"), + memory_size: 128, + version: String::from("$LATEST"), + log_stream: String::from("LogStream"), + log_group: String::from("LogGroup"), + }) + } + + fn get_runtime_api_endpoint(&self) -> Result { + if self.error { + return Err(error::RuntimeError::unrecoverable("Mock error")); + } + + Ok(String::from("http://localhost:8080")) + } + } + + fn set_endpoint_env_var() { + env::set_var(RUNTIME_ENDPOINT_VAR, "localhost:8080"); + } + + fn set_lambda_env_vars() { + env::set_var("AWS_LAMBDA_FUNCTION_NAME", "test_func"); + env::set_var("AWS_LAMBDA_FUNCTION_VERSION", "$LATEST"); + env::set_var("AWS_LAMBDA_LOG_STREAM_NAME", "LogStreamName"); + env::set_var("AWS_LAMBDA_LOG_GROUP_NAME", "LogGroup2"); + env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128"); + } + + fn unset_env_vars() { + env::remove_var(RUNTIME_ENDPOINT_VAR); + env::remove_var("AWS_LAMBDA_FUNCTION_NAME"); + env::remove_var("AWS_LAMBDA_FUNCTION_VERSION"); + env::remove_var("AWS_LAMBDA_LOG_STREAM_NAME"); + env::remove_var("AWS_LAMBDA_LOG_GROUP_NAME"); + env::remove_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE"); + } + + #[test] + fn function_config_from_env_vars() { + unset_env_vars(); + set_endpoint_env_var(); + set_lambda_env_vars(); + let config_provider: &ConfigProvider = &EnvConfigProvider {}; + let env_settings = config_provider.get_function_settings(); + assert_eq!( + env_settings.is_err(), + false, + "Env settings returned an error: {}", + env_settings.err().unwrap().description() + ); + let settings = env_settings.unwrap(); + assert_eq!( + settings.memory_size, 128, + "Invalid memory size: {}", + settings.memory_size + ); + let endpoint = config_provider.get_runtime_api_endpoint(); + assert_eq!( + endpoint.is_err(), + false, + "Env endpoint returned an error: {}", + endpoint.err().unwrap().description() + ); + + unset_env_vars(); + let err_env_settings = config_provider.get_function_settings(); + assert!( + err_env_settings.is_err(), + "Env config did not return error without variables" + ); + let err_endpoint = config_provider.get_runtime_api_endpoint(); + assert!( + err_endpoint.is_err(), + "Env endpoint did not return error without variables" + ); + } +} diff --git a/lambda-runtime-core/src/error.rs b/lambda-runtime-core/src/error.rs new file mode 100644 index 00000000..987a3829 --- /dev/null +++ b/lambda-runtime-core/src/error.rs @@ -0,0 +1,87 @@ +//! The error module defines the error types that can be returned +//! by custom handlers as well as the runtime itself. +use std::{env, error::Error, fmt}; + +use lambda_runtime_client::error::ApiError; + +/// The `RuntimeError` object is returned by the custom runtime as it polls +/// for new events and tries to execute the handler function. The error +/// is primarily used by other methods within this crate and should not be relevant +/// to developers building Lambda functions. Handlers are expected to return +/// the `HandlerError` defined in this module. +#[derive(Debug, Clone)] +pub struct RuntimeError { + msg: String, + /// The request id that generated this error + pub(crate) request_id: Option, + /// Whether the error is recoverable or not. + pub(crate) recoverable: bool, +} + +impl RuntimeError { + /// Creates a new `RuntimeError` that is unrecoverable and it will cause the + /// runtime to panic in order to force a restart of the execution environment. + /// When a new `RuntimeError` is created the stack trace for the error is collected + /// automatically using the `backtrace` crate. + /// + /// # Arguments + /// + /// * `msg` The error message to be attached to the error. + /// + /// # Returns + /// A new `RuntimeError` instance with the `recoverable` property set to `false`. + pub(crate) fn unrecoverable(msg: &str) -> RuntimeError { + let mut new_error = RuntimeError::new(msg); + new_error.recoverable = false; + new_error + } + + /// Creates a new `RuntimeError` with the given properties. The stack trace for the + /// error is collected automatically using the `backtrace` crate. + /// + /// # Arguments + /// + /// * `msg` The error message + /// + /// # Returns + /// A new `RuntimeError` instance. + pub(crate) fn new(msg: &str) -> RuntimeError { + RuntimeError { + msg: String::from(msg), + recoverable: true, + request_id: None, + } + } +} + +impl fmt::Display for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} + +// This is important for other errors to wrap this one. +impl Error for RuntimeError { + fn description(&self) -> &str { + &self.msg + } + + fn cause(&self) -> Option<&Error> { + // Generic error, underlying cause isn't tracked. + None + } +} + +impl From for RuntimeError { + fn from(e: env::VarError) -> Self { + RuntimeError::unrecoverable(e.description()) + } +} + +impl From for RuntimeError { + fn from(e: ApiError) -> Self { + let mut err = RuntimeError::new(e.description()); + err.recoverable = e.recoverable; + err + } +} diff --git a/lambda-runtime-core/src/handler.rs b/lambda-runtime-core/src/handler.rs new file mode 100644 index 00000000..a39dfaa4 --- /dev/null +++ b/lambda-runtime-core/src/handler.rs @@ -0,0 +1,38 @@ +use crate::context::Context; +use std::{fmt::Display, fmt::Debug, fmt}; + +/// Functions acting as a handler must conform to this type. +pub trait Handler { + /// Run the handler. + fn run(&mut self, event: Vec, ctx: Context) -> Result, HandlerError>; +} + +impl<'ev, F> Handler for F +where + F: FnMut(Vec, Context) -> Result, HandlerError>, +{ + fn run(&mut self, event: Vec, ctx: Context) -> Result, HandlerError> { + (*self)(event, ctx) + } +} + +/// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`. +/// The `HandlerError` object can be generated `From` any object that supports `Display`, +/// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using +/// the `?` syntax. For example `let _age_num: u8 = e.age.parse()?;` will return the +/// `::Err` from the handler function. +pub struct HandlerError { + msg: String, +} + +impl From for HandlerError { + fn from(e: E) -> HandlerError { + HandlerError { msg: format!("{}", e) } + } +} + +impl Display for HandlerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} \ No newline at end of file diff --git a/lambda-runtime-core/src/lib.rs b/lambda-runtime-core/src/lib.rs new file mode 100644 index 00000000..306d2074 --- /dev/null +++ b/lambda-runtime-core/src/lib.rs @@ -0,0 +1,21 @@ +#![warn(missing_docs)] +#![deny(warnings)] +//! The Lambda runtime core crate implements [Lambda's custom runtime main loop](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html#runtimes-custom-build). +//! The crate receives a `Handler` type that consumed events in the form of `Vec` and +//! outputs a `Result` with a `Vec` successful output. +//! +//! **Unless you have specific requirements to consume/produce raw bytes, you should look at the +//! [`lambda_runtime` crate](https://crates.io/crates/lambda_runtime)**. +//! +//! TODO: Add example + +mod context; +mod env; +mod error; +mod handler; +mod runtime; + +pub use crate::context::Context; +pub use crate::env::{ConfigProvider, EnvConfigProvider}; +pub use crate::handler::{Handler, HandlerError}; +pub use crate::runtime::*; diff --git a/lambda-runtime-core/src/runtime.rs b/lambda-runtime-core/src/runtime.rs new file mode 100644 index 00000000..9e25ab58 --- /dev/null +++ b/lambda-runtime-core/src/runtime.rs @@ -0,0 +1,288 @@ +use crate::context::Context; +use crate::env::{ConfigProvider, EnvConfigProvider, FunctionSettings}; +use crate::error::RuntimeError; +use crate::handler::{Handler, HandlerError}; +use lambda_runtime_client::{error::ErrorResponse, RuntimeClient}; +use log::*; +use tokio::runtime::Runtime as TokioRuntime; + +const MAX_RETRIES: i8 = 3; + +/// Creates a new runtime and begins polling for events using Lambda's Runtime APIs. +/// +/// # Arguments +/// +/// * `f` A function pointer that conforms to the `Handler` type. +/// +/// # Panics +/// The function panics if the Lambda environment variables are not set. +pub fn start(f: impl Handler, runtime: Option) { + start_with_config(f, &EnvConfigProvider::default(), runtime) +} + +#[macro_export] +macro_rules! lambda { + ($handler:ident) => { + $crate::start($handler, None) + }; + ($handler:ident, $runtime:expr) => { + $crate::start($handler, Some($runtime)) + }; +} + +/// Internal implementation of the start method that receives a config provider. This method +/// is used for unit tests with a mock provider. The provider data is used to construct the +/// `HttpRuntimeClient` which is then passed to the `start_with_runtime_client()` function. +/// +/// # Arguments +/// +/// * `f` A function pointer that conforms to the `Handler` type. +/// * `config` An implementation of the `ConfigProvider` trait with static lifetime. +/// +/// # Panics +/// The function panics if the `ConfigProvider` returns an error from the `get_runtime_api_endpoint()` +/// or `get_function_settings()` methods. The panic forces AWS Lambda to terminate the environment +/// and spin up a new one for the next invocation. +pub fn start_with_config(f: impl Handler, config: &C, runtime: Option) +where + C: ConfigProvider, +{ + // if we cannot find the endpoint we panic, nothing else we can do. + let endpoint: String; + match config.get_runtime_api_endpoint() { + Ok(value) => endpoint = value, + Err(e) => { + panic!("Could not find runtime API env var: {}", e); + } + } + + // if we can't get the settings from the environment variable + // we also panic. + let function_config: FunctionSettings; + let settings = config.get_function_settings(); + match settings { + Ok(env_settings) => function_config = env_settings, + Err(e) => { + panic!("Could not find runtime API env var: {}", e); + } + } + + match RuntimeClient::new(endpoint, runtime) { + Ok(client) => { + start_with_runtime_client(f, function_config, client); + } + Err(e) => { + panic!("Could not create runtime client SDK: {}", e); + } + } +} + +/// Starts the rust runtime with the given Runtime API client. +/// +/// # Arguments +/// +/// * `f` A function pointer that conforms to the `Handler` type. +/// * `client` An implementation of the `lambda_runtime_client::RuntimeClient` +/// trait with a lifetime that matches that of the environment, +/// in this case expressed as `'env`. +/// +/// # Panics +/// The function panics if we cannot instantiate a new `RustRuntime` object. +pub(crate) fn start_with_runtime_client(f: impl Handler, func_settings: FunctionSettings, client: RuntimeClient) { + let mut lambda_runtime: Runtime<_> = Runtime::new(f, func_settings, MAX_RETRIES, client); + + // start the infinite loop + lambda_runtime.start(); +} + +/// Internal representation of the runtime object that polls for events and communicates +/// with the Runtime APIs +pub(super) struct Runtime { + runtime_client: RuntimeClient, + handler: F, + max_retries: i8, + settings: FunctionSettings, +} + +// generic methods implementation +impl Runtime { + /// Creates a new instance of the `Runtime` object populated with the environment + /// settings. + /// + /// # Arguments + /// + /// * `f` A function pointer that conforms to the `Handler` type. + /// * `retries` The maximum number of times we should retry calling the Runtime APIs + /// for recoverable errors while polling for new events. + /// + /// # Return + /// A `Result` for the `Runtime` object or a `errors::RuntimeSerror`. The runtime + /// fails the init if this function returns an error. If we cannot find the + /// `AWS_LAMBDA_RUNTIME_API` variable in the environment the function panics. + pub(super) fn new(f: F, config: FunctionSettings, retries: i8, client: RuntimeClient) -> Self { + debug!( + "Creating new runtime with {} max retries for endpoint {}", + retries, + client.get_endpoint() + ); + Runtime { + runtime_client: client, + settings: config, + handler: f, + max_retries: retries, + } + } +} + +// implementation of methods that require the Event and Output types +// to be compatible with `serde`'s Deserialize/Serialize. +impl Runtime +where + F: Handler, +{ + /// Starts the main event loop and begin polling or new events. If one of the + /// Runtime APIs returns an unrecoverable error this method calls the init failed + /// API and then panics. + fn start(&mut self) { + debug!("Beginning main event loop"); + loop { + let (event, ctx) = self.get_next_event(0, None); + let request_id = ctx.aws_request_id.clone(); + info!("Received new event with AWS request id: {}", request_id); + let function_outcome = self.invoke(event, ctx); + match function_outcome { + Ok(response) => { + debug!( + "Function executed succesfully for {}, pushing response to Runtime API", + request_id + ); + match self.runtime_client.event_response(&request_id, &response) { + Ok(_) => info!("Response for {} accepted by Runtime API", request_id), + // unrecoverable error while trying to communicate with the endpoint. + // we let the Lambda Runtime API know that we have died + Err(e) => { + error!("Could not send response for {} to Runtime API: {}", request_id, e); + if !e.recoverable { + error!( + "Error for {} is not recoverable, sending fail_init signal and panicking.", + request_id + ); + self.runtime_client.fail_init(&ErrorResponse::from(Box::new(e))); + panic!("Could not send response"); + } + } + } + } + Err(e) => { + debug!("Handler returned an error for {}: {}", request_id, e); + debug!("Attempting to send error response to Runtime API for {}", request_id); + match self + .runtime_client + .event_error(&request_id, &ErrorResponse::from(Box::new(e))) + { + Ok(_) => info!("Error response for {} accepted by Runtime API", request_id), + Err(e) => { + error!("Unable to send error response for {} to Runtime API: {}", request_id, e); + if !e.recoverable { + error!( + "Error for {} is not recoverable, sending fail_init signal and panicking", + request_id + ); + self.runtime_client.fail_init(&ErrorResponse::from(Box::new(e))); + panic!("Could not send error response"); + } + } + } + } + } + } + } + + /// Invoke the handler function. This method is split out of the main loop to + /// make it testable. + pub(super) fn invoke(&mut self, e: Vec, ctx: Context) -> Result, HandlerError> { + (self.handler).run(e, ctx) + } + + /// Attempts to get the next event from the Runtime APIs and keeps retrying + /// unless the error throws is not recoverable. + /// + /// # Return + /// The next `Event` object to be processed. + pub(super) fn get_next_event(&self, retries: i8, e: Option) -> (Vec, Context) { + if let Some(err) = e { + if retries > self.max_retries { + error!("Unrecoverable error while fetching next event: {}", err); + match err.request_id.clone() { + Some(req_id) => { + self.runtime_client + .event_error(&req_id, &ErrorResponse::from(Box::new(err))) + .expect("Could not send event error response"); + } + None => { + self.runtime_client.fail_init(&ErrorResponse::from(Box::new(err))); + } + } + + // these errors are not recoverable. Either we can't communicate with the runtie APIs + // or we cannot parse the event. panic to restart the environment. + panic!("Could not retrieve next event"); + } + } + + match self.runtime_client.next_event() { + Ok((ev_data, invocation_ctx)) => { + let mut handler_ctx = Context::new(self.settings.clone()); + handler_ctx.invoked_function_arn = invocation_ctx.invoked_function_arn; + handler_ctx.aws_request_id = invocation_ctx.aws_request_id; + handler_ctx.xray_trace_id = invocation_ctx.xray_trace_id; + handler_ctx.client_context = invocation_ctx.client_context; + handler_ctx.identity = invocation_ctx.identity; + handler_ctx.deadline = invocation_ctx.deadline; + + (ev_data, handler_ctx) + } + Err(e) => self.get_next_event(retries + 1, Option::from(RuntimeError::from(e))), + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use crate::context; + use crate::env; + use lambda_runtime_client::RuntimeClient; + + #[test] + fn runtime_invokes_handler() { + let config: &env::ConfigProvider = &env::tests::MockConfigProvider { error: false }; + let client = RuntimeClient::new( + config + .get_runtime_api_endpoint() + .expect("Could not get runtime endpoint"), + None, + ) + .expect("Could not initialize client"); + let handler = |_e: Vec, _c: context::Context| -> Result, HandlerError> { Ok(b"hello".to_vec()) }; + let retries: i8 = 3; + let mut runtime = Runtime::new( + handler, + config + .get_function_settings() + .expect("Could not load environment config"), + retries, + client, + ); + let output = runtime.invoke(b"test".to_vec(), context::tests::test_context(10)); + assert_eq!( + output.is_err(), + false, + "Handler threw an unexpected error: {}", + output.err().unwrap() + ); + let output_bytes = output.ok().unwrap(); + let output_string = String::from_utf8(output_bytes).unwrap(); + assert_eq!(output_string, "hello", "Unexpected output message: {}", output_string); + } +} From 51652425f6dc7edf13c7ebcee9e2114bee9528d8 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 13:01:02 -0800 Subject: [PATCH 17/43] Slimmed down runtime crate that only wraps the runtime core crate --- Cargo.lock | 542 +--------------------------------- Cargo.toml | 1 + lambda-runtime/Cargo.toml | 15 +- lambda-runtime/src/context.rs | 134 --------- lambda-runtime/src/env.rs | 183 ------------ lambda-runtime/src/error.rs | 119 -------- lambda-runtime/src/lib.rs | 89 +++++- lambda-runtime/src/runtime.rs | 364 ----------------------- 8 files changed, 96 insertions(+), 1351 deletions(-) delete mode 100644 lambda-runtime/src/context.rs delete mode 100644 lambda-runtime/src/env.rs delete mode 100644 lambda-runtime/src/error.rs delete mode 100644 lambda-runtime/src/runtime.rs diff --git a/Cargo.lock b/Cargo.lock index 7c3ed972..505aa2d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,25 +1,3 @@ -[[package]] -name = "aho-corasick" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "argon2rs" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "arrayvec" version = "0.4.7" @@ -49,43 +27,11 @@ dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "base64" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "bitflags" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "blake2-rfc" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "block-buffer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "byteorder" version = "1.2.6" @@ -117,7 +63,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -129,28 +74,6 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "constant_time_eq" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "core-foundation" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "core-foundation-sys" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-deque" version = "0.6.1" @@ -178,33 +101,6 @@ name = "crossbeam-utils" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "crypto-mac" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "digest" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "dirs" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "failure" version = "0.1.3" @@ -225,29 +121,11 @@ dependencies = [ "synstructure 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fnv" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -276,14 +154,6 @@ dependencies = [ "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "generic-array" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "h2" version = "0.1.13" @@ -301,20 +171,6 @@ dependencies = [ "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hex" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hmac" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crypto-mac 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "http" version = "0.1.13" @@ -355,28 +211,6 @@ dependencies = [ "want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hyper-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", - "native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "indexmap" version = "1.0.1" @@ -409,15 +243,9 @@ dependencies = [ name = "lambda_runtime" version = "0.1.0" dependencies = [ - "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lambda_runtime_client 0.1.0", + "lambda_runtime_core 0.1.0", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rusoto_core 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rusoto_dynamodb 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", @@ -440,6 +268,20 @@ dependencies = [ "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lambda_runtime_core" +version = "0.1.0" +dependencies = [ + "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", + "lambda_runtime_client 0.1.0", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.1.0" @@ -475,26 +317,6 @@ dependencies = [ "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "md5" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memchr" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "memoffset" version = "0.2.1" @@ -539,23 +361,6 @@ dependencies = [ "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "native-tls" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "net2" version = "0.2.33" @@ -592,35 +397,6 @@ dependencies = [ "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "openssl" -version = "0.10.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "openssl-probe" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "openssl-sys" -version = "0.9.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "owning_ref" version = "0.3.3" @@ -650,16 +426,6 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "pkg-config" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "proc-macro2" version = "0.4.19" @@ -676,16 +442,6 @@ dependencies = [ "proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand" version = "0.5.5" @@ -708,98 +464,6 @@ name = "redox_syscall" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "redox_users" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "regex-syntax" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "remove_dir_all" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rusoto_core" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "hmac 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rusoto_credential 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rusoto_credential" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-timer 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rusoto_dynamodb" -version = "0.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "rusoto_core 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rustc-demangle" version = "0.1.9" @@ -818,50 +482,11 @@ name = "ryu" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "safemem" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "schannel" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "security-framework" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "security-framework-sys" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "semver" version = "0.9.0" @@ -900,17 +525,6 @@ dependencies = [ "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "sha2" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "simple-error" version = "0.1.12" @@ -969,27 +583,6 @@ dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tempfile" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "time" version = "0.1.40" @@ -1158,29 +751,6 @@ name = "try-lock" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "typenum" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ucd-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.1.0" @@ -1194,26 +764,6 @@ dependencies = [ "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "url" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "utf8-ranges" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vcpkg" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "version_check" version = "0.1.5" @@ -1272,60 +822,31 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "xml-rs" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [metadata] -"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a" -"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" -"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" -"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781" "checksum bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0ce55bd354b095246fc34caf4e9e242f5297a7fd938b090cadfea6eee614aa62" "checksum cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "f159dfd43363c4d08055a07703eb7a3406b0dac4d0584d96965a3262db3c9d16" "checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3" "checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" -"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" -"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" "checksum crossbeam-deque 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3486aefc4c0487b9cb52372c97df0a48b8c249514af1ee99703bf70d2f2ceda1" "checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" -"checksum crypto-mac 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0999b4ff4d3446d4ddb19a63e9e00c1876e75cd7000d20e57a693b4b3f08d958" -"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" -"checksum dirs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "88972de891f6118092b643d85a0b28e0678e0f948d7f879aa32f2d5aafe97d2a" "checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" "checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" -"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "49e7653e374fe0d0c12de4250f0bdb60680b8c80eed558c5c7538eec9c89e21b" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum h2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd33bafe2e6370e6c8eb0cf1b8c5f93390b90acde7e9b03723f166b28b648ed" -"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" -"checksum hmac 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44f3bdb08579d99d7dc761c0e266f13b5f2ab8c8c703b9fc9ef333cd8f48f55e" "checksum http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "24f58e8c2d8e886055c3ead7b28793e1455270b5fb39650984c224bc538ba581" "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" "checksum hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4aca412c241a2dd53af261efc7adf7736fdebd67dc0d1cc1ffdbcb9407e0e810" -"checksum hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "32cd73f14ad370d3b4d4b7dce08f69b81536c82e39fcc89731930fe5788cd661" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" @@ -1335,55 +856,32 @@ dependencies = [ "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum lock_api 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775751a3e69bde4df9b38dd00a1b5d6ac13791e4223d4a0506577f0dd27cfb7a" "checksum log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fcce5fa49cc693c312001daf1d13411c4a5283796bac1084299ea3e567113f" -"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -"checksum md5 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "79c56d6a0b07f9e19282511c83fc5b086364cbae4ba8c7d5f190c3d9b0425a48" -"checksum memchr 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b3629fe9fdbff6daa6c33b90f7c08355c1aca05a3d01fa8063b822fcf185f3b" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "71646331f2619b1026cc302f87a2b8b648d5c6dd6937846a16cc8ce0f347f432" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" -"checksum openssl 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "5e1309181cdcbdb51bc3b6bedb33dfac2a83b3d585033d3f6d9e22e8c1928613" -"checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)" = "278c1ad40a89aa1e741a1eed089a2f60b18fab8089c3139b542140fc7d674106" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" "checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" -"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" -"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum proc-macro2 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "ffe022fb8c8bd254524b0b3305906c1921fa37a84a644e29079a9e62200c3901" "checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5" -"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" "checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c" "checksum rand_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "edecf0f94da5551fc9b492093e30b041a891657db7940ee221f9d2f66e82eef2" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" -"checksum redox_users 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "214a97e49be64fd2c86f568dd0cb2c757d2cc53de95b273b6ad0a1c908482f26" -"checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" -"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rusoto_core 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9be4aac9e74cc9bc3401a77fe2bfe72bb603c2e4fcf7bf68ada89c2b0fd7049d" -"checksum rusoto_credential 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9a331ddbb4cbc2f99464092cb3dcded64af9ea1e009f4ef719c9fa1130731992" -"checksum rusoto_dynamodb 0.35.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0e63a3b5de70380f6cffaa5e26ac4e86eefa7c0ec03b7a72d36192adb49bc53" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7" -"checksum safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8dca453248a96cb0749e36ccdfe2b0b4e54a61bfef89fb97ec621eb8e0a93dd9" -"checksum schannel 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0e1a231dc10abf6749cfa5d7767f25888d484201accbd919b66ab5413c502d56" -"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum security-framework 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "697d3f3c23a618272ead9e1fb259c1411102b31c6af8b93f1d64cca9c3b0e8e0" -"checksum security-framework-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab01dfbe5756785b5b4d46e0289e5a18071dfa9a7c2b24213ea00b9ef9b665bf" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" "checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" "checksum serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "bb47a3d5c84320222f66d7db21157c4a7407755de41798f9b4c1c40593397b1a" -"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum simple-error 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "01c1c2ededd95f93b1d65e7f8b5b17670e926bf9cbb55f8b91b26b0bd40d3259" "checksum simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25111f1d77db1ac3ee11b62ba4b7a162e6bb3be43e28273f0d3935cc8d3ff7fb" "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" @@ -1392,8 +890,6 @@ dependencies = [ "checksum string 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00caf261d6f90f588f8450b8e1230fa0d5be49ee6140fdfbcb55335aff350970" "checksum syn 0.15.6 (registry+https://github.com/rust-lang/crates.io-index)" = "854b08a640fc8f54728fb95321e3ec485b365a97fe47609797c671addd1dde69" "checksum synstructure 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ec37f4fab4bafaf6b5621c1d54e6aa5d4d059a8f84929e87abfdd7f9f04c6db2" -"checksum tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "55c1195ef8513f3273d55ff59fe5da6940287a0d7a98331254397f464833675b" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" "checksum tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6e93c78d23cc61aa245a8acd2c4a79c4d7fa7fb5c3ca90d5737029f043a84895" "checksum tokio-codec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c501eceaf96f0e1793cf26beb63da3d11c738c4a943fdf3746d81d64684c39f" @@ -1408,15 +904,8 @@ dependencies = [ "checksum tokio-udp 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "da941144b816d0dcda4db3a1ba87596e4df5e860a72b70783fe435891f80601c" "checksum tokio-uds 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22e3aa6d1fcc19e635418dc0a30ab5bd65d347973d6f43f1a37bf8d9d1335fc9" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" -"checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" -"checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" @@ -1426,4 +915,3 @@ dependencies = [ "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -"checksum xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c1cb601d29fe2c2ac60a2b2e5e293994d87a1f6fa9687a31a15270f909be9c2" diff --git a/Cargo.toml b/Cargo.toml index 53c4bf12..90ea7053 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "lambda-runtime-client", + "lambda-runtime-core", "lambda-runtime" ] \ No newline at end of file diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index d2fb6e72..459e8f56 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -8,22 +8,19 @@ license = "Apache-2.0" homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" repository = "https://github.com/awslabs/aws-lambda-rust-runtime" documentation = "https://docs.rs/lambda_runtime" +edition = "2018" [dependencies] serde = "^1" serde_json = "^1" serde_derive = "^1" +tokio = "0.1" log = "^0.4" -hyper = "^0.12" -tokio = "^0.1" -backtrace = "^0.3" -lambda_runtime_client = { path = "../lambda-runtime-client", version = "^0.1" } -chrono = "^0.4" -simple_logger = "^1" +lambda_runtime_core = { path = "../lambda-runtime-core", version = "^0.1" } [dev-dependencies] -hyper-tls = "^0.3" +simple_logger = "^1" simple-error = "^0.1" failure = "^0.1" -rusoto_core = "^0.35" -rusoto_dynamodb = "^0.35" \ No newline at end of file +#rusoto_core = "^0.35" +#rusoto_dynamodb = "^0.35" \ No newline at end of file diff --git a/lambda-runtime/src/context.rs b/lambda-runtime/src/context.rs deleted file mode 100644 index 75ea16e3..00000000 --- a/lambda-runtime/src/context.rs +++ /dev/null @@ -1,134 +0,0 @@ -use chrono::Utc; - -use env as lambda_env; -use lambda_runtime_client; - -/// The Lambda function execution context. The values in this struct -/// are populated using the [Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html) -/// and the headers returned by the poll request to the Runtime APIs. -/// A new instance of the `Context` object is passed to each handler invocation. -#[derive(Default, Clone)] -pub struct Context { - /// The amount of memory allocated to the Lambda function in Mb. - /// This value is extracted from the `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` - /// environment variable set by the Lambda service. - pub memory_limit_in_mb: i32, - /// The name of the Lambda function as registered with the Lambda - /// service. The value is extracted from the `AWS_LAMBDA_FUNCTION_NAME` - /// environment variable set by the Lambda service. - pub function_name: String, - /// The version of the function being invoked. This value is extracted - /// from the `AWS_LAMBDA_FUNCTION_VERSION` environment variable set - /// by the Lambda service. - pub function_version: String, - /// The fully qualified ARN (Amazon Resource Name) for the function - /// invocation event. This value is returned by the Lambda Runtime APIs - /// as a header. - pub invoked_function_arn: String, - /// The AWS request ID for the current invocation event. This value - /// is returned by the Lambda Runtime APIs as a header. - pub aws_request_id: String, - /// The X-Ray trace ID for the current invocation. This value is returned - /// by the Lambda Runtime APIs as a header. Developers can use this value - /// with the AWS SDK to create new, custom sub-segments to the current - /// invocation. - pub xray_trace_id: String, - /// The name of the CloudWatch log stream for the current execution - /// environment. This value is extracted from the `AWS_LAMBDA_LOG_STREAM_NAME` - /// environment variable set by the Lambda service. - pub log_stream_name: String, - /// The name of the CloudWatch log group for the current execution - /// environment. This value is extracted from the `AWS_LAMBDA_LOG_GROUP_NAME` - /// environment variable set by the Lambda service. - pub log_group_name: String, - - /// The client context sent by the AWS Mobile SDK with the invocation - /// request. This value is returned by the Lambda Runtime APIs as a - /// header. This value is populated only if the invocation request - /// originated from an AWS Mobile SDK or an SDK that attached the client - /// context information to the request. - pub client_context: Option, - /// The information of the Cognito identity that sent the invocation - /// request to the Lambda service. This value is returned by the Lambda - /// Runtime APIs in a header and it's only populated if the invocation - /// request was performed with AWS credentials federated through the Cognito - /// identity service. - pub identity: Option, - - /// The deadline for the current handler execution in nanoseconds based - /// on a unix `MONOTONIC` clock. - pub(super) deadline: u128, -} - -impl Context { - /// Generates a new `Context` object for an event. Uses the responses headers alongside the - /// environment variable values from the `FunctionSettings` object to populate the data. - /// - /// # Arguments - /// - /// * `local_settings` A populated environment settings object - /// - /// # Return - /// A new, populated `Context` object. - pub(super) fn new(local_settings: lambda_env::FunctionSettings) -> Context { - Context { - xray_trace_id: String::from(""), - memory_limit_in_mb: local_settings.memory_size, - function_name: local_settings.function_name, - function_version: local_settings.version, - log_stream_name: local_settings.log_stream, - log_group_name: local_settings.log_group, - ..Default::default() - } - } - - /// Returns the remaining time in the execution in milliseconds. This is based on the - /// deadline header passed by Lambda's Runtime APIs. - pub fn get_time_remaining_millis(&self) -> u128 { - let millis = Utc::now().timestamp_millis(); - self.deadline - millis as u128 - } -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use env::{self, ConfigProvider}; - use std::{thread::sleep, time}; - - fn get_deadline(secs: u8) -> u128 { - Utc::now().timestamp_millis() as u128 + (u128::from(secs) * 1_000) - } - - pub(crate) fn test_context(deadline: u8) -> Context { - Context { - memory_limit_in_mb: 128, - function_name: "test_func".to_string(), - function_version: "$LATEST".to_string(), - invoked_function_arn: "arn:aws:lambda".to_string(), - aws_request_id: "123".to_string(), - xray_trace_id: "123".to_string(), - log_stream_name: "logStream".to_string(), - log_group_name: "logGroup".to_string(), - client_context: Option::default(), - identity: Option::default(), - deadline: get_deadline(deadline), - } - } - - #[test] - fn verify_time_remaining() { - let config = env::tests::MockConfigProvider { error: false }; - let mut ctx = Context::new(config.get_function_settings().unwrap()); - ctx.deadline = get_deadline(10); - println!("Set deadline to: {}", ctx.deadline); - sleep(time::Duration::new(2, 0)); - - let remaining = ctx.get_time_remaining_millis(); - assert!( - remaining > 7800 && remaining < 8200, - "Remaining time in millis outside the expected range: {}", - remaining - ); - } -} diff --git a/lambda-runtime/src/env.rs b/lambda-runtime/src/env.rs deleted file mode 100644 index 9410302c..00000000 --- a/lambda-runtime/src/env.rs +++ /dev/null @@ -1,183 +0,0 @@ -use std::env; - -use error::RuntimeError; - -/// The name of the environment variable in the Lambda execution -/// environment for the Runtime APIs endpoint. The valie of this -/// variable is read once as the runtime starts. -pub const RUNTIME_ENDPOINT_VAR: &str = "AWS_LAMBDA_RUNTIME_API"; - -/// Clone-able generic function settings object. The data is loaded -/// from environment variables during the init process. The data -/// for the object is cloned in the `Context` for each invocation. -#[derive(Clone)] -pub struct FunctionSettings { - pub function_name: String, - pub memory_size: i32, - pub version: String, - pub log_stream: String, - pub log_group: String, -} - -/// Trait used by the `RustRuntime` module to retrieve configuration information -/// about the environement. This is implemented by the `EnvConfigProvider` using -/// the environment variables. We also have a mock implementation for the unit tests -pub trait ConfigProvider { - /// Loads the function settings such as name, arn, memory amount, version, etc. - /// - /// # Return - /// A `Result` of `FunctionSettings` or a `RuntimeError`. The runtime - /// fails the init process if this function returns an error. - fn get_function_settings(&self) -> Result; - - /// Returns the endpoint (hostname:port) for the Runtime API endpoint - fn get_runtime_api_endpoint(&self) -> Result; -} - -/// Implementation of the `ConfigProvider` trait that reads the settings from -/// environment variables in the Lambda execution environment. This is the config -/// used by the `start()` method of this module. -pub struct EnvConfigProvider; - -impl EnvConfigProvider { - pub fn new() -> Self { - EnvConfigProvider {} - } -} - -impl ConfigProvider for EnvConfigProvider { - /// Loads the function settings from the Lambda environment variables: - /// https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html - fn get_function_settings(&self) -> Result { - let function_name = env::var("AWS_LAMBDA_FUNCTION_NAME")?; - let version = env::var("AWS_LAMBDA_FUNCTION_VERSION")?; - let log_stream = env::var("AWS_LAMBDA_LOG_STREAM_NAME")?; - let log_group = env::var("AWS_LAMBDA_LOG_GROUP_NAME")?; - let memory_str = env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")?; - let parsed_memory_str = memory_str.parse::(); - let memory_size: i32; - match parsed_memory_str { - Ok(int_value) => memory_size = int_value, - Err(_parse_err) => { - error!( - "Memory value from environment is not i32: {}", - env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE").unwrap() - ); - return Err(RuntimeError::unrecoverable(&format!( - "Could not parse memory value: {}", - memory_str - ))); - } - }; - - Ok(FunctionSettings { - function_name, - memory_size, - version, - log_stream, - log_group, - }) - } - - /// Loads the endpoint from Lambda's default environment variable: AWS_LAMBDA_RUNTIME_API - fn get_runtime_api_endpoint(&self) -> Result { - let endpoint = env::var(RUNTIME_ENDPOINT_VAR)?; - Ok(endpoint) - } -} - -#[cfg(test)] -pub(crate) mod tests { - use env::*; - use error; - use std::{env, error::Error}; - - pub(crate) struct MockConfigProvider { - pub(crate) error: bool, - } - - impl ConfigProvider for MockConfigProvider { - fn get_function_settings(&self) -> Result { - if self.error { - return Err(error::RuntimeError::unrecoverable("Mock error")); - } - - Ok(FunctionSettings { - function_name: String::from("MockFunction"), - memory_size: 128, - version: String::from("$LATEST"), - log_stream: String::from("LogStream"), - log_group: String::from("LogGroup"), - }) - } - - fn get_runtime_api_endpoint(&self) -> Result { - if self.error { - return Err(error::RuntimeError::unrecoverable("Mock error")); - } - - Ok(String::from("http://localhost:8080")) - } - } - - fn set_endpoint_env_var() { - env::set_var(RUNTIME_ENDPOINT_VAR, "localhost:8080"); - } - - fn set_lambda_env_vars() { - env::set_var("AWS_LAMBDA_FUNCTION_NAME", "test_func"); - env::set_var("AWS_LAMBDA_FUNCTION_VERSION", "$LATEST"); - env::set_var("AWS_LAMBDA_LOG_STREAM_NAME", "LogStreamName"); - env::set_var("AWS_LAMBDA_LOG_GROUP_NAME", "LogGroup2"); - env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128"); - } - - fn unset_env_vars() { - env::remove_var(RUNTIME_ENDPOINT_VAR); - env::remove_var("AWS_LAMBDA_FUNCTION_NAME"); - env::remove_var("AWS_LAMBDA_FUNCTION_VERSION"); - env::remove_var("AWS_LAMBDA_LOG_STREAM_NAME"); - env::remove_var("AWS_LAMBDA_LOG_GROUP_NAME"); - env::remove_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE"); - } - - #[test] - fn function_config_from_env_vars() { - unset_env_vars(); - set_endpoint_env_var(); - set_lambda_env_vars(); - let config_provider: &ConfigProvider = &EnvConfigProvider {}; - let env_settings = config_provider.get_function_settings(); - assert_eq!( - env_settings.is_err(), - false, - "Env settings returned an error: {}", - env_settings.err().unwrap().description() - ); - let settings = env_settings.unwrap(); - assert_eq!( - settings.memory_size, 128, - "Invalid memory size: {}", - settings.memory_size - ); - let endpoint = config_provider.get_runtime_api_endpoint(); - assert_eq!( - endpoint.is_err(), - false, - "Env endpoint returned an error: {}", - endpoint.err().unwrap().description() - ); - - unset_env_vars(); - let err_env_settings = config_provider.get_function_settings(); - assert!( - err_env_settings.is_err(), - "Env config did not return error without variables" - ); - let err_endpoint = config_provider.get_runtime_api_endpoint(); - assert!( - err_endpoint.is_err(), - "Env endpoint did not return error without variables" - ); - } -} diff --git a/lambda-runtime/src/error.rs b/lambda-runtime/src/error.rs deleted file mode 100644 index ebbcd504..00000000 --- a/lambda-runtime/src/error.rs +++ /dev/null @@ -1,119 +0,0 @@ -//! The error module defines the error types that can be returned -//! by custom handlers as well as the runtime itself. -use std::{ - env, - error::Error, - fmt::{self, Debug, Display}, -}; - -use lambda_runtime_client::error::ApiError; -use serde_json; - -/// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`. -/// The `HandlerError` object can be generated `From` any object that supports `Display`, -/// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using -/// the `?` syntax. For example `let _age_num: u8 = e.age.parse()?;` will return the -/// `::Err` from the handler function. -pub struct HandlerError { - msg: String, -} - -impl From for HandlerError { - fn from(e: E) -> HandlerError { - HandlerError { msg: format!("{}", e) } - } -} - -impl Display for HandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg) - } -} - -/// The `RuntimeError` object is returned by the custom runtime as it polls -/// for new events and tries to execute the handler function. The error -/// is primarily used by other methods within this crate and should not be relevant -/// to developers building Lambda functions. Handlers are expected to return -/// the `HandlerError` defined in this module. -#[derive(Debug, Clone)] -pub struct RuntimeError { - msg: String, - /// The request id that generated this error - pub(crate) request_id: Option, - /// Whether the error is recoverable or not. - pub(crate) recoverable: bool, -} - -impl RuntimeError { - /// Creates a new `RuntimeError` that is unrecoverable and it will cause the - /// runtime to panic in order to force a restart of the execution environment. - /// When a new `RuntimeError` is created the stack trace for the error is collected - /// automatically using the `backtrace` crate. - /// - /// # Arguments - /// - /// * `msg` The error message to be attached to the error. - /// - /// # Returns - /// A new `RuntimeError` instance with the `recoverable` property set to `false`. - pub(crate) fn unrecoverable(msg: &str) -> RuntimeError { - let mut new_error = RuntimeError::new(msg); - new_error.recoverable = false; - new_error - } - - /// Creates a new `RuntimeError` with the given properties. The stack trace for the - /// error is collected automatically using the `backtrace` crate. - /// - /// # Arguments - /// - /// * `msg` The error message - /// - /// # Returns - /// A new `RuntimeError` instance. - pub(crate) fn new(msg: &str) -> RuntimeError { - RuntimeError { - msg: String::from(msg), - recoverable: true, - request_id: None, - } - } -} - -impl fmt::Display for RuntimeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg) - } -} - -// This is important for other errors to wrap this one. -impl Error for RuntimeError { - fn description(&self) -> &str { - &self.msg - } - - fn cause(&self) -> Option<&Error> { - // Generic error, underlying cause isn't tracked. - None - } -} - -impl From for RuntimeError { - fn from(e: env::VarError) -> Self { - RuntimeError::unrecoverable(e.description()) - } -} - -impl From for RuntimeError { - fn from(e: serde_json::Error) -> Self { - RuntimeError::unrecoverable(e.description()) - } -} - -impl From for RuntimeError { - fn from(e: ApiError) -> Self { - let mut err = RuntimeError::new(e.description()); - err.recoverable = e.recoverable; - err - } -} diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 3b776f7c..6dd447a3 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -39,21 +39,80 @@ //! }) //! } //! ``` -#[macro_use] -extern crate log; +use lambda_runtime_core::{start_with_config, EnvConfigProvider}; +use serde; +use serde_json; +use tokio::runtime::Runtime as TokioRuntime; -extern crate backtrace; -extern crate chrono; -extern crate lambda_runtime_client; -extern crate serde; -extern crate serde_json; -extern crate tokio; +pub use lambda_runtime_core::{Context, HandlerError}; -mod context; -mod env; -mod error; -mod runtime; +/// Functions acting as a handler must conform to this type. +pub trait Handler { + /// Method to execute the handler function + fn run(&mut self, event: E, ctx: Context) -> Result; +} -pub use context::*; -pub use error::HandlerError; -pub use runtime::*; +/// Implementation of the `Handler` trait for both function pointers +/// and closures. +impl Handler for F +where + F: FnMut(E, Context) -> Result, +{ + fn run(&mut self, event: E, ctx: Context) -> Result { + (*self)(event, ctx) + } +} + +/// Wraps a typed handler into a closure that complies with the `Handler` trait +/// defined in the `lambda_runtime_core` crate. The closure simply uses `serde_json` +/// to serialize and deserialize the incoming event from a `Vec` and the output +/// to a `Vec`. +fn wrap(mut h: impl Handler) -> impl FnMut(Vec, Context) -> Result, HandlerError> +where + E: serde::de::DeserializeOwned, + O: serde::Serialize, +{ + move |ev, ctx| { + let event: E = serde_json::from_slice(&ev)?; + match h.run(event, ctx) { + Ok(out) => { + let out_bytes = serde_json::to_vec(&out)?; + Ok(out_bytes) + } + Err(err) => Err(err), + } + } +} + +/// Creates a new runtime and begins polling for events using Lambda's Runtime APIs. +/// +/// # Arguments +/// +/// * `f` A function pointer that conforms to the `Handler` type. +/// +/// # Panics +/// The function panics if the Lambda environment variables are not set. +pub fn start(f: impl Handler, runtime: Option) +where + E: serde::de::DeserializeOwned, + O: serde::Serialize, +{ + let wrapped = wrap(f); + start_with_config(wrapped, &EnvConfigProvider::default(), runtime) +} + +#[macro_export] +macro_rules! lambda { + ($handler:ident) => { + $crate::start($handler, None) + }; + ($handler:ident, $runtime:expr) => { + $crate::start($handler, Some($runtime)) + }; + ($handler:expr) => { + $crate::start($handler, None) + }; + ($handler:expr, $runtime:expr) => { + $crate::start($handler, Some($runtime)) + }; +} diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime/src/runtime.rs deleted file mode 100644 index 6b56a690..00000000 --- a/lambda-runtime/src/runtime.rs +++ /dev/null @@ -1,364 +0,0 @@ -use std::{marker::PhantomData, result}; - -use serde; -use serde_json; - -use context::Context; -use env::{ConfigProvider, EnvConfigProvider, FunctionSettings}; -use error::{HandlerError, RuntimeError}; -use lambda_runtime_client::{error::ErrorResponse, RuntimeClient}; -use tokio::runtime::Runtime as TokioRuntime; - -const MAX_RETRIES: i8 = 3; - -/// Functions acting as a handler must conform to this type. -pub trait Handler { - /// Run the handler. - fn run(&mut self, event: E, ctx: Context) -> Result; -} - -impl Handler for F -where - F: FnMut(E, Context) -> Result, -{ - fn run(&mut self, event: E, ctx: Context) -> Result { - (*self)(event, ctx) - } -} - -/// Creates a new runtime and begins polling for events using Lambda's Runtime APIs. -/// -/// # Arguments -/// -/// * `f` A function pointer that conforms to the `Handler` type. -/// -/// # Panics -/// The function panics if the Lambda environment variables are not set. -pub fn start(f: impl Handler, runtime: Option) -where - E: serde::de::DeserializeOwned, - O: serde::Serialize, -{ - start_with_config(f, &EnvConfigProvider::new(), runtime) -} - -#[macro_export] -macro_rules! lambda { - ($handler:ident) => { - $crate::start($handler, None) - }; - ($handler:ident, $runtime:expr) => { - $crate::start($handler, Some($runtime)) - }; -} - -/// Internal implementation of the start method that receives a config provider. This method -/// is used for unit tests with a mock provider. The provider data is used to construct the -/// `HttpRuntimeClient` which is then passed to the `start_with_runtime_client()` function. -/// -/// # Arguments -/// -/// * `f` A function pointer that conforms to the `Handler` type. -/// * `config` An implementation of the `ConfigProvider` trait with static lifetime. -/// -/// # Panics -/// The function panics if the `ConfigProvider` returns an error from the `get_runtime_api_endpoint()` -/// or `get_function_settings()` methods. The panic forces AWS Lambda to terminate the environment -/// and spin up a new one for the next invocation. -pub(crate) fn start_with_config(f: impl Handler, config: &C, runtime: Option) -where - E: serde::de::DeserializeOwned, - O: serde::Serialize, - C: ConfigProvider, -{ - // if we cannot find the endpoint we panic, nothing else we can do. - let endpoint: String; - match config.get_runtime_api_endpoint() { - Ok(value) => endpoint = value, - Err(e) => { - panic!("Could not find runtime API env var: {}", e); - } - } - - // if we can't get the settings from the environment variable - // we also panic. - let function_config: FunctionSettings; - let settings = config.get_function_settings(); - match settings { - Ok(env_settings) => function_config = env_settings, - Err(e) => { - panic!("Could not find runtime API env var: {}", e); - } - } - - match RuntimeClient::new(endpoint, runtime) { - Ok(client) => { - start_with_runtime_client(f, function_config, client); - } - Err(e) => { - panic!("Could not create runtime client SDK: {}", e); - } - } -} - -/// Starts the rust runtime with the given Runtime API client. -/// -/// # Arguments -/// -/// * `f` A function pointer that conforms to the `Handler` type. -/// * `client` An implementation of the `lambda_runtime_client::RuntimeClient` -/// trait with a lifetime that matches that of the environment, -/// in this case expressed as `'env`. -/// -/// # Panics -/// The function panics if we cannot instantiate a new `RustRuntime` object. -pub(crate) fn start_with_runtime_client( - f: impl Handler, - func_settings: FunctionSettings, - client: RuntimeClient, -) where - E: serde::de::DeserializeOwned, - O: serde::Serialize, -{ - let mut lambda_runtime: Runtime<_, E, O>; - match Runtime::new(f, func_settings, MAX_RETRIES, client) { - Ok(r) => lambda_runtime = r, - Err(e) => { - panic!("Error while starting runtime: {}", e); - } - } - - // start the infinite loop - lambda_runtime.start(); -} - -/// Internal representation of the runtime object that polls for events and communicates -/// with the Runtime APIs -pub(super) struct Runtime { - runtime_client: RuntimeClient, - handler: F, - max_retries: i8, - settings: FunctionSettings, - _phan: PhantomData<(E, O)>, -} - -// generic methods implementation -impl Runtime { - /// Creates a new instance of the `Runtime` object populated with the environment - /// settings. - /// - /// # Arguments - /// - /// * `f` A function pointer that conforms to the `Handler` type. - /// * `retries` The maximum number of times we should retry calling the Runtime APIs - /// for recoverable errors while polling for new events. - /// - /// # Return - /// A `Result` for the `Runtime` object or a `errors::RuntimeSerror`. The runtime - /// fails the init if this function returns an error. If we cannot find the - /// `AWS_LAMBDA_RUNTIME_API` variable in the environment the function panics. - pub(super) fn new( - f: F, - config: FunctionSettings, - retries: i8, - client: RuntimeClient, - ) -> result::Result { - debug!( - "Creating new runtime with {} max retries for endpoint {}", - retries, - client.get_endpoint() - ); - Ok(Runtime { - runtime_client: client, - settings: config, - handler: f, - max_retries: retries, - _phan: PhantomData, - }) - } -} - -// implementation of methods that require the Event and Output types -// to be compatible with `serde`'s Deserialize/Serialize. -impl Runtime -where - F: Handler, - E: serde::de::DeserializeOwned, - O: serde::Serialize, -{ - /// Starts the main event loop and begin polling or new events. If one of the - /// Runtime APIs returns an unrecoverable error this method calls the init failed - /// API and then panics. - fn start(&mut self) { - debug!("Beginning main event loop"); - loop { - let (event, ctx) = self.get_next_event(0, None); - let request_id = ctx.aws_request_id.clone(); - info!("Received new event with AWS request id: {}", request_id); - let function_outcome = self.invoke(event, ctx); - match function_outcome { - Ok(response) => { - debug!( - "Function executed succesfully for {}, pushing response to Runtime API", - request_id - ); - match serde_json::to_vec(&response) { - Ok(response_bytes) => { - match self.runtime_client.event_response(&request_id, response_bytes) { - Ok(_) => info!("Response for {} accepted by Runtime API", request_id), - // unrecoverable error while trying to communicate with the endpoint. - // we let the Lambda Runtime API know that we have died - Err(e) => { - error!("Could not send response for {} to Runtime API: {}", request_id, e); - if !e.recoverable { - error!( - "Error for {} is not recoverable, sending fail_init signal and panicking.", - request_id - ); - self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); - panic!("Could not send response"); - } - } - } - } - Err(e) => { - error!( - "Could not marshal output object to Vec JSON represnetation for request {}: {}", - request_id, e - ); - self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); - panic!("Failed to marshal handler output, panic"); - } - } - } - Err(e) => { - debug!("Handler returned an error for {}: {}", request_id, e); - debug!("Attempting to send error response to Runtime API for {}", request_id); - match self - .runtime_client - .event_error(&request_id, ErrorResponse::from(Box::new(e))) - { - Ok(_) => info!("Error response for {} accepted by Runtime API", request_id), - Err(e) => { - error!("Unable to send error response for {} to Runtime API: {}", request_id, e); - if !e.recoverable { - error!( - "Error for {} is not recoverable, sending fail_init signal and panicking", - request_id - ); - self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); - panic!("Could not send error response"); - } - } - } - } - } - } - } - - /// Invoke the handler function. This method is split out of the main loop to - /// make it testable. - pub(super) fn invoke(&mut self, e: E, ctx: Context) -> Result { - (&mut self.handler).run(e, ctx) - } - - /// Attempts to get the next event from the Runtime APIs and keeps retrying - /// unless the error throws is not recoverable. - /// - /// # Return - /// The next `Event` object to be processed. - pub(super) fn get_next_event(&self, retries: i8, e: Option) -> (E, Context) { - if let Some(err) = e { - if retries > self.max_retries { - error!("Unrecoverable error while fetching next event: {}", err); - match err.request_id.clone() { - Some(req_id) => { - self.runtime_client - .event_error(&req_id, ErrorResponse::from(Box::new(err))) - .expect("Could not send event error response"); - } - None => { - self.runtime_client.fail_init(ErrorResponse::from(Box::new(err))); - } - } - - // these errors are not recoverable. Either we can't communicate with the runtie APIs - // or we cannot parse the event. panic to restart the environment. - panic!("Could not retrieve next event"); - } - } - - match self.runtime_client.next_event() { - Ok((ev_data, invocation_ctx)) => { - let parse_result = serde_json::from_slice(&ev_data); - match parse_result { - Ok(ev) => { - let mut handler_ctx = Context::new(self.settings.clone()); - handler_ctx.invoked_function_arn = invocation_ctx.invoked_function_arn; - handler_ctx.aws_request_id = invocation_ctx.aws_request_id; - handler_ctx.xray_trace_id = invocation_ctx.xray_trace_id; - handler_ctx.client_context = invocation_ctx.client_context; - handler_ctx.identity = invocation_ctx.identity; - handler_ctx.deadline = invocation_ctx.deadline; - - (ev, handler_ctx) - } - Err(mut e) => { - error!("Could not parse event to type: {}", e); - let mut runtime_err = RuntimeError::from(e); - runtime_err.request_id = Option::from(invocation_ctx.aws_request_id); - self.get_next_event(retries + 1, Option::from(runtime_err)) - } - } - } - Err(e) => self.get_next_event(retries + 1, Option::from(RuntimeError::from(e))), - } - } -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use context; - use env; - use lambda_runtime_client::RuntimeClient; - - #[test] - fn runtime_invokes_handler() { - let config: &env::ConfigProvider = &env::tests::MockConfigProvider { error: false }; - let client = RuntimeClient::new( - config - .get_runtime_api_endpoint() - .expect("Could not get runtime endpoint"), - None, - ) - .expect("Could not initialize client"); - let handler = |_e: String, _c: context::Context| -> Result { Ok("hello".to_string()) }; - let retries: i8 = 3; - let runtime = Runtime::new( - handler, - config - .get_function_settings() - .expect("Could not load environment config"), - retries, - client, - ); - assert_eq!( - runtime.is_err(), - false, - "Runtime threw an unexpected error: {}", - runtime.err().unwrap() - ); - let output = runtime - .unwrap() - .invoke(String::from("test"), context::tests::test_context(10)); - assert_eq!( - output.is_err(), - false, - "Handler threw an unexpected error: {}", - output.err().unwrap() - ); - let output_string = output.ok().unwrap(); - assert_eq!(output_string, "hello", "Unexpected output message: {}", output_string); - } -} From ab54a2e05d272399d0f30a09ddf7178f9ad1f162 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 15:54:01 -0800 Subject: [PATCH 18/43] Moved HandlerError to error module for backward compatibility --- lambda-runtime/examples/basic.rs | 2 +- lambda-runtime/examples/with_custom_runtime.rs | 2 +- lambda-runtime/src/lib.rs | 15 ++++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lambda-runtime/examples/basic.rs b/lambda-runtime/examples/basic.rs index a7cc40af..480cf46d 100644 --- a/lambda-runtime/examples/basic.rs +++ b/lambda-runtime/examples/basic.rs @@ -1,6 +1,6 @@ use std::error::Error; -use lambda_runtime::{lambda, Context, HandlerError}; +use lambda_runtime::{error::HandlerError, lambda, Context}; use log::{self, error}; use serde_derive::{Deserialize, Serialize}; use simple_error::bail; diff --git a/lambda-runtime/examples/with_custom_runtime.rs b/lambda-runtime/examples/with_custom_runtime.rs index 5ba8a88f..f7ab817e 100644 --- a/lambda-runtime/examples/with_custom_runtime.rs +++ b/lambda-runtime/examples/with_custom_runtime.rs @@ -1,4 +1,4 @@ -use lambda_runtime::{lambda, Context, HandlerError}; +use lambda_runtime::{error::HandlerError, lambda, Context}; use log::{self, error}; use serde_derive::{Deserialize, Serialize}; use simple_error::bail; diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 6dd447a3..f521f9e8 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -44,21 +44,26 @@ use serde; use serde_json; use tokio::runtime::Runtime as TokioRuntime; -pub use lambda_runtime_core::{Context, HandlerError}; +pub use lambda_runtime_core::Context; + +/// The error module exposes the HandlerError type. +pub mod error { + pub use lambda_runtime_core::HandlerError; +} /// Functions acting as a handler must conform to this type. pub trait Handler { /// Method to execute the handler function - fn run(&mut self, event: E, ctx: Context) -> Result; + fn run(&mut self, event: E, ctx: Context) -> Result; } /// Implementation of the `Handler` trait for both function pointers /// and closures. impl Handler for F where - F: FnMut(E, Context) -> Result, + F: FnMut(E, Context) -> Result, { - fn run(&mut self, event: E, ctx: Context) -> Result { + fn run(&mut self, event: E, ctx: Context) -> Result { (*self)(event, ctx) } } @@ -67,7 +72,7 @@ where /// defined in the `lambda_runtime_core` crate. The closure simply uses `serde_json` /// to serialize and deserialize the incoming event from a `Vec` and the output /// to a `Vec`. -fn wrap(mut h: impl Handler) -> impl FnMut(Vec, Context) -> Result, HandlerError> +fn wrap(mut h: impl Handler) -> impl FnMut(Vec, Context) -> Result, error::HandlerError> where E: serde::de::DeserializeOwned, O: serde::Serialize, From 99b13284b5687999778cc42d7d6b90a2fe9e204f Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 18:09:10 -0800 Subject: [PATCH 19/43] Fixed error package in examples --- lambda-runtime/examples/custom_error.rs | 2 +- lambda-runtime/examples/custom_error_failure.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs index d9e18783..04bdc51b 100644 --- a/lambda-runtime/examples/custom_error.rs +++ b/lambda-runtime/examples/custom_error.rs @@ -3,7 +3,7 @@ extern crate log; extern crate serde_derive; extern crate simple_logger; -use lambda::{lambda, HandlerError}; +use lambda::{error::HandlerError, lambda}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::{error::Error, fmt}; diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index cfa9a47a..2e014e28 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -5,7 +5,7 @@ extern crate serde_derive; extern crate simple_logger; use failure::Fail; -use lambda::{lambda, HandlerError}; +use lambda::{lambda, error::HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error as StdError; From 3844b435a181f351bc131dd150db5679d5c5a8f5 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 20:10:08 -0800 Subject: [PATCH 20/43] Addressing CR on newline at the end of file and import grouping --- lambda-runtime-client/src/lib.rs | 2 +- lambda-runtime-core/src/env.rs | 3 +-- lambda-runtime-core/src/handler.rs | 4 ++-- lambda-runtime-core/src/lib.rs | 10 ++++++---- lambda-runtime-core/src/runtime.rs | 13 +++++++------ lambda-runtime/examples/custom_error_failure.rs | 2 +- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lambda-runtime-client/src/lib.rs b/lambda-runtime-client/src/lib.rs index 6ab0daef..1f216fab 100644 --- a/lambda-runtime-client/src/lib.rs +++ b/lambda-runtime-client/src/lib.rs @@ -46,7 +46,7 @@ //! let resp_object = CustomResponse{ surname: String::from("Doe")}; //! let resp_vec = serde_json::to_vec(&resp_object) //! .expect("Could not serialize CustomResponse to Vec"); -//! client.event_response(&event_context.aws_request_id, resp_vec) +//! client.event_response(&event_context.aws_request_id, &resp_vec) //! .expect("Response sent successfully"); //! } else { //! // return a custom error by implementing the RuntimeApiError trait. diff --git a/lambda-runtime-core/src/env.rs b/lambda-runtime-core/src/env.rs index d24c0b36..d21e05aa 100644 --- a/lambda-runtime-core/src/env.rs +++ b/lambda-runtime-core/src/env.rs @@ -89,8 +89,7 @@ impl ConfigProvider for EnvConfigProvider { #[cfg(test)] pub(crate) mod tests { - use crate::env::*; - use crate::error; + use crate::{env::*, error}; use std::{env, error::Error}; pub(crate) struct MockConfigProvider { diff --git a/lambda-runtime-core/src/handler.rs b/lambda-runtime-core/src/handler.rs index a39dfaa4..dd2d7cdb 100644 --- a/lambda-runtime-core/src/handler.rs +++ b/lambda-runtime-core/src/handler.rs @@ -1,5 +1,5 @@ use crate::context::Context; -use std::{fmt::Display, fmt::Debug, fmt}; +use std::fmt::{self, Debug, Display}; /// Functions acting as a handler must conform to this type. pub trait Handler { @@ -35,4 +35,4 @@ impl Display for HandlerError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.msg) } -} \ No newline at end of file +} diff --git a/lambda-runtime-core/src/lib.rs b/lambda-runtime-core/src/lib.rs index 306d2074..fc75a9af 100644 --- a/lambda-runtime-core/src/lib.rs +++ b/lambda-runtime-core/src/lib.rs @@ -15,7 +15,9 @@ mod error; mod handler; mod runtime; -pub use crate::context::Context; -pub use crate::env::{ConfigProvider, EnvConfigProvider}; -pub use crate::handler::{Handler, HandlerError}; -pub use crate::runtime::*; +pub use crate::{ + context::Context, + env::{ConfigProvider, EnvConfigProvider}, + handler::{Handler, HandlerError}, + runtime::*, +}; diff --git a/lambda-runtime-core/src/runtime.rs b/lambda-runtime-core/src/runtime.rs index 9fc6d1dd..899dea76 100644 --- a/lambda-runtime-core/src/runtime.rs +++ b/lambda-runtime-core/src/runtime.rs @@ -1,7 +1,9 @@ -use crate::context::Context; -use crate::env::{ConfigProvider, EnvConfigProvider, FunctionSettings}; -use crate::error::RuntimeError; -use crate::handler::{Handler, HandlerError}; +use crate::{ + context::Context, + env::{ConfigProvider, EnvConfigProvider, FunctionSettings}, + error::RuntimeError, + handler::{Handler, HandlerError}, +}; use lambda_runtime_client::{error::ErrorResponse, RuntimeClient}; use log::*; use tokio::runtime::Runtime as TokioRuntime; @@ -256,8 +258,7 @@ where #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::context; - use crate::env; + use crate::{context, env}; use lambda_runtime_client::RuntimeClient; #[test] diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index 2e014e28..215f8429 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -5,7 +5,7 @@ extern crate serde_derive; extern crate simple_logger; use failure::Fail; -use lambda::{lambda, error::HandlerError}; +use lambda::{error::HandlerError, lambda}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error as StdError; From fa2a5b1f4c15fdc1681085d326b97d456263ae7c Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 20:17:55 -0800 Subject: [PATCH 21/43] More style changes from code review --- lambda-runtime/Cargo.toml | 2 - lambda-runtime/examples/custom_error.rs | 9 +- .../examples/custom_error_failure.rs | 10 +- lambda-runtime/src/error_ext_impl.rs | 133 ------------------ 4 files changed, 4 insertions(+), 150 deletions(-) delete mode 100644 lambda-runtime/src/error_ext_impl.rs diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 82bc1c2d..3f283572 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -27,5 +27,3 @@ lambda_runtime_core = { path = "../lambda-runtime-core", version = "^0.1" } simple_logger = "^1" simple-error = "^0.1" failure = "^0.1" -#rusoto_core = "^0.35" -#rusoto_dynamodb = "^0.35" diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs index 04bdc51b..634c4222 100644 --- a/lambda-runtime/examples/custom_error.rs +++ b/lambda-runtime/examples/custom_error.rs @@ -1,9 +1,4 @@ -extern crate lambda_runtime as lambda; -extern crate log; -extern crate serde_derive; -extern crate simple_logger; - -use lambda::{error::HandlerError, lambda}; +use lambda_runtime::{error::HandlerError, lambda, Context}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::{error::Error, fmt}; @@ -48,7 +43,7 @@ fn main() -> Result<(), Box> { Ok(()) } -fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { +fn my_handler(e: CustomEvent, c: Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); // in this case, we explicitly initialize and box our custom error type. diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index 215f8429..b828eadf 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -1,11 +1,5 @@ -extern crate failure; -extern crate lambda_runtime as lambda; -extern crate log; -extern crate serde_derive; -extern crate simple_logger; - use failure::Fail; -use lambda::{error::HandlerError, lambda}; +use lambda_runtime::{error::HandlerError, lambda, Context}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error as StdError; @@ -33,7 +27,7 @@ fn main() -> Result<(), Box> { Ok(()) } -fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { +fn my_handler(e: CustomEvent, c: Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); return Err((CustomError {}).into()); diff --git a/lambda-runtime/src/error_ext_impl.rs b/lambda-runtime/src/error_ext_impl.rs deleted file mode 100644 index a8a890b4..00000000 --- a/lambda-runtime/src/error_ext_impl.rs +++ /dev/null @@ -1,133 +0,0 @@ -// Generated code, DO NOT MODIFY! - -use error::LambdaErrorExt; -use std::{ - alloc::LayoutErr, - cell::{BorrowError, BorrowMutError}, - char::{DecodeUtf16Error, ParseCharError}, - env::{JoinPathsError, VarError}, - ffi::{FromBytesWithNulError, IntoStringError, NulError}, - net::AddrParseError, - num::{ParseFloatError, ParseIntError}, - path::StripPrefixError, - str::{ParseBoolError, Utf8Error}, - string::{FromUtf16Error, FromUtf8Error, ParseError}, - sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError}, - time::SystemTimeError, -}; - -impl LambdaErrorExt for VarError { - fn error_type(&self) -> &str { - "VarError" - } -} -impl LambdaErrorExt for ParseError { - fn error_type(&self) -> &str { - "ParseError" - } -} -impl LambdaErrorExt for RecvTimeoutError { - fn error_type(&self) -> &str { - "RecvTimeoutError" - } -} -impl LambdaErrorExt for TryRecvError { - fn error_type(&self) -> &str { - "TryRecvError" - } -} -impl LambdaErrorExt for LayoutErr { - fn error_type(&self) -> &str { - "LayoutErr" - } -} -impl LambdaErrorExt for BorrowError { - fn error_type(&self) -> &str { - "BorrowError" - } -} -impl LambdaErrorExt for BorrowMutError { - fn error_type(&self) -> &str { - "BorrowMutError" - } -} -impl LambdaErrorExt for DecodeUtf16Error { - fn error_type(&self) -> &str { - "DecodeUtf16Error" - } -} -impl LambdaErrorExt for ParseCharError { - fn error_type(&self) -> &str { - "ParseCharError" - } -} -impl LambdaErrorExt for JoinPathsError { - fn error_type(&self) -> &str { - "JoinPathsError" - } -} -impl LambdaErrorExt for FromBytesWithNulError { - fn error_type(&self) -> &str { - "FromBytesWithNulError" - } -} -impl LambdaErrorExt for IntoStringError { - fn error_type(&self) -> &str { - "IntoStringError" - } -} -impl LambdaErrorExt for NulError { - fn error_type(&self) -> &str { - "NulError" - } -} -impl LambdaErrorExt for AddrParseError { - fn error_type(&self) -> &str { - "AddrParseError" - } -} -impl LambdaErrorExt for ParseFloatError { - fn error_type(&self) -> &str { - "ParseFloatError" - } -} -impl LambdaErrorExt for ParseIntError { - fn error_type(&self) -> &str { - "ParseIntError" - } -} -impl LambdaErrorExt for StripPrefixError { - fn error_type(&self) -> &str { - "StripPrefixError" - } -} -impl LambdaErrorExt for ParseBoolError { - fn error_type(&self) -> &str { - "ParseBoolError" - } -} -impl LambdaErrorExt for Utf8Error { - fn error_type(&self) -> &str { - "Utf8Error" - } -} -impl LambdaErrorExt for FromUtf16Error { - fn error_type(&self) -> &str { - "FromUtf16Error" - } -} -impl LambdaErrorExt for FromUtf8Error { - fn error_type(&self) -> &str { - "FromUtf8Error" - } -} -impl LambdaErrorExt for RecvError { - fn error_type(&self) -> &str { - "RecvError" - } -} -impl LambdaErrorExt for SystemTimeError { - fn error_type(&self) -> &str { - "SystemTimeError" - } -} From b6423f7c29efd6e4c9798c5f4ff2aa3b378bd103 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 27 Dec 2018 20:46:31 -0800 Subject: [PATCH 22/43] Fixed tests, including http crate --- lambda-http/src/ext.rs | 4 ++-- lambda-http/src/lib.rs | 2 +- lambda-runtime/src/lib.rs | 8 ++------ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lambda-http/src/ext.rs b/lambda-http/src/ext.rs index 6b4c76ab..44699735 100644 --- a/lambda-http/src/ext.rs +++ b/lambda-http/src/ext.rs @@ -41,7 +41,7 @@ pub enum PayloadError { /// extern crate lambda_runtime as lambda; /// #[macro_use] extern crate serde_derive; /// -/// use lambda::{Context, HandlerError}; +/// use lambda::{Context, error::HandlerError}; /// use lambda_http::{Body, Request, Response, RequestExt}; /// /// #[derive(Debug,Deserialize,Default)] @@ -59,7 +59,7 @@ pub enum PayloadError { /// fn handler( /// request: Request, /// ctx: lambda::Context -/// ) -> Result, lambda::HandlerError> { +/// ) -> Result, HandlerError> { /// let args: Args = request.payload() /// .unwrap_or_else(|_parse_err| None) /// .unwrap_or_default(); diff --git a/lambda-http/src/lib.rs b/lambda-http/src/lib.rs index 8014a0fd..73d74337 100644 --- a/lambda-http/src/lib.rs +++ b/lambda-http/src/lib.rs @@ -6,7 +6,7 @@ //! //! ```rust,no_run //! use lambda_http::{lambda, IntoResponse, Request, RequestExt}; -//! use lambda_runtime::{Context, HandlerError}; +//! use lambda_runtime::{Context, error::HandlerError}; //! //! fn main() { //! lambda!(handler) diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index f521f9e8..189d8d84 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -7,11 +7,7 @@ //! package must be called `bootstrap`. //! //! ```rust,no_run -//! extern crate serde_derive; -//! extern crate lambda_runtime; -//! extern crate simple_error; -//! -//! use lambda_runtime::{HandlerError, lambda}; +//! use lambda_runtime::{error::HandlerError, lambda, Context}; //! use simple_error::bail; //! use serde_derive::{Serialize, Deserialize}; //! @@ -30,7 +26,7 @@ //! lambda!(my_handler); //! } //! -//! fn my_handler(e: CustomEvent, ctx: lambda_runtime::Context) -> Result { +//! fn my_handler(e: CustomEvent, ctx: Context) -> Result { //! if e.first_name == "" { //! bail!("Empty first name"); //! } From 77459b5c143e2f2b139bf507e5ec0bd78bc0e51d Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 28 Dec 2018 10:26:00 -0800 Subject: [PATCH 23/43] First refactor of errors in the client crate and new errors crate --- lambda-runtime-client/Cargo.toml | 3 +- lambda-runtime-client/src/client.rs | 266 +++++++++----------- lambda-runtime-client/src/error.rs | 145 ++++------- lambda-runtime-errors/Cargo.toml | 15 ++ lambda-runtime-errors/errorgen.py | 118 +++++++++ lambda-runtime-errors/src/error_ext_impl.rs | 133 ++++++++++ lambda-runtime-errors/src/lib.rs | 21 ++ 7 files changed, 458 insertions(+), 243 deletions(-) create mode 100644 lambda-runtime-errors/Cargo.toml create mode 100644 lambda-runtime-errors/errorgen.py create mode 100644 lambda-runtime-errors/src/error_ext_impl.rs create mode 100644 lambda-runtime-errors/src/lib.rs diff --git a/lambda-runtime-client/Cargo.toml b/lambda-runtime-client/Cargo.toml index 3982ef56..521a8e4c 100644 --- a/lambda-runtime-client/Cargo.toml +++ b/lambda-runtime-client/Cargo.toml @@ -22,5 +22,6 @@ http = "0.1" serde = "^1" serde_json = "^1" serde_derive = "^1" +failure = "^0.1" log = "0.4" -backtrace = "0.3" +lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 41cb4ef4..48f84a25 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,4 +1,5 @@ -use crate::error::{ApiError, ErrorResponse, ERROR_TYPE_UNHANDLED}; +use crate::error::{ApiError, ApiErrorKind, ErrorResponse, ERROR_TYPE_UNHANDLED}; +use failure::{Error, ResultExt}; use hyper::{ client::HttpConnector, header::{self, HeaderMap, HeaderValue}, @@ -124,21 +125,24 @@ pub struct EventContext { pub struct RuntimeClient { _runtime: Runtime, http_client: Client, - endpoint: String, + endpoint: Uri, } impl<'ev> RuntimeClient { /// Creates a new instance of the Runtime APIclient SDK. The http client has timeouts disabled and /// will always send a `Connection: keep-alive` header. - pub fn new(endpoint: String, runtime: Option) -> Result { - debug!("Starting new HttpRuntimeClient for {}", endpoint); + pub fn new(host: &str, runtime: Option) -> Result { + debug!("Starting new HttpRuntimeClient for {}", host); // start a tokio core main event loop for hyper let runtime = match runtime { Some(r) => r, - None => Runtime::new()?, + None => Runtime::new().context(ApiErrorKind::Unrecoverable("Could not initialize runtime".to_string()))?, }; let http_client = Client::builder().executor(runtime.executor()).build_http(); + let endpoint = format!("http://{}/{}/runtime/invocation/next", host, RUNTIME_API_VERSION) + .parse::() + .context(ApiErrorKind::Unrecoverable("Could not parse API uri".to_string()))?; Ok(RuntimeClient { _runtime: runtime, @@ -150,55 +154,47 @@ impl<'ev> RuntimeClient { impl<'ev> RuntimeClient { /// Polls for new events to the Runtime APIs. - pub fn next_event(&self) -> Result<(Vec, EventContext), ApiError> { - let uri = format!( - "http://{}/{}/runtime/invocation/next", - self.endpoint, RUNTIME_API_VERSION - ) - .parse()?; + pub fn next_event(&self) -> Result<(Vec, EventContext), Error> { trace!("Polling for next event"); // We wait instead of processing the future asynchronously because AWS Lambda // itself enforces only one event per container at a time. No point in taking on // the additional complexity. - let out = self.http_client.get(uri).wait(); - match out { - Ok(resp) => { - if resp.status().is_client_error() { - error!( - "Runtime API returned client error when polling for new events: {}", - resp.status() - ); - return Err(ApiError::new(&format!( - "Error {} when polling for events", - resp.status() - ))); - } - if resp.status().is_server_error() { - error!( - "Runtime API returned server error when polling for new events: {}", - resp.status() - ); - return Err(ApiError::new("Server error when polling for new events") - .unrecoverable() - .clone()); - } - let ctx = self.get_event_context(&resp.headers())?; - let out = resp.into_body().concat2().wait()?; - let buf = out.into_bytes().to_vec(); - - trace!( - "Received new event for request id {}. Event length {} bytes", - ctx.aws_request_id, - buf.len() - ); - Ok((buf, ctx)) - } - Err(e) => { - error!("Error when fetching next event from Runtime API: {}", e); - Err(ApiError::from(e)) - } + let resp = self + .http_client + .get(self.endpoint.clone()) + .wait() + .context(ApiErrorKind::Unrecoverable("Could not fetch next event".to_string()))?; + + if resp.status().is_client_error() { + error!( + "Runtime API returned client error when polling for new events: {}", + resp.status() + ); + Err(ApiErrorKind::Recoverable(format!( + "Error {} when polling for events", + resp.status() + )))?; + } + if resp.status().is_server_error() { + error!( + "Runtime API returned server error when polling for new events: {}", + resp.status() + ); + Err(ApiErrorKind::Unrecoverable( + "Server error when polling for new events".to_string(), + ))?; } + let ctx = self.get_event_context(&resp.headers())?; + let out = resp.into_body().concat2().wait()?; + let buf = out.into_bytes().to_vec(); + + trace!( + "Received new event for request id {}. Event length {} bytes", + ctx.aws_request_id, + buf.len() + ); + Ok((buf, ctx)) } /// Calls the Lambda Runtime APIs to submit a response to an event. In this function we treat @@ -214,39 +210,31 @@ impl<'ev> RuntimeClient { /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. pub fn event_response(&self, request_id: &str, output: &[u8]) -> Result<(), ApiError> { - let uri: Uri = format!( - "http://{}/{}/runtime/invocation/{}/response", - self.endpoint, RUNTIME_API_VERSION, request_id - ) - .parse()?; trace!( "Posting response for request {} to Runtime API. Response length {} bytes", request_id, output.len() ); - let req = self.get_runtime_post_request(&uri, output); - - match self.http_client.request(req).wait() { - Ok(resp) => { - if !resp.status().is_success() { - error!( - "Error from Runtime API when posting response for request {}: {}", - request_id, - resp.status() - ); - return Err(ApiError::new(&format!( - "Error {} while sending response", - resp.status() - ))); - } - trace!("Posted response to Runtime API for request {}", request_id); - Ok(()) - } - Err(e) => { - error!("Error when calling runtime API for request {}: {}", request_id, e); - Err(ApiError::from(e)) - } + let req = self.get_runtime_post_request(&self.endpoint, output); + + let resp = self + .http_client + .request(req) + .wait() + .context(ApiErrorKind::Recoverable("Could not post event response".to_string()))?; + if !resp.status().is_success() { + error!( + "Error from Runtime API when posting response for request {}: {}", + request_id, + resp.status() + ); + Err(ApiErrorKind::Recoverable(format!( + "Error {} while sending response", + resp.status() + )))?; } + trace!("Posted response to Runtime API for request {}", request_id); + Ok(()) } /// Calls Lambda's Runtime APIs to send an error generated by the `Handler`. Because it's rust, @@ -262,39 +250,29 @@ impl<'ev> RuntimeClient { /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. pub fn event_error(&self, request_id: &str, e: &ErrorResponse) -> Result<(), ApiError> { - let uri: Uri = format!( - "http://{}/{}/runtime/invocation/{}/error", - self.endpoint, RUNTIME_API_VERSION, request_id - ) - .parse()?; trace!( "Posting error to runtime API for request {}: {}", request_id, e.error_message ); - let req = self.get_runtime_error_request(&uri, &e); - - match self.http_client.request(req).wait() { - Ok(resp) => { - if !resp.status().is_success() { - error!( - "Error from Runtime API when posting error response for request {}: {}", - request_id, - resp.status() - ); - return Err(ApiError::new(&format!( - "Error {} while sending response", - resp.status() - ))); - } - trace!("Posted error response for request id {}", request_id); - Ok(()) - } - Err(e) => { - error!("Error when calling runtime API for request {}: {}", request_id, e); - Err(ApiError::from(e)) - } + let req = self.get_runtime_error_request(&self.endpoint, &e); + + let resp = self.http_client.request(req).wait().context(ApiErrorKind::Recoverable( + "Could not post event error response".to_string(), + ))?; + if !resp.status().is_success() { + error!( + "Error from Runtime API when posting error response for request {}: {}", + request_id, + resp.status() + ); + Err(ApiErrorKind::Recoverable(format!( + "Error {} while sending response", + resp.status() + )))?; } + trace!("Posted error response for request id {}", request_id); + Ok(()) } /// Calls the Runtime APIs to report a failure during the init process. @@ -309,11 +287,8 @@ impl<'ev> RuntimeClient { /// If it cannot send the init error. In this case we panic to force the runtime /// to restart. pub fn fail_init(&self, e: &ErrorResponse) { - let uri: Uri = format!("http://{}/{}/runtime/init/error", self.endpoint, RUNTIME_API_VERSION) - .parse() - .expect("Could not generate Runtime URI"); error!("Calling fail_init Runtime API: {}", e.error_message); - let req = self.get_runtime_error_request(&uri, &e); + let req = self.get_runtime_error_request(&self.endpoint, &e); self.http_client .request(req) @@ -330,7 +305,7 @@ impl<'ev> RuntimeClient { /// Returns the endpoint configured for this HTTP Runtime client. pub fn get_endpoint(&self) -> String { - self.endpoint.clone() + format!("{}", self.endpoint) } /// Creates a Hyper `Request` object for the given `Uri` and `Body`. Sets the @@ -382,37 +357,20 @@ impl<'ev> RuntimeClient { fn get_event_context(&self, headers: &HeaderMap) -> Result { // let headers = resp.headers(); - let aws_request_id = match headers.get(LambdaHeaders::RequestId.as_str()) { - Some(value) => value.to_str()?.to_owned(), - None => { - error!("Response headers do not contain request id header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::RequestId))); - } - }; - - let invoked_function_arn = match headers.get(LambdaHeaders::FunctionArn.as_str()) { - Some(value) => value.to_str()?.to_owned(), - None => { - error!("Response headers do not contain function arn header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::FunctionArn))); - } - }; - - let xray_trace_id = match headers.get(LambdaHeaders::TraceId.as_str()) { - Some(value) => value.to_str()?.to_owned(), - None => { - error!("Response headers do not contain trace id header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::TraceId))); - } - }; - - let deadline = match headers.get(LambdaHeaders::Deadline.as_str()) { - Some(value) => value.to_str()?.parse()?, - None => { - error!("Response headers do not contain deadline header"); - return Err(ApiError::new(&format!("Missing {} header", LambdaHeaders::Deadline))); - } - }; + let aws_request_id = header_string( + headers.get(LambdaHeaders::RequestId.as_str()), + &LambdaHeaders::RequestId, + )?; + let invoked_function_arn = header_string( + headers.get(LambdaHeaders::FunctionArn.as_str()), + &LambdaHeaders::FunctionArn, + )?; + let xray_trace_id = header_string(headers.get(LambdaHeaders::TraceId.as_str()), &LambdaHeaders::TraceId)?; + let deadline = header_string(headers.get(LambdaHeaders::Deadline.as_str()), &LambdaHeaders::Deadline)? + .parse::() + .context(ApiErrorKind::Recoverable( + "Could not parse deadline header value to int".to_string(), + ))?; let mut ctx = EventContext { aws_request_id, @@ -424,19 +382,43 @@ impl<'ev> RuntimeClient { }; if let Some(ctx_json) = headers.get(LambdaHeaders::ClientContext.as_str()) { - let ctx_json = ctx_json.to_str()?; + let ctx_json = ctx_json.to_str().context(ApiErrorKind::Recoverable( + "Could not convert context header content to string".to_string(), + ))?; trace!("Found Client Context in response headers: {}", ctx_json); - let ctx_value: ClientContext = serde_json::from_str(&ctx_json)?; + let ctx_value: ClientContext = serde_json::from_str(&ctx_json).context(ApiErrorKind::Recoverable( + "Could not parse client context value as json object".to_string(), + ))?; ctx.client_context = Option::from(ctx_value); }; if let Some(cognito_json) = headers.get(LambdaHeaders::CognitoIdentity.as_str()) { - let cognito_json = cognito_json.to_str()?; + let cognito_json = cognito_json.to_str().context(ApiErrorKind::Recoverable( + "Could not convert congnito context header content to string".to_string(), + ))?; trace!("Found Cognito Identity in response headers: {}", cognito_json); - let identity_value: CognitoIdentity = serde_json::from_str(&cognito_json)?; + let identity_value: CognitoIdentity = serde_json::from_str(&cognito_json).context( + ApiErrorKind::Recoverable("Could not parse cognito context value as json object".to_string()), + )?; ctx.identity = Option::from(identity_value); }; Ok(ctx) } } + +fn header_string(value: Option<&HeaderValue>, header_type: &LambdaHeaders) -> Result { + match value { + Some(value_str) => Ok(value_str + .to_str() + .context(ApiErrorKind::Recoverable(format!( + "Could not parse {} header", + header_type + )))? + .to_owned()), + None => { + error!("Response headers do not contain {} header", header_type); + Err(ApiErrorKind::Recoverable(format!("Missing {} header", header_type)))? + } + } +} diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 8c0b9888..9fc9d4e9 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -1,20 +1,13 @@ //! This module defines the `RuntimeApiError` trait that developers should implement //! to send their custom errors to the AWS Lambda Runtime Client SDK. The module also //! defines the `ApiError` type returned by the `RuntimeClient` implementations. -use backtrace; -use http::{header::ToStrError, uri::InvalidUri}; +use failure::{AsFail, Backtrace, Context, Fail}; use hyper; +use lambda_runtime_errors::LambdaErrorExt; use log::*; use serde_derive::*; use serde_json; -use std::{ - env, - error::Error, - fmt::{self, Display}, - io, - num::ParseIntError, - option::Option, -}; +use std::{fmt, fmt::Display, option::Option}; /// Error type description for the `ErrorResponse` event. This type should be returned /// for errors that were handled by the function code or framework. @@ -60,134 +53,86 @@ impl ErrorResponse { /// /// # Return /// A new instance of the `ErrorResponse` object. - fn new(message: String, err_type: String) -> ErrorResponse { + fn new(message: String, err_type: String, backtrace: Option<&Backtrace>) -> Self { let mut err = ErrorResponse { error_message: message, error_type: err_type, stack_trace: Option::default(), }; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { + // assume that failure is smart enough to only collect a backtrace + // if the env variable is enabled + if let Some(stack) = backtrace { trace!("Begin backtrace collection"); - let trace = Option::from(backtrace::Backtrace::new()); - let trace_string = format!("{:?}", trace) + let trace_string = format!("{:?}", stack) .lines() .map(|s| s.to_string()) .collect::>(); trace!("Completed backtrace collection"); err.stack_trace = Option::from(trace_string); } - err - } - /// Creates a new `RuntimeError` object with the handled error type. - /// - /// # Arguments - /// - /// * `message` The error message for the Lambda Runtime APIs. - /// - /// # Return - /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. - pub fn handled(message: String) -> ErrorResponse { - ErrorResponse::new(message, RUNTIME_ERROR_TYPE.to_owned()) - } - - /// Creates a new `RuntimeError` object with the unhandled error type. - /// - /// # Arguments - /// - /// * `message` The error message for the Lambda Runtime APIs. - /// - /// # Return - /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. - pub fn unhandled(message: String) -> ErrorResponse { - ErrorResponse::new(message, RUNTIME_ERROR_TYPE.to_owned()) + err } } -impl From> for ErrorResponse { - fn from(e: Box) -> Self { - Self::handled(format!("{}", e)) +impl From for ErrorResponse { + fn from(e: T) -> Self { + ErrorResponse::new(format!("{}", e), e.error_type().to_owned(), e.as_fail().backtrace()) } } /// Represents an error generated by the Lambda Runtime API client. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ApiError { - msg: String, - /// Whether the current error is recoverable. If the error is not - /// recoverable a runtime should panic to force the Lambda service - /// to restart the execution environment. - pub recoverable: bool, + inner: Context, } -impl ApiError { - pub(crate) fn new(description: &str) -> ApiError { - ApiError { - msg: String::from(description), - recoverable: true, - } - } - - pub(crate) fn unrecoverable(&mut self) -> &ApiError { - self.recoverable = false; - - self - } -} - -impl fmt::Display for ApiError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.msg) - } +/// Failure context for the `ApiError` type. The kind is used to indicate whether the +/// error is recoverable and should be retried or not. +#[derive(Clone, Eq, PartialEq, Debug, Fail)] +pub enum ApiErrorKind { + /// Runtime implementations that receive recoverable errors should automatically + /// retry requests + #[fail(display = "Recoverable API error: {}", _0)] + Recoverable(String), + /// Unrecoverable error should cause the runtime implementation to call the `fail_init` + /// method of the Runtime APIs if it is appropriate and then shutdown gracefully + #[fail(display = "Unrecoverable API error: {}", _0)] + Unrecoverable(String), } -// This is important for other errors to wrap this one. -impl Error for ApiError { - fn description(&self) -> &str { - &self.msg +impl Fail for ApiError { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() } - fn cause(&self) -> Option<&dyn Error> { - // Generic error, underlying cause isn't tracked. - None + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() } } -unsafe impl Send for ApiError {} -unsafe impl Sync for ApiError {} -impl From for ApiError { - fn from(e: serde_json::Error) -> Self { - ApiError::new(e.description()) +impl Display for ApiError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) } } -impl From for ApiError { - fn from(e: InvalidUri) -> Self { - ApiError::new(e.description()) +impl LambdaErrorExt for ApiError { + fn error_type(&self) -> &str { + "RuntimeApiError" } } -impl From for ApiError { - fn from(e: hyper::Error) -> Self { - ApiError::new(e.description()) - } -} - -impl From for ApiError { - fn from(e: ToStrError) -> Self { - ApiError::new(e.description()) - } -} - -impl From for ApiError { - fn from(e: ParseIntError) -> Self { - ApiError::new(e.description()) +impl From for ApiError { + fn from(kind: ApiErrorKind) -> Self { + Self { + inner: Context::new(kind), + } } } -impl From for ApiError { - fn from(e: io::Error) -> Self { - ApiError::new(e.description()) +impl From> for ApiError { + fn from(inner: Context) -> Self { + Self { inner: inner } } } diff --git a/lambda-runtime-errors/Cargo.toml b/lambda-runtime-errors/Cargo.toml new file mode 100644 index 00000000..cbe1edd4 --- /dev/null +++ b/lambda-runtime-errors/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "lambda_runtime_errors" +version = "0.1.0" +authors = ["Stefano Buliani", "David Barsky"] +edition = "2018" +description = "Rust runtime errors for AWS Lambda" +keywords = ["AWS", "Lambda", "Runtime", "Rust"] +license = "Apache-2.0" +homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" +repository = "https://github.com/awslabs/aws-lambda-rust-runtime" +documentation = "https://docs.rs/lambda_runtime" + +[dependencies] +log = "^0.4" +failure = "^0.1" \ No newline at end of file diff --git a/lambda-runtime-errors/errorgen.py b/lambda-runtime-errors/errorgen.py new file mode 100644 index 00000000..a61e1381 --- /dev/null +++ b/lambda-runtime-errors/errorgen.py @@ -0,0 +1,118 @@ +# Generates the LambdaErrorExt implementation for all of the Errors +# in the standard library, excluding unstable APIs and errors that +# require generics. +# +# !! Please note this script is a hacky, short term solution !! +from html.parser import HTMLParser +import urllib2 + +RUST_ERROR_DOCS = "https://doc.rust-lang.org/std/error/trait.Error.html" +GENERATED_FILE_NAME = "./src/error_ext_impl.rs" +UNSTABLE_APIS = ["std::alloc::AllocErr", + "std::alloc::CannotReallocInPlace", + "std::char::CharTryFromError", + "std::num::TryFromIntError"] +GENERIC_ERRORS = ["std::sync::TryLockError", + "std::sync::PoisonError", + "std::sync::mpsc::TrySendError", + "std::sync::mpsc::SendError", + "std::io::IntoInnerError"] + + +class ErrorHtmlParser(HTMLParser): + def __init__(self): + self.reset() + self.errors = [] + self.parsing = False + self.cur_error = self.empty_error() + + def handle_starttag(self, tag, attrs): + if self.parsing and tag == "a": + href = "" + if len(attrs) == 1: + href = attrs[0][1] + else: + href = attrs[1][1] + parts = href.split("/") + cnt = 0 + package = "" + for part in parts: + cnt = cnt + 1 + if part == "..": + continue + if cnt == len(parts): + break + + package += part + "::" + + if package.endswith("::"): + package = package[0:len(package) - 2] + self.cur_error["package"] = package + self.cur_error["href"] = href + + def empty_error(self): + return { + "package": "", + "name": "" + } + + def handle_data(self, data): + if data == " Error for " or data == "impl Error for ": + self.start_parsing() + else: + if self.parsing: + self.cur_error["name"] = data + if self.is_valid_error(self.cur_error): + self.errors.append(self.cur_error) + self.cur_error = self.empty_error + self.parsing = False + + def is_valid_error(self, err): + if err["name"] == "Box": + return False + if ".html" in err["package"]: + return False + if err["package"] == "": + return False + if err["package"] + "::" + err["name"] in UNSTABLE_APIS: + return False + if err["package"] + "::" + err["name"] in GENERIC_ERRORS: + return False + + return True + + def start_parsing(self): + if not self.parsing: + self.parsing = True + self.cur_error = self.empty_error() + else: + if self.cur_error["package"] == "" or self.cur_error["name"] == "": + print("Starting new error with empty existing error") + + +res = urllib2.urlopen(RUST_ERROR_DOCS) +assert res.getcode() == 200, "Could not retrieve Rust error docs" + +error_docs_html = res.read() +assert error_docs_html != "", "Empty Error docs" + +parser = ErrorHtmlParser() +parser.feed(error_docs_html) + +print("found {} valid errors. Beginning code generation to {}".format( + len(parser.errors), GENERATED_FILE_NAME)) + +# code gen +with open(GENERATED_FILE_NAME, "a") as f: + f.write("// Generated code, DO NOT MODIFY!\n\n") + for err in parser.errors: + f.write("use {}::{};\n".format(err["package"], err["name"])) + f.write("use error::LambdaErrorExt;\n\n") + for err in parser.errors: + f.write("""impl LambdaErrorExt for {} {{ + fn error_type(&self) -> &str {{ + "{}" + }} +}}\n""".format(err["name"], err["name"])) + + f.close() diff --git a/lambda-runtime-errors/src/error_ext_impl.rs b/lambda-runtime-errors/src/error_ext_impl.rs new file mode 100644 index 00000000..6a5f09ce --- /dev/null +++ b/lambda-runtime-errors/src/error_ext_impl.rs @@ -0,0 +1,133 @@ +// Generated code, DO NOT MODIFY! + +use crate::LambdaErrorExt; +use std::{ + alloc::LayoutErr, + cell::{BorrowError, BorrowMutError}, + char::{DecodeUtf16Error, ParseCharError}, + env::{JoinPathsError, VarError}, + ffi::{FromBytesWithNulError, IntoStringError, NulError}, + net::AddrParseError, + num::{ParseFloatError, ParseIntError}, + path::StripPrefixError, + str::{ParseBoolError, Utf8Error}, + string::{FromUtf16Error, FromUtf8Error, ParseError}, + sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError}, + time::SystemTimeError, +}; + +impl LambdaErrorExt for VarError { + fn error_type(&self) -> &str { + "VarError" + } +} +impl LambdaErrorExt for ParseError { + fn error_type(&self) -> &str { + "ParseError" + } +} +impl LambdaErrorExt for RecvTimeoutError { + fn error_type(&self) -> &str { + "RecvTimeoutError" + } +} +impl LambdaErrorExt for TryRecvError { + fn error_type(&self) -> &str { + "TryRecvError" + } +} +impl LambdaErrorExt for LayoutErr { + fn error_type(&self) -> &str { + "LayoutErr" + } +} +impl LambdaErrorExt for BorrowError { + fn error_type(&self) -> &str { + "BorrowError" + } +} +impl LambdaErrorExt for BorrowMutError { + fn error_type(&self) -> &str { + "BorrowMutError" + } +} +impl LambdaErrorExt for DecodeUtf16Error { + fn error_type(&self) -> &str { + "DecodeUtf16Error" + } +} +impl LambdaErrorExt for ParseCharError { + fn error_type(&self) -> &str { + "ParseCharError" + } +} +impl LambdaErrorExt for JoinPathsError { + fn error_type(&self) -> &str { + "JoinPathsError" + } +} +impl LambdaErrorExt for FromBytesWithNulError { + fn error_type(&self) -> &str { + "FromBytesWithNulError" + } +} +impl LambdaErrorExt for IntoStringError { + fn error_type(&self) -> &str { + "IntoStringError" + } +} +impl LambdaErrorExt for NulError { + fn error_type(&self) -> &str { + "NulError" + } +} +impl LambdaErrorExt for AddrParseError { + fn error_type(&self) -> &str { + "AddrParseError" + } +} +impl LambdaErrorExt for ParseFloatError { + fn error_type(&self) -> &str { + "ParseFloatError" + } +} +impl LambdaErrorExt for ParseIntError { + fn error_type(&self) -> &str { + "ParseIntError" + } +} +impl LambdaErrorExt for StripPrefixError { + fn error_type(&self) -> &str { + "StripPrefixError" + } +} +impl LambdaErrorExt for ParseBoolError { + fn error_type(&self) -> &str { + "ParseBoolError" + } +} +impl LambdaErrorExt for Utf8Error { + fn error_type(&self) -> &str { + "Utf8Error" + } +} +impl LambdaErrorExt for FromUtf16Error { + fn error_type(&self) -> &str { + "FromUtf16Error" + } +} +impl LambdaErrorExt for FromUtf8Error { + fn error_type(&self) -> &str { + "FromUtf8Error" + } +} +impl LambdaErrorExt for RecvError { + fn error_type(&self) -> &str { + "RecvError" + } +} +impl LambdaErrorExt for SystemTimeError { + fn error_type(&self) -> &str { + "SystemTimeError" + } +} diff --git a/lambda-runtime-errors/src/lib.rs b/lambda-runtime-errors/src/lib.rs new file mode 100644 index 00000000..2907285a --- /dev/null +++ b/lambda-runtime-errors/src/lib.rs @@ -0,0 +1,21 @@ +//! The Lambda runtime errors crate defines the `LambdaErrorExt` trait +//! that can be used by libriaries to return errors compatible with the +//! AWS Lambda Rust runtime. +mod error_ext_impl; + +pub use crate::error_ext_impl::*; + +/// The `LambdaErrorExt` trait defines the `error_type()` method used +/// by the AWS Lambda runtime client to generate `ErrorResponse` +/// objects. The value returned by the `error_type()` method is used to +/// populate the `errorType` field in the Lambda response. This crate +/// includes an implementation of this trait for most errors in the +/// standard library. By default, error return their type name. +pub trait LambdaErrorExt { + /// The value for this field should be an alphanumeric unique identifier + /// of the error type. For example `MyCustomError`. + /// + /// # Return + /// An alphanumeric identifier for the error + fn error_type(&self) -> &str; +} From ecbae1cef993134f2c21a42dd830bc24000e798d Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 3 Jan 2019 17:04:20 -0800 Subject: [PATCH 24/43] Bump dependencies --- Cargo.lock | 105 +++++++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6afd197..25371ffc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,8 +19,8 @@ dependencies = [ "autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -30,7 +30,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -125,16 +125,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "failure" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -254,7 +254,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -277,14 +277,14 @@ name = "lambda_http" version = "0.1.0" dependencies = [ "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime 0.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -294,12 +294,12 @@ dependencies = [ name = "lambda_runtime" version = "0.1.0" dependencies = [ - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime_core 0.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "simple-error 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -309,13 +309,14 @@ dependencies = [ name = "lambda_runtime_client" version = "0.1.0" dependencies = [ - "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", + "lambda_runtime_errors 0.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -325,14 +326,24 @@ version = "0.1.0" dependencies = [ "backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime_client 0.1.0", + "lambda_runtime_errors 0.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lambda_runtime_errors" +version = "0.1.0" +dependencies = [ + "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lazy_static" version = "1.2.0" @@ -345,7 +356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.45" +version = "0.2.46" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -385,7 +396,7 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", @@ -399,7 +410,7 @@ version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -420,7 +431,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -447,7 +458,7 @@ name = "num_cpus" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -472,7 +483,7 @@ name = "parking_lot_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -507,7 +518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -519,7 +530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -587,12 +598,12 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.44" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-demangle" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -628,12 +639,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.82" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.82" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)", @@ -643,12 +654,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -658,7 +669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -725,8 +736,8 @@ name = "time" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -876,7 +887,7 @@ dependencies = [ "bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -996,8 +1007,8 @@ dependencies = [ "checksum crossbeam-epoch 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f10a4f8f409aaac4b16a5474fb233624238fcdeefb9ba50d5ea059aab63ba31c" "checksum crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "41ee4864f4797060e52044376f7d107429ce1fb43460021b126424b7180ee21a" "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd" -"checksum failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6dd377bcc1b1b7ce911967e3ec24fa19c3224394ec05b54aa7b083d498341ac7" -"checksum failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "64c2d913fe8ed3b6c6518eedf4538255b989945c14c2a7d5cbff62a5e2120596" +"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" +"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" @@ -1014,7 +1025,7 @@ dependencies = [ "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" +"checksum libc 0.2.46 (registry+https://github.com/rust-lang/crates.io-index)" = "023a4cd09b2ff695f9734c1934145a315594b7986398496841c7031a5a1bbdbd" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" @@ -1042,16 +1053,16 @@ dependencies = [ "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05" "checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3" -"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70" -"checksum rustc-demangle 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "01b90379b8664dd83460d59bdc5dd1fd3172b8913788db483ed1325171eab2f7" +"checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" +"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "eb9e9b8cde282a9fe6a42dd4681319bfb63f121b8a8ee9439c6f4107e58a46f7" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa52f19aee12441d5ad11c9a00459122bd8f98707cadf9778c540674f1935b6" -"checksum serde_derive 1.0.82 (registry+https://github.com/rust-lang/crates.io-index)" = "96a7f9496ac65a2db5929afa087b54f8fc5008dcfbe48a8874ed20049b0d6154" -"checksum serde_json 1.0.33 (registry+https://github.com/rust-lang/crates.io-index)" = "c37ccd6be3ed1fdf419ee848f7c758eb31b054d7cd3ae3600e3bae0adf569811" +"checksum serde 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "0e732ed5a5592c17d961555e3b552985baf98d50ce418b7b655f31f6ba7eb1b7" +"checksum serde_derive 1.0.84 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d6115a3ca25c224e409185325afc16a0d5aaaabc15c42b09587d6f1ba39a5b" +"checksum serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)" = "bdf540260cfee6da923831f4776ddc495ada940c30117977c70f1313a6130545" "checksum serde_urlencoded 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d48f9f99cd749a2de71d29da5f948de7f2764cc5a9d7f3c97e3514d4ee6eabf2" "checksum simple-error 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "c00e871264295428089fb278c4b225be3ca92c227c918857f2e562bac973257b" "checksum simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25111f1d77db1ac3ee11b62ba4b7a162e6bb3be43e28273f0d3935cc8d3ff7fb" From 6896b1b114a5bb4fcec595636ae07e0c7fc770cc Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 3 Jan 2019 17:04:59 -0800 Subject: [PATCH 25/43] New derive crate (experimental) for LambdaErrorExt and new HandlerError type --- lambda-runtime-errors-derive/Cargo.toml | 25 +++ lambda-runtime-errors-derive/src/lib.rs | 40 +++++ lambda-runtime-errors-derive/tests/tests.rs | 75 +++++++++ lambda-runtime-errors/Cargo.toml | 3 +- lambda-runtime-errors/errorgen.py | 30 +++- lambda-runtime-errors/src/error_ext_impl.rs | 163 +++++++++++++++++--- lambda-runtime-errors/src/lib.rs | 100 ++++++++++++ 7 files changed, 406 insertions(+), 30 deletions(-) create mode 100644 lambda-runtime-errors-derive/Cargo.toml create mode 100644 lambda-runtime-errors-derive/src/lib.rs create mode 100644 lambda-runtime-errors-derive/tests/tests.rs mode change 100644 => 100755 lambda-runtime-errors/errorgen.py diff --git a/lambda-runtime-errors-derive/Cargo.toml b/lambda-runtime-errors-derive/Cargo.toml new file mode 100644 index 00000000..74aafdc2 --- /dev/null +++ b/lambda-runtime-errors-derive/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "lambda_runtime_errors_derive" +version = "0.1.0" +authors = ["Stefano Buliani", "David Barsky"] +edition = "2018" +description = "Rust runtime errors derive for AWS Lambda" +keywords = ["AWS", "Lambda", "Runtime", "Rust"] +license = "Apache-2.0" +homepage = "https://github.com/awslabs/aws-lambda-rust-runtime" +repository = "https://github.com/awslabs/aws-lambda-rust-runtime" +documentation = "https://docs.rs/lambda_runtime" + +[dependencies] +log = "^0.4" +syn = "^0.15" +synstructure = "^0.10" +proc-macro2 = "^0.4" +quote = "^0.6" + +[dev-dependencies] +lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } +failure = "^0.1" + +[lib] +proc-macro = true \ No newline at end of file diff --git a/lambda-runtime-errors-derive/src/lib.rs b/lambda-runtime-errors-derive/src/lib.rs new file mode 100644 index 00000000..0ee2fb9d --- /dev/null +++ b/lambda-runtime-errors-derive/src/lib.rs @@ -0,0 +1,40 @@ +use proc_macro2::TokenStream; +use quote::quote; +use synstructure::decl_derive; + +decl_derive!([LambdaErrorExt, attributes()] => lambda_error_derive); + +fn lambda_error_derive(s: synstructure::Structure) -> TokenStream { + /* + if let Data::Struct(d) = &s.ast().data { + if let Fields::Named(fs) = &d.fields { + for f in fs.named.iter() { + let field_name = f.ident.clone(); + println!("!!Field name: {}", field_name.expect("Field must be named")); + } + } + }*/ + + for v in s.variants() { + println!("Variant: {}", v.ast().ident); + /*if let Some(id) = v.prefix { + println!("Variant: {}", id); + }*/ + } + + let name = format!("{}", s.ast().ident); + + let err_impl = s.gen_impl(quote! { + use lambda_runtime_errors::LambdaErrorExt; + + gen impl LambdaErrorExt for @Self { + fn error_type(&self) -> &str { + #name + } + } + }); + + (quote! { + #err_impl + }) +} diff --git a/lambda-runtime-errors-derive/tests/tests.rs b/lambda-runtime-errors-derive/tests/tests.rs new file mode 100644 index 00000000..51024742 --- /dev/null +++ b/lambda-runtime-errors-derive/tests/tests.rs @@ -0,0 +1,75 @@ +use failure::{Backtrace, Context, Fail}; +use lambda_runtime_errors::LambdaErrorExt; +use std::fmt; + +#[derive(LambdaErrorExt)] +struct BasicCustomError; + +#[derive(Fail, LambdaErrorExt, Debug)] +#[fail(display = "Input was invalid UTF-8")] +struct FailureCustomError; + +#[derive(Debug, LambdaErrorExt)] +struct FailureCustomWithKind { + inner: Context, +} + +#[derive(Clone, Eq, PartialEq, Debug, Fail, LambdaErrorExt)] +enum FailureErrorKind { + #[fail(display = "First contextual error message.")] + FirstVariant, + #[fail(display = "Second contextual error message: {}.", _0)] + SecondVariant(String), +} + +impl Fail for FailureCustomWithKind { + fn cause(&self) -> Option<&Fail> { + self.inner.cause() + } + + fn backtrace(&self) -> Option<&Backtrace> { + self.inner.backtrace() + } +} + +impl fmt::Display for FailureCustomWithKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +impl FailureCustomWithKind { + pub fn kind(&self) -> FailureErrorKind { + self.inner.get_context().clone() + } +} + +impl From for FailureCustomWithKind { + fn from(kind: FailureErrorKind) -> Self { + FailureCustomWithKind { + inner: Context::new(kind), + } + } +} + +#[test] +fn simple_error_type() { + let err = BasicCustomError {}; + assert_eq!( + err.error_type(), + "BasicCustomError", + "Custom error not implemented correctly" + ); +} + +#[test] +fn fail_custom_error() { + let err = FailureCustomError {}; + assert_eq!(err.error_type(), "FailureCustomError", "Error type wrong") +} + +#[test] +fn fail_variant_first() { + let err = FailureCustomWithKind::from(FailureErrorKind::FirstVariant); + //assert_eq!(err.error_type(), "FailureCustomError", "Error type wrong") +} diff --git a/lambda-runtime-errors/Cargo.toml b/lambda-runtime-errors/Cargo.toml index cbe1edd4..f52244b9 100644 --- a/lambda-runtime-errors/Cargo.toml +++ b/lambda-runtime-errors/Cargo.toml @@ -12,4 +12,5 @@ documentation = "https://docs.rs/lambda_runtime" [dependencies] log = "^0.4" -failure = "^0.1" \ No newline at end of file +failure = "^0.1" +serde_json = "^1" diff --git a/lambda-runtime-errors/errorgen.py b/lambda-runtime-errors/errorgen.py old mode 100644 new mode 100755 index a61e1381..3f7efec0 --- a/lambda-runtime-errors/errorgen.py +++ b/lambda-runtime-errors/errorgen.py @@ -1,10 +1,13 @@ +#!/usr/bin/env python3 + # Generates the LambdaErrorExt implementation for all of the Errors # in the standard library, excluding unstable APIs and errors that # require generics. # # !! Please note this script is a hacky, short term solution !! +import os +from urllib.request import urlopen from html.parser import HTMLParser -import urllib2 RUST_ERROR_DOCS = "https://doc.rust-lang.org/std/error/trait.Error.html" GENERATED_FILE_NAME = "./src/error_ext_impl.rs" @@ -21,6 +24,7 @@ class ErrorHtmlParser(HTMLParser): def __init__(self): + super().__init__() self.reset() self.errors = [] self.parsing = False @@ -90,28 +94,44 @@ def start_parsing(self): print("Starting new error with empty existing error") -res = urllib2.urlopen(RUST_ERROR_DOCS) +res = urlopen(RUST_ERROR_DOCS) assert res.getcode() == 200, "Could not retrieve Rust error docs" error_docs_html = res.read() assert error_docs_html != "", "Empty Error docs" parser = ErrorHtmlParser() -parser.feed(error_docs_html) +parser.feed(error_docs_html.decode()) print("found {} valid errors. Beginning code generation to {}".format( len(parser.errors), GENERATED_FILE_NAME)) +if os.path.isfile(GENERATED_FILE_NAME): + os.remove(GENERATED_FILE_NAME) + # code gen with open(GENERATED_FILE_NAME, "a") as f: f.write("// Generated code, DO NOT MODIFY!\n\n") + + # use statements for err in parser.errors: f.write("use {}::{};\n".format(err["package"], err["name"])) - f.write("use error::LambdaErrorExt;\n\n") + f.write( + "use crate::{LambdaErrorExt, HandlerError};\n\n") + + # impl for LambdaErrorExt for the standard library errors for err in parser.errors: f.write("""impl LambdaErrorExt for {} {{ fn error_type(&self) -> &str {{ - "{}" + "{}::{}" + }} +}}\n""".format(err["name"], err["package"], err["name"])) + + # impl From trait for standard library errors to HandlerError + for err in parser.errors: + f.write("""impl From<{}> for HandlerError {{ + fn from(e: {}) -> Self {{ + HandlerError::new(e) }} }}\n""".format(err["name"], err["name"])) diff --git a/lambda-runtime-errors/src/error_ext_impl.rs b/lambda-runtime-errors/src/error_ext_impl.rs index 6a5f09ce..8dae7cc7 100644 --- a/lambda-runtime-errors/src/error_ext_impl.rs +++ b/lambda-runtime-errors/src/error_ext_impl.rs @@ -1,6 +1,6 @@ // Generated code, DO NOT MODIFY! -use crate::LambdaErrorExt; +use crate::{HandlerError, LambdaErrorExt}; use std::{ alloc::LayoutErr, cell::{BorrowError, BorrowMutError}, @@ -18,116 +18,231 @@ use std::{ impl LambdaErrorExt for VarError { fn error_type(&self) -> &str { - "VarError" + "std::env::VarError" } } impl LambdaErrorExt for ParseError { fn error_type(&self) -> &str { - "ParseError" + "std::string::ParseError" } } impl LambdaErrorExt for RecvTimeoutError { fn error_type(&self) -> &str { - "RecvTimeoutError" + "std::sync::mpsc::RecvTimeoutError" } } impl LambdaErrorExt for TryRecvError { fn error_type(&self) -> &str { - "TryRecvError" + "std::sync::mpsc::TryRecvError" } } impl LambdaErrorExt for LayoutErr { fn error_type(&self) -> &str { - "LayoutErr" + "std::alloc::LayoutErr" } } impl LambdaErrorExt for BorrowError { fn error_type(&self) -> &str { - "BorrowError" + "std::cell::BorrowError" } } impl LambdaErrorExt for BorrowMutError { fn error_type(&self) -> &str { - "BorrowMutError" + "std::cell::BorrowMutError" } } impl LambdaErrorExt for DecodeUtf16Error { fn error_type(&self) -> &str { - "DecodeUtf16Error" + "std::char::DecodeUtf16Error" } } impl LambdaErrorExt for ParseCharError { fn error_type(&self) -> &str { - "ParseCharError" + "std::char::ParseCharError" } } impl LambdaErrorExt for JoinPathsError { fn error_type(&self) -> &str { - "JoinPathsError" + "std::env::JoinPathsError" } } impl LambdaErrorExt for FromBytesWithNulError { fn error_type(&self) -> &str { - "FromBytesWithNulError" + "std::ffi::FromBytesWithNulError" } } impl LambdaErrorExt for IntoStringError { fn error_type(&self) -> &str { - "IntoStringError" + "std::ffi::IntoStringError" } } impl LambdaErrorExt for NulError { fn error_type(&self) -> &str { - "NulError" + "std::ffi::NulError" } } impl LambdaErrorExt for AddrParseError { fn error_type(&self) -> &str { - "AddrParseError" + "std::net::AddrParseError" } } impl LambdaErrorExt for ParseFloatError { fn error_type(&self) -> &str { - "ParseFloatError" + "std::num::ParseFloatError" } } impl LambdaErrorExt for ParseIntError { fn error_type(&self) -> &str { - "ParseIntError" + "std::num::ParseIntError" } } impl LambdaErrorExt for StripPrefixError { fn error_type(&self) -> &str { - "StripPrefixError" + "std::path::StripPrefixError" } } impl LambdaErrorExt for ParseBoolError { fn error_type(&self) -> &str { - "ParseBoolError" + "std::str::ParseBoolError" } } impl LambdaErrorExt for Utf8Error { fn error_type(&self) -> &str { - "Utf8Error" + "std::str::Utf8Error" } } impl LambdaErrorExt for FromUtf16Error { fn error_type(&self) -> &str { - "FromUtf16Error" + "std::string::FromUtf16Error" } } impl LambdaErrorExt for FromUtf8Error { fn error_type(&self) -> &str { - "FromUtf8Error" + "std::string::FromUtf8Error" } } impl LambdaErrorExt for RecvError { fn error_type(&self) -> &str { - "RecvError" + "std::sync::mpsc::RecvError" } } impl LambdaErrorExt for SystemTimeError { fn error_type(&self) -> &str { - "SystemTimeError" + "std::time::SystemTimeError" + } +} +impl From for HandlerError { + fn from(e: VarError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: RecvTimeoutError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: TryRecvError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: LayoutErr) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: BorrowError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: BorrowMutError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: DecodeUtf16Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseCharError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: JoinPathsError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: FromBytesWithNulError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: IntoStringError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: NulError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: AddrParseError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseFloatError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseIntError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: StripPrefixError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: ParseBoolError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: Utf8Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: FromUtf16Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: FromUtf8Error) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: RecvError) -> Self { + HandlerError::new(e) + } +} +impl From for HandlerError { + fn from(e: SystemTimeError) -> Self { + HandlerError::new(e) } } diff --git a/lambda-runtime-errors/src/lib.rs b/lambda-runtime-errors/src/lib.rs index 2907285a..1dfa8601 100644 --- a/lambda-runtime-errors/src/lib.rs +++ b/lambda-runtime-errors/src/lib.rs @@ -5,6 +5,9 @@ mod error_ext_impl; pub use crate::error_ext_impl::*; +use failure::{format_err, Compat, Error}; +use std::fmt; + /// The `LambdaErrorExt` trait defines the `error_type()` method used /// by the AWS Lambda runtime client to generate `ErrorResponse` /// objects. The value returned by the `error_type()` method is used to @@ -19,3 +22,100 @@ pub trait LambdaErrorExt { /// An alphanumeric identifier for the error fn error_type(&self) -> &str; } + +impl LambdaErrorExt for Error { + fn error_type(&self) -> &str { + if let Some(name) = self.find_root_cause().name() { + return name; + } + "FailureError" + } +} + +// We implement this trait here so that we can use the Compat type +// in the lambda-runtime crate - heaps of fun between failure and std::error +impl LambdaErrorExt for Compat { + fn error_type(&self) -> &str { + "CompatFailureError" + } +} + +/// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`. +/// The `HandlerError` object can be generated `From` any object that supports `Display`, +/// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using +/// the `?` syntax. For example `let _age_num: u8 = e.age.parse()?;` will return the +/// `::Err` from the handler function. +//pub type HandlerError = failure::Error; +#[derive(Debug)] +pub struct HandlerError { + err_type: String, + inner: failure::Error, +} +impl HandlerError { + pub fn new(e: T) -> Self { + let err_type = e.error_type().to_owned(); + HandlerError { + err_type, + inner: failure::Error::from(e), + } + } +} +impl std::error::Error for HandlerError {} +impl fmt::Display for HandlerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {}", self.err_type, self.inner.find_root_cause()) + } +} +impl LambdaErrorExt for HandlerError { + fn error_type(&self) -> &str { + &self.err_type + } +} +impl From<&str> for HandlerError { + fn from(s: &str) -> Self { + HandlerError { + err_type: "UnknownError".to_owned(), + inner: format_err!("{}", s), + } + } +} +impl From for HandlerError { + fn from(e: failure::Error) -> Self { + let error_type = e.error_type(); + HandlerError { + err_type: error_type.to_owned(), + inner: e, + } + } +} +impl From for HandlerError { + fn from(e: serde_json::error::Error) -> Self { + HandlerError { + err_type: "JsonError".to_owned(), + inner: failure::Error::from(e), + } + } +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use failure::Fail; + + #[derive(Fail, Debug)] + #[fail(display = "Custom Error")] + struct CustomError; + + #[test] + fn std_error_type() { + let parsed_int = "hello".parse::(); + let err = HandlerError::from(parsed_int.err().unwrap()); + assert_eq!(err.error_type(), "std::num::ParseIntError"); + } + + #[test] + fn error_type_from_failure() { + let err = HandlerError::from(failure::Error::from(CustomError {})); + assert_eq!(err.error_type(), "lambda_runtime_errors::tests::CustomError"); + } +} From 3f79fcbdab153e58fa7a35dfdfa549a23b66e29c Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 3 Jan 2019 17:06:10 -0800 Subject: [PATCH 26/43] Switched back to ApiError and added new is_recoverable() method --- lambda-runtime-client/Cargo.toml | 2 +- lambda-runtime-client/src/client.rs | 10 +++++++--- lambda-runtime-client/src/error.rs | 18 ++++++++++++++---- lambda-runtime-client/src/lib.rs | 3 +-- 4 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lambda-runtime-client/Cargo.toml b/lambda-runtime-client/Cargo.toml index 521a8e4c..5bf60f90 100644 --- a/lambda-runtime-client/Cargo.toml +++ b/lambda-runtime-client/Cargo.toml @@ -22,6 +22,6 @@ http = "0.1" serde = "^1" serde_json = "^1" serde_derive = "^1" -failure = "^0.1" log = "0.4" lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } +failure = "^0.1" \ No newline at end of file diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 48f84a25..e864e79d 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,5 +1,5 @@ use crate::error::{ApiError, ApiErrorKind, ErrorResponse, ERROR_TYPE_UNHANDLED}; -use failure::{Error, ResultExt}; +use failure::ResultExt; use hyper::{ client::HttpConnector, header::{self, HeaderMap, HeaderValue}, @@ -154,7 +154,7 @@ impl<'ev> RuntimeClient { impl<'ev> RuntimeClient { /// Polls for new events to the Runtime APIs. - pub fn next_event(&self) -> Result<(Vec, EventContext), Error> { + pub fn next_event(&self) -> Result<(Vec, EventContext), ApiError> { trace!("Polling for next event"); // We wait instead of processing the future asynchronously because AWS Lambda @@ -186,7 +186,11 @@ impl<'ev> RuntimeClient { ))?; } let ctx = self.get_event_context(&resp.headers())?; - let out = resp.into_body().concat2().wait()?; + let out = resp + .into_body() + .concat2() + .wait() + .context(ApiErrorKind::Recoverable("Could not read event boxy".to_string()))?; let buf = out.into_bytes().to_vec(); trace!( diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 9fc9d4e9..53b7d036 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -2,12 +2,13 @@ //! to send their custom errors to the AWS Lambda Runtime Client SDK. The module also //! defines the `ApiError` type returned by the `RuntimeClient` implementations. use failure::{AsFail, Backtrace, Context, Fail}; -use hyper; use lambda_runtime_errors::LambdaErrorExt; use log::*; use serde_derive::*; -use serde_json; -use std::{fmt, fmt::Display, option::Option}; +use std::{ + fmt::{self, Display}, + option::Option, +}; /// Error type description for the `ErrorResponse` event. This type should be returned /// for errors that were handled by the function code or framework. @@ -87,6 +88,15 @@ pub struct ApiError { inner: Context, } +impl ApiError { + /// Returns `true` if the API error is recoverable and should be retried + pub fn is_recoverable(&self) -> bool { + match *self.inner.get_context() { + ApiErrorKind::Recoverable(_) => true, + _ => false, + } + } +} /// Failure context for the `ApiError` type. The kind is used to indicate whether the /// error is recoverable and should be retried or not. #[derive(Clone, Eq, PartialEq, Debug, Fail)] @@ -133,6 +143,6 @@ impl From for ApiError { impl From> for ApiError { fn from(inner: Context) -> Self { - Self { inner: inner } + Self { inner } } } diff --git a/lambda-runtime-client/src/lib.rs b/lambda-runtime-client/src/lib.rs index 1f216fab..2e922f12 100644 --- a/lambda-runtime-client/src/lib.rs +++ b/lambda-runtime-client/src/lib.rs @@ -32,8 +32,7 @@ //! } //! //! fn main() { -//! let runtime_endpoint = String::from("http://localhost:8080"); -//! let client = RuntimeClient::new(runtime_endpoint, None) +//! let client = RuntimeClient::new("http://localhost:8080", None) //! .expect("Could not initialize client"); //! //! let (event_data, event_context) = client.next_event() From 1b1e6243305f912dbff495542d541a498288665f Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 3 Jan 2019 17:06:43 -0800 Subject: [PATCH 27/43] Parametrized error type and updated examples --- lambda-runtime-core/Cargo.toml | 3 +- lambda-runtime-core/examples/simple.rs | 21 +++ lambda-runtime-core/src/error.rs | 15 ++- lambda-runtime-core/src/handler.rs | 36 ++---- lambda-runtime-core/src/lib.rs | 4 +- lambda-runtime-core/src/runtime.rs | 60 +++++---- lambda-runtime/Cargo.toml | 2 +- lambda-runtime/examples/custom_error.rs | 31 +++-- .../examples/custom_error_failure.rs | 16 ++- lambda-runtime/src/lib.rs | 121 ++++++++++++++++-- 10 files changed, 228 insertions(+), 81 deletions(-) create mode 100644 lambda-runtime-core/examples/simple.rs diff --git a/lambda-runtime-core/Cargo.toml b/lambda-runtime-core/Cargo.toml index 99870625..d6e7246e 100644 --- a/lambda-runtime-core/Cargo.toml +++ b/lambda-runtime-core/Cargo.toml @@ -16,8 +16,9 @@ hyper = "^0.12" tokio = "^0.1" backtrace = "^0.3" lambda_runtime_client = { path = "../lambda-runtime-client", version = "^0.1" } +lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } chrono = "^0.4" +failure = "^0.1" [dev-dependencies] -failure = "^0.1" simple_logger = "^1" \ No newline at end of file diff --git a/lambda-runtime-core/examples/simple.rs b/lambda-runtime-core/examples/simple.rs new file mode 100644 index 00000000..1644fb82 --- /dev/null +++ b/lambda-runtime-core/examples/simple.rs @@ -0,0 +1,21 @@ +use failure::format_err; +use lambda_runtime_core::{lambda, Context, HandlerError}; +use simple_logger; +use std::error::Error; + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(data: Vec, _c: Context) -> Result, HandlerError> { + let first_name = String::from_utf8(data)?; + + if first_name == "" { + return Err(format_err!("First name must be valid").into()); + } + + Ok(format!("Hello, {}!", first_name).as_bytes().to_vec()) +} diff --git a/lambda-runtime-core/src/error.rs b/lambda-runtime-core/src/error.rs index 987a3829..89cad043 100644 --- a/lambda-runtime-core/src/error.rs +++ b/lambda-runtime-core/src/error.rs @@ -3,6 +3,7 @@ use std::{env, error::Error, fmt}; use lambda_runtime_client::error::ApiError; +use lambda_runtime_errors::LambdaErrorExt; /// The `RuntimeError` object is returned by the custom runtime as it polls /// for new events and tries to execute the handler function. The error @@ -54,6 +55,16 @@ impl RuntimeError { } } +impl LambdaErrorExt for RuntimeError { + fn error_type(&self) -> &str { + if self.recoverable { + "RecoverableRuntimeError" + } else { + "UnrecoverableRuntimeError" + } + } +} + impl fmt::Display for RuntimeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.msg) @@ -80,8 +91,8 @@ impl From for RuntimeError { impl From for RuntimeError { fn from(e: ApiError) -> Self { - let mut err = RuntimeError::new(e.description()); - err.recoverable = e.recoverable; + let mut err = RuntimeError::new(&format!("{}", e)); + err.recoverable = e.is_recoverable(); err } } diff --git a/lambda-runtime-core/src/handler.rs b/lambda-runtime-core/src/handler.rs index dd2d7cdb..7f99e564 100644 --- a/lambda-runtime-core/src/handler.rs +++ b/lambda-runtime-core/src/handler.rs @@ -1,38 +1,20 @@ use crate::context::Context; -use std::fmt::{self, Debug, Display}; +use failure::Fail; +use lambda_runtime_errors::LambdaErrorExt; +use std::fmt::Display; /// Functions acting as a handler must conform to this type. -pub trait Handler { +pub trait Handler { /// Run the handler. - fn run(&mut self, event: Vec, ctx: Context) -> Result, HandlerError>; + fn run(&mut self, event: Vec, ctx: Context) -> Result, E>; } -impl<'ev, F> Handler for F +impl<'ev, F, E> Handler for F where - F: FnMut(Vec, Context) -> Result, HandlerError>, + F: FnMut(Vec, Context) -> Result, E>, + E: Fail + LambdaErrorExt + Display + Send + Sync, { - fn run(&mut self, event: Vec, ctx: Context) -> Result, HandlerError> { + fn run(&mut self, event: Vec, ctx: Context) -> Result, E> { (*self)(event, ctx) } } - -/// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`. -/// The `HandlerError` object can be generated `From` any object that supports `Display`, -/// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using -/// the `?` syntax. For example `let _age_num: u8 = e.age.parse()?;` will return the -/// `::Err` from the handler function. -pub struct HandlerError { - msg: String, -} - -impl From for HandlerError { - fn from(e: E) -> HandlerError { - HandlerError { msg: format!("{}", e) } - } -} - -impl Display for HandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg) - } -} diff --git a/lambda-runtime-core/src/lib.rs b/lambda-runtime-core/src/lib.rs index fc75a9af..5aca47ee 100644 --- a/lambda-runtime-core/src/lib.rs +++ b/lambda-runtime-core/src/lib.rs @@ -18,6 +18,8 @@ mod runtime; pub use crate::{ context::Context, env::{ConfigProvider, EnvConfigProvider}, - handler::{Handler, HandlerError}, + handler::Handler, runtime::*, }; + +pub use lambda_runtime_errors::{HandlerError, LambdaErrorExt}; diff --git a/lambda-runtime-core/src/runtime.rs b/lambda-runtime-core/src/runtime.rs index 899dea76..00d07273 100644 --- a/lambda-runtime-core/src/runtime.rs +++ b/lambda-runtime-core/src/runtime.rs @@ -2,10 +2,13 @@ use crate::{ context::Context, env::{ConfigProvider, EnvConfigProvider, FunctionSettings}, error::RuntimeError, - handler::{Handler, HandlerError}, + handler::Handler, }; +use failure::Fail; use lambda_runtime_client::{error::ErrorResponse, RuntimeClient}; +use lambda_runtime_errors::LambdaErrorExt; use log::*; +use std::{fmt::Display, marker::PhantomData}; use tokio::runtime::Runtime as TokioRuntime; const MAX_RETRIES: i8 = 3; @@ -18,7 +21,10 @@ const MAX_RETRIES: i8 = 3; /// /// # Panics /// The function panics if the Lambda environment variables are not set. -pub fn start(f: impl Handler, runtime: Option) { +pub fn start(f: impl Handler, runtime: Option) +where + E: Fail + LambdaErrorExt + Display + Send + Sync, +{ start_with_config(f, &EnvConfigProvider::default(), runtime) } @@ -51,9 +57,10 @@ macro_rules! lambda { /// The function panics if the `ConfigProvider` returns an error from the `get_runtime_api_endpoint()` /// or `get_function_settings()` methods. The panic forces AWS Lambda to terminate the environment /// and spin up a new one for the next invocation. -pub fn start_with_config(f: impl Handler, config: &C, runtime: Option) +pub fn start_with_config(f: impl Handler, config: &C, runtime: Option) where C: ConfigProvider, + E: Fail + LambdaErrorExt + Display + Send + Sync, { // if we cannot find the endpoint we panic, nothing else we can do. let endpoint: String; @@ -75,7 +82,7 @@ where } } - match RuntimeClient::new(endpoint, runtime) { + match RuntimeClient::new(&endpoint, runtime) { Ok(client) => { start_with_runtime_client(f, function_config, client); } @@ -96,8 +103,11 @@ where /// /// # Panics /// The function panics if we cannot instantiate a new `RustRuntime` object. -pub(crate) fn start_with_runtime_client(f: impl Handler, func_settings: FunctionSettings, client: RuntimeClient) { - let mut lambda_runtime: Runtime<_> = Runtime::new(f, func_settings, MAX_RETRIES, client); +pub(crate) fn start_with_runtime_client(f: impl Handler, func_settings: FunctionSettings, client: RuntimeClient) +where + E: Fail + LambdaErrorExt + Display + Send + Sync, +{ + let mut lambda_runtime: Runtime<_, E> = Runtime::new(f, func_settings, MAX_RETRIES, client); // start the infinite loop lambda_runtime.start(); @@ -105,15 +115,20 @@ pub(crate) fn start_with_runtime_client(f: impl Handler, func_settings: Function /// Internal representation of the runtime object that polls for events and communicates /// with the Runtime APIs -pub(super) struct Runtime { +pub(super) struct Runtime { runtime_client: RuntimeClient, handler: F, max_retries: i8, settings: FunctionSettings, + _phantom: PhantomData, } // generic methods implementation -impl Runtime { +impl Runtime +where + F: Handler, + E: Fail + LambdaErrorExt + Display + Send + Sync, +{ /// Creates a new instance of the `Runtime` object populated with the environment /// settings. /// @@ -133,20 +148,23 @@ impl Runtime { retries, client.get_endpoint() ); + Runtime { runtime_client: client, settings: config, handler: f, max_retries: retries, + _phantom: PhantomData, } } } // implementation of methods that require the Event and Output types // to be compatible with `serde`'s Deserialize/Serialize. -impl Runtime +impl Runtime where - F: Handler, + F: Handler, + E: Fail + LambdaErrorExt + Display + Send + Sync, { /// Starts the main event loop and begin polling or new events. If one of the /// Runtime APIs returns an unrecoverable error this method calls the init failed @@ -170,12 +188,12 @@ where // we let the Lambda Runtime API know that we have died Err(e) => { error!("Could not send response for {} to Runtime API: {}", request_id, e); - if !e.recoverable { + if !e.is_recoverable() { error!( "Error for {} is not recoverable, sending fail_init signal and panicking.", request_id ); - self.runtime_client.fail_init(&ErrorResponse::from(Box::new(e))); + self.runtime_client.fail_init(&ErrorResponse::from(e)); panic!("Could not send response"); } } @@ -184,19 +202,16 @@ where Err(e) => { debug!("Handler returned an error for {}: {}", request_id, e); debug!("Attempting to send error response to Runtime API for {}", request_id); - match self - .runtime_client - .event_error(&request_id, &ErrorResponse::from(Box::new(e))) - { + match self.runtime_client.event_error(&request_id, &ErrorResponse::from(e)) { Ok(_) => info!("Error response for {} accepted by Runtime API", request_id), Err(e) => { error!("Unable to send error response for {} to Runtime API: {}", request_id, e); - if !e.recoverable { + if !e.is_recoverable() { error!( "Error for {} is not recoverable, sending fail_init signal and panicking", request_id ); - self.runtime_client.fail_init(&ErrorResponse::from(Box::new(e))); + self.runtime_client.fail_init(&ErrorResponse::from(e)); panic!("Could not send error response"); } } @@ -208,7 +223,7 @@ where /// Invoke the handler function. This method is split out of the main loop to /// make it testable. - pub(super) fn invoke(&mut self, e: Vec, ctx: Context) -> Result, HandlerError> { + pub(super) fn invoke(&mut self, e: Vec, ctx: Context) -> Result, E> { (self.handler).run(e, ctx) } @@ -224,11 +239,11 @@ where match err.request_id.clone() { Some(req_id) => { self.runtime_client - .event_error(&req_id, &ErrorResponse::from(Box::new(err))) + .event_error(&req_id, &ErrorResponse::from(err)) .expect("Could not send event error response"); } None => { - self.runtime_client.fail_init(&ErrorResponse::from(Box::new(err))); + self.runtime_client.fail_init(&ErrorResponse::from(err)); } } @@ -260,12 +275,13 @@ pub(crate) mod tests { use super::*; use crate::{context, env}; use lambda_runtime_client::RuntimeClient; + use lambda_runtime_errors::HandlerError; #[test] fn runtime_invokes_handler() { let config: &dyn env::ConfigProvider = &env::tests::MockConfigProvider { error: false }; let client = RuntimeClient::new( - config + &config .get_runtime_api_endpoint() .expect("Could not get runtime endpoint"), None, diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 3f283572..18a3cc67 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -22,8 +22,8 @@ serde_derive = "^1" tokio = "0.1" log = "^0.4" lambda_runtime_core = { path = "../lambda-runtime-core", version = "^0.1" } +failure = "^0.1" [dev-dependencies] simple_logger = "^1" simple-error = "^0.1" -failure = "^0.1" diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs index 634c4222..066d2da5 100644 --- a/lambda-runtime/examples/custom_error.rs +++ b/lambda-runtime/examples/custom_error.rs @@ -1,4 +1,4 @@ -use lambda_runtime::{error::HandlerError, lambda, Context}; +use lambda_runtime::{error::LambdaErrorExt, lambda, Context}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::{error::Error, fmt}; @@ -7,20 +7,29 @@ use std::{error::Error, fmt}; struct CustomError { msg: String, } - +impl CustomError { + fn new(message: &str) -> CustomError { + CustomError { + msg: message.to_owned(), + } + } +} impl fmt::Display for CustomError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.msg) } } - impl Error for CustomError {} - -impl CustomError { - fn new(message: &str) -> CustomError { - CustomError { - msg: message.to_owned(), - } +impl From for CustomError { + fn from(i: std::num::ParseIntError) -> Self { + CustomError::new(&format!("{}", i)) + } +} +// the value return by the error_type function is included as the +// `errorType` in the AWS Lambda response +impl LambdaErrorExt for CustomError { + fn error_type(&self) -> &str { + "CustomError" } } @@ -43,12 +52,12 @@ fn main() -> Result<(), Box> { Ok(()) } -fn my_handler(e: CustomEvent, c: Context) -> Result { +fn my_handler(e: CustomEvent, c: Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); // in this case, we explicitly initialize and box our custom error type. // the HandlerError type is an alias to Box/ - return Err(CustomError::new("Empty first name").into()); + return Err(CustomError::new("Empty first name")); } // For errors simply want to return, because the HandlerError is an alias to any diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index b828eadf..12c72c83 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -1,5 +1,5 @@ use failure::Fail; -use lambda_runtime::{error::HandlerError, lambda, Context}; +use lambda_runtime::{error::LambdaErrorExt, lambda, Context}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error as StdError; @@ -7,6 +7,16 @@ use std::error::Error as StdError; #[derive(Fail, Debug)] #[fail(display = "Custom Error")] struct CustomError; +impl LambdaErrorExt for CustomError { + fn error_type(&self) -> &str { + "CustomError" + } +} +impl From for CustomError { + fn from(_i: std::num::ParseIntError) -> Self { + CustomError {} + } +} #[derive(Deserialize)] struct CustomEvent { @@ -27,10 +37,10 @@ fn main() -> Result<(), Box> { Ok(()) } -fn my_handler(e: CustomEvent, c: Context) -> Result { +fn my_handler(e: CustomEvent, c: Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err((CustomError {}).into()); + return Err(CustomError {}); } let _age_num: u8 = e.age.parse()?; diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 189d8d84..086a2747 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -35,31 +35,34 @@ //! }) //! } //! ``` -use lambda_runtime_core::{start_with_config, EnvConfigProvider}; +use failure::Fail; +use lambda_runtime_core::{start_with_config, EnvConfigProvider, HandlerError, LambdaErrorExt}; use serde; use serde_json; +use std::fmt::Display; use tokio::runtime::Runtime as TokioRuntime; pub use lambda_runtime_core::Context; /// The error module exposes the HandlerError type. pub mod error { - pub use lambda_runtime_core::HandlerError; + pub use lambda_runtime_core::{HandlerError, LambdaErrorExt}; } /// Functions acting as a handler must conform to this type. -pub trait Handler { +pub trait Handler { /// Method to execute the handler function - fn run(&mut self, event: E, ctx: Context) -> Result; + fn run(&mut self, event: EV, ctx: Context) -> Result; } /// Implementation of the `Handler` trait for both function pointers /// and closures. -impl Handler for F +impl Handler for F where - F: FnMut(E, Context) -> Result, + F: FnMut(EV, Context) -> Result, + ER: Fail + LambdaErrorExt + Display + Send + Sync, { - fn run(&mut self, event: E, ctx: Context) -> Result { + fn run(&mut self, event: EV, ctx: Context) -> Result { (*self)(event, ctx) } } @@ -68,19 +71,20 @@ where /// defined in the `lambda_runtime_core` crate. The closure simply uses `serde_json` /// to serialize and deserialize the incoming event from a `Vec` and the output /// to a `Vec`. -fn wrap(mut h: impl Handler) -> impl FnMut(Vec, Context) -> Result, error::HandlerError> +fn wrap(mut h: impl Handler) -> impl FnMut(Vec, Context) -> Result, HandlerError> where - E: serde::de::DeserializeOwned, + EV: serde::de::DeserializeOwned, O: serde::Serialize, + ER: Fail + LambdaErrorExt + Display + Send + Sync, { move |ev, ctx| { - let event: E = serde_json::from_slice(&ev)?; + let event: EV = serde_json::from_slice(&ev)?; match h.run(event, ctx) { Ok(out) => { let out_bytes = serde_json::to_vec(&out)?; Ok(out_bytes) } - Err(err) => Err(err), + Err(e) => Err(HandlerError::new(e)), } } } @@ -93,10 +97,11 @@ where /// /// # Panics /// The function panics if the Lambda environment variables are not set. -pub fn start(f: impl Handler, runtime: Option) +pub fn start(f: impl Handler, runtime: Option) where - E: serde::de::DeserializeOwned, + EV: serde::de::DeserializeOwned, O: serde::Serialize, + ER: Fail + LambdaErrorExt + Display + Send + Sync, { let wrapped = wrap(f); start_with_config(wrapped, &EnvConfigProvider::default(), runtime) @@ -117,3 +122,93 @@ macro_rules! lambda { $crate::start($handler, Some($runtime)) }; } + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use lambda_runtime_core::Context; + use serde_derive::{Deserialize, Serialize}; + use serde_json; + + fn test_context() -> Context { + Context { + memory_limit_in_mb: 128, + function_name: "test_func".to_string(), + function_version: "$LATEST".to_string(), + invoked_function_arn: "arn:aws:lambda".to_string(), + aws_request_id: "123".to_string(), + xray_trace_id: "123".to_string(), + log_stream_name: "logStream".to_string(), + log_group_name: "logGroup".to_string(), + client_context: Option::default(), + identity: Option::default(), + deadline: 0, + } + } + + #[derive(Serialize, Deserialize)] + struct Input { + name: String, + } + + #[derive(Serialize, Deserialize)] + struct Output { + msg: String, + } + + #[test] + fn runtime_invokes_handler() { + let handler_ok = |_e: Input, _c: Context| -> Result { + Ok(Output { + msg: "hello".to_owned(), + }) + }; + let mut wrapped_ok = wrap(handler_ok); + let input = Input { + name: "test".to_owned(), + }; + let output = wrapped_ok.run( + serde_json::to_vec(&input).expect("Could not convert input to Vec"), + test_context(), + ); + assert_eq!( + output.is_err(), + false, + "Handler threw an unexpected error: {}", + output.err().unwrap() + ); + let output_obj: Output = serde_json::from_slice(&output.ok().unwrap()).expect("Could not serialize output"); + assert_eq!( + output_obj.msg, + "hello".to_owned(), + "Unexpected output message: {}", + output_obj.msg + ); + } + + #[test] + fn runtime_captures_error() { + let handler_ok = |e: Input, _c: Context| -> Result { + let _age = e.name.parse::()?; + Ok(Output { + msg: "hello".to_owned(), + }) + }; + let mut wrapped_ok = wrap(handler_ok); + let input = Input { + name: "test".to_owned(), + }; + let output = wrapped_ok.run( + serde_json::to_vec(&input).expect("Could not convert input to Vec"), + test_context(), + ); + assert_eq!(output.is_err(), true, "Handler did not throw an error"); + let err = output.err().unwrap(); + assert_eq!( + err.error_type(), + "std::num::ParseIntError", + "Unexpected error_type: {}", + err.error_type() + ); + } +} From 7de9169fce81c25db1d2340d0b954250bca26597 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 4 Jan 2019 09:43:10 -0800 Subject: [PATCH 28/43] Switched to more descriptive generic names --- lambda-runtime-core/src/handler.rs | 12 ++++---- lambda-runtime-core/src/runtime.rs | 48 +++++++++++++++++------------- lambda-runtime/src/lib.rs | 32 ++++++++++---------- 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/lambda-runtime-core/src/handler.rs b/lambda-runtime-core/src/handler.rs index 7f99e564..41d2eff5 100644 --- a/lambda-runtime-core/src/handler.rs +++ b/lambda-runtime-core/src/handler.rs @@ -4,17 +4,17 @@ use lambda_runtime_errors::LambdaErrorExt; use std::fmt::Display; /// Functions acting as a handler must conform to this type. -pub trait Handler { +pub trait Handler { /// Run the handler. - fn run(&mut self, event: Vec, ctx: Context) -> Result, E>; + fn run(&mut self, event: Vec, ctx: Context) -> Result, EventError>; } -impl<'ev, F, E> Handler for F +impl<'ev, Function, EventError> Handler for Function where - F: FnMut(Vec, Context) -> Result, E>, - E: Fail + LambdaErrorExt + Display + Send + Sync, + Function: FnMut(Vec, Context) -> Result, EventError>, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { - fn run(&mut self, event: Vec, ctx: Context) -> Result, E> { + fn run(&mut self, event: Vec, ctx: Context) -> Result, EventError> { (*self)(event, ctx) } } diff --git a/lambda-runtime-core/src/runtime.rs b/lambda-runtime-core/src/runtime.rs index 00d07273..7e77684d 100644 --- a/lambda-runtime-core/src/runtime.rs +++ b/lambda-runtime-core/src/runtime.rs @@ -21,9 +21,9 @@ const MAX_RETRIES: i8 = 3; /// /// # Panics /// The function panics if the Lambda environment variables are not set. -pub fn start(f: impl Handler, runtime: Option) +pub fn start(f: impl Handler, runtime: Option) where - E: Fail + LambdaErrorExt + Display + Send + Sync, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { start_with_config(f, &EnvConfigProvider::default(), runtime) } @@ -57,10 +57,13 @@ macro_rules! lambda { /// The function panics if the `ConfigProvider` returns an error from the `get_runtime_api_endpoint()` /// or `get_function_settings()` methods. The panic forces AWS Lambda to terminate the environment /// and spin up a new one for the next invocation. -pub fn start_with_config(f: impl Handler, config: &C, runtime: Option) -where - C: ConfigProvider, - E: Fail + LambdaErrorExt + Display + Send + Sync, +pub fn start_with_config( + f: impl Handler, + config: &Config, + runtime: Option, +) where + Config: ConfigProvider, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { // if we cannot find the endpoint we panic, nothing else we can do. let endpoint: String; @@ -103,11 +106,14 @@ where /// /// # Panics /// The function panics if we cannot instantiate a new `RustRuntime` object. -pub(crate) fn start_with_runtime_client(f: impl Handler, func_settings: FunctionSettings, client: RuntimeClient) -where - E: Fail + LambdaErrorExt + Display + Send + Sync, +pub(crate) fn start_with_runtime_client( + f: impl Handler, + func_settings: FunctionSettings, + client: RuntimeClient, +) where + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { - let mut lambda_runtime: Runtime<_, E> = Runtime::new(f, func_settings, MAX_RETRIES, client); + let mut lambda_runtime: Runtime<_, EventError> = Runtime::new(f, func_settings, MAX_RETRIES, client); // start the infinite loop lambda_runtime.start(); @@ -115,19 +121,19 @@ where /// Internal representation of the runtime object that polls for events and communicates /// with the Runtime APIs -pub(super) struct Runtime { +pub(super) struct Runtime { runtime_client: RuntimeClient, - handler: F, + handler: Function, max_retries: i8, settings: FunctionSettings, - _phantom: PhantomData, + _phantom: PhantomData, } // generic methods implementation -impl Runtime +impl Runtime where - F: Handler, - E: Fail + LambdaErrorExt + Display + Send + Sync, + Function: Handler, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { /// Creates a new instance of the `Runtime` object populated with the environment /// settings. @@ -142,7 +148,7 @@ where /// A `Result` for the `Runtime` object or a `errors::RuntimeSerror`. The runtime /// fails the init if this function returns an error. If we cannot find the /// `AWS_LAMBDA_RUNTIME_API` variable in the environment the function panics. - pub(super) fn new(f: F, config: FunctionSettings, retries: i8, client: RuntimeClient) -> Self { + pub(super) fn new(f: Function, config: FunctionSettings, retries: i8, client: RuntimeClient) -> Self { debug!( "Creating new runtime with {} max retries for endpoint {}", retries, @@ -161,10 +167,10 @@ where // implementation of methods that require the Event and Output types // to be compatible with `serde`'s Deserialize/Serialize. -impl Runtime +impl Runtime where - F: Handler, - E: Fail + LambdaErrorExt + Display + Send + Sync, + Function: Handler, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { /// Starts the main event loop and begin polling or new events. If one of the /// Runtime APIs returns an unrecoverable error this method calls the init failed @@ -223,7 +229,7 @@ where /// Invoke the handler function. This method is split out of the main loop to /// make it testable. - pub(super) fn invoke(&mut self, e: Vec, ctx: Context) -> Result, E> { + pub(super) fn invoke(&mut self, e: Vec, ctx: Context) -> Result, EventError> { (self.handler).run(e, ctx) } diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 086a2747..9b018abe 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -50,19 +50,19 @@ pub mod error { } /// Functions acting as a handler must conform to this type. -pub trait Handler { +pub trait Handler { /// Method to execute the handler function - fn run(&mut self, event: EV, ctx: Context) -> Result; + fn run(&mut self, event: Event, ctx: Context) -> Result; } /// Implementation of the `Handler` trait for both function pointers /// and closures. -impl Handler for F +impl Handler for Function where - F: FnMut(EV, Context) -> Result, - ER: Fail + LambdaErrorExt + Display + Send + Sync, + Function: FnMut(Event, Context) -> Result, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { - fn run(&mut self, event: EV, ctx: Context) -> Result { + fn run(&mut self, event: Event, ctx: Context) -> Result { (*self)(event, ctx) } } @@ -71,14 +71,16 @@ where /// defined in the `lambda_runtime_core` crate. The closure simply uses `serde_json` /// to serialize and deserialize the incoming event from a `Vec` and the output /// to a `Vec`. -fn wrap(mut h: impl Handler) -> impl FnMut(Vec, Context) -> Result, HandlerError> +fn wrap( + mut h: impl Handler, +) -> impl FnMut(Vec, Context) -> Result, HandlerError> where - EV: serde::de::DeserializeOwned, - O: serde::Serialize, - ER: Fail + LambdaErrorExt + Display + Send + Sync, + Event: serde::de::DeserializeOwned, + Output: serde::Serialize, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { move |ev, ctx| { - let event: EV = serde_json::from_slice(&ev)?; + let event: Event = serde_json::from_slice(&ev)?; match h.run(event, ctx) { Ok(out) => { let out_bytes = serde_json::to_vec(&out)?; @@ -97,11 +99,11 @@ where /// /// # Panics /// The function panics if the Lambda environment variables are not set. -pub fn start(f: impl Handler, runtime: Option) +pub fn start(f: impl Handler, runtime: Option) where - EV: serde::de::DeserializeOwned, - O: serde::Serialize, - ER: Fail + LambdaErrorExt + Display + Send + Sync, + Event: serde::de::DeserializeOwned, + Output: serde::Serialize, + EventError: Fail + LambdaErrorExt + Display + Send + Sync, { let wrapped = wrap(f); start_with_config(wrapped, &EnvConfigProvider::default(), runtime) From b991438536aab793eadd0c73ca594cf02733e834 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 4 Jan 2019 10:21:22 -0800 Subject: [PATCH 29/43] Added a new LambdaResultExt to simplify error handling in handlers --- Cargo.lock | 1 + lambda-runtime-core/src/lib.rs | 2 +- lambda-runtime-errors/Cargo.toml | 3 + lambda-runtime-errors/src/lib.rs | 71 +++++++++++++++++++++++- lambda-runtime/examples/failure_error.rs | 38 +++++++++++++ lambda-runtime/src/lib.rs | 2 +- 6 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 lambda-runtime/examples/failure_error.rs diff --git a/Cargo.lock b/Cargo.lock index 25371ffc..c15dbc58 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -340,6 +340,7 @@ name = "lambda_runtime_errors" version = "0.1.0" dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lambda_runtime_core 0.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.34 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/lambda-runtime-core/src/lib.rs b/lambda-runtime-core/src/lib.rs index 5aca47ee..7a52c1d1 100644 --- a/lambda-runtime-core/src/lib.rs +++ b/lambda-runtime-core/src/lib.rs @@ -22,4 +22,4 @@ pub use crate::{ runtime::*, }; -pub use lambda_runtime_errors::{HandlerError, LambdaErrorExt}; +pub use lambda_runtime_errors::{HandlerError, LambdaErrorExt, LambdaResultExt}; diff --git a/lambda-runtime-errors/Cargo.toml b/lambda-runtime-errors/Cargo.toml index f52244b9..c23a2a92 100644 --- a/lambda-runtime-errors/Cargo.toml +++ b/lambda-runtime-errors/Cargo.toml @@ -14,3 +14,6 @@ documentation = "https://docs.rs/lambda_runtime" log = "^0.4" failure = "^0.1" serde_json = "^1" + +[dev-dependencies] +lambda_runtime_core = { path = "../lambda-runtime-core", version = "^0.1"} diff --git a/lambda-runtime-errors/src/lib.rs b/lambda-runtime-errors/src/lib.rs index 1dfa8601..3a26bd2d 100644 --- a/lambda-runtime-errors/src/lib.rs +++ b/lambda-runtime-errors/src/lib.rs @@ -5,7 +5,7 @@ mod error_ext_impl; pub use crate::error_ext_impl::*; -use failure::{format_err, Compat, Error}; +use failure::{format_err, Compat, Error, Fail}; use std::fmt; /// The `LambdaErrorExt` trait defines the `error_type()` method used @@ -40,6 +40,75 @@ impl LambdaErrorExt for Compat { } } +/// `Result` type extension for AWS that makes it easy to generate a `HandlerError` +/// object or a `Compat` from the failure crate using an existing result. +/// This trait should be imported from the `lambda_runtime_core` or `lambda_runtime` +/// crates. +pub trait LambdaResultExt { + /// Takes the incoming `Result` and maps it to a Result that returns an `HandlerError` object. + /// The `HandlerError` type already includes implementations of the `From` trait for most + /// standard library errors. This method is intended to be used when a the `From` trait is not + /// implemented. + /// + /// # Example + /// + /// ```rust,no_run + /// use lambda_runtime_core::{Context, LambdaResultExt, HandlerError, lambda}; + /// use std::error::Error as StdError; + /// + /// fn main() -> Result<(), Box> { + /// lambda!(my_handler); + /// Ok(()) + /// } + /// + /// fn my_handler(_event: Vec, _ctx: Context) -> Result, HandlerError> { + /// let age = "hello"; // this will throw an error when we try to parse it into an int + /// age.parse::().handler_error()?; + /// + /// Ok(vec!()) + /// } + /// ``` + fn handler_error(self) -> Result; + + /// Takes the incoming result and converts it into an `Error` type from the `failure` crate + /// wrapped in a `Compat` object to make it implement the `Error` trait from the standard + /// library. This method makes it easy to write handler functions that return `Compat` + /// directly. + /// + /// # Example + /// + /// ```rust,no_run + /// use lambda_runtime_core::{Context, LambdaResultExt, lambda}; + /// use failure::{Error, Compat}; + /// use std::error::Error as StdError; + /// + /// fn main() -> Result<(), Box> { + /// lambda!(my_handler); + /// Ok(()) + /// } + /// + /// fn my_handler(_event: Vec, _ctx: Context) -> Result, Compat> { + /// let age = "hello"; // this will throw an error when we try to parse it into an int + /// age.parse::().failure_compat()?; + /// Ok(vec!()) + /// } + /// ``` + fn failure_compat(self) -> Result>; +} + +impl LambdaResultExt for Result +where + ERR: Fail + LambdaErrorExt, +{ + fn handler_error(self) -> Result { + self.map_err(HandlerError::new) + } + + fn failure_compat(self) -> Result> { + self.map_err(|err| Error::from(err).compat()) + } +} + /// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`. /// The `HandlerError` object can be generated `From` any object that supports `Display`, /// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using diff --git a/lambda-runtime/examples/failure_error.rs b/lambda-runtime/examples/failure_error.rs new file mode 100644 index 00000000..d159eb96 --- /dev/null +++ b/lambda-runtime/examples/failure_error.rs @@ -0,0 +1,38 @@ +use failure::{format_err, Compat, Error}; +use lambda_runtime::{error::LambdaResultExt, lambda, Context}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use std::error::Error as StdError; + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: Context) -> Result> { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + let err = format_err!("Invalid First Name"); + return Err(err.compat()); + } + + let _age_num: u8 = e.age.parse().failure_compat()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 9b018abe..c3fa5f73 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -46,7 +46,7 @@ pub use lambda_runtime_core::Context; /// The error module exposes the HandlerError type. pub mod error { - pub use lambda_runtime_core::{HandlerError, LambdaErrorExt}; + pub use lambda_runtime_core::{HandlerError, LambdaErrorExt, LambdaResultExt}; } /// Functions acting as a handler must conform to this type. From bedbf927e43c5ee5ffd771bf97dc99cc952f0575 Mon Sep 17 00:00:00 2001 From: sapessi Date: Fri, 4 Jan 2019 14:15:45 -0800 Subject: [PATCH 30/43] Added build script to generate the metadata file --- Cargo.lock | 1 + lambda-runtime-core/Cargo.toml | 5 ++++- lambda-runtime-core/build.rs | 41 ++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 lambda-runtime-core/build.rs diff --git a/Cargo.lock b/Cargo.lock index c15dbc58..7635bf55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -331,6 +331,7 @@ dependencies = [ "lambda_runtime_client 0.1.0", "lambda_runtime_errors 0.1.0", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/lambda-runtime-core/Cargo.toml b/lambda-runtime-core/Cargo.toml index d6e7246e..0de51414 100644 --- a/lambda-runtime-core/Cargo.toml +++ b/lambda-runtime-core/Cargo.toml @@ -21,4 +21,7 @@ chrono = "^0.4" failure = "^0.1" [dev-dependencies] -simple_logger = "^1" \ No newline at end of file +simple_logger = "^1" + +[build-dependencies] +rustc_version = "^0.2" \ No newline at end of file diff --git a/lambda-runtime-core/build.rs b/lambda-runtime-core/build.rs new file mode 100644 index 00000000..21157584 --- /dev/null +++ b/lambda-runtime-core/build.rs @@ -0,0 +1,41 @@ +use rustc_version::{Channel, VersionMeta}; +use std::env; +use std::fs; +use std::path::Path; +use std::process::Command; + +const RUNTIME_METADATA_FILE: &str = "runtime_release"; + +fn main() { + println!("Generating AWS Lambda metadata file"); + let out_dir = env::var("OUT_DIR").unwrap(); + let compiler = env::var("RUSTC").unwrap(); + let cargo_version = env::var("CARGO_PKG_VERSION").unwrap(); + let compiler_version = + VersionMeta::for_command(Command::new(compiler.clone())).expect("Could not load compiler metdata"); + let chn: &str; + match compiler_version.channel { + Channel::Dev => chn = "dev", + Channel::Nightly => chn = "nightly", + Channel::Beta => chn = "beta", + Channel::Stable => chn = "stable", + } + let compiler_str = format!("{}/{}-{}", compiler, compiler_version.semver, chn); + + let agent = format!("AWS_Lambda_Rust/{} ({})", cargo_version, compiler_str); + // we expect this library to be built as a dependency and the output directory + // to be something like: my-lambda-function/target/release/build/lambda_runtime_core-c1abe336a4420096/out. + // we want the metadata file to be generated alongside the executable of the function + // so we travel 3 directories up to my-lambda-function/target/release. + let metadata_path = Path::new(&out_dir) + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join(RUNTIME_METADATA_FILE); + println!("Writing runtime metadata to: {}", metadata_path.to_str().unwrap()); + println!("Runtime metadata: {}", agent); + fs::write(metadata_path, agent).expect("Could not write runtime metdata file"); +} From e3f6b345e619b564ff709972b82fe83f7d715220 Mon Sep 17 00:00:00 2001 From: sapessi Date: Sat, 5 Jan 2019 11:21:41 -0800 Subject: [PATCH 31/43] Changed the runtime client SDK to receive a user-agent as init param. runtime core now imports a generated function to collect the runtime info --- lambda-runtime-client/src/client.rs | 21 ++++++++++++++++----- lambda-runtime-client/src/lib.rs | 2 +- lambda-runtime-core/build.rs | 14 +++++++++++++- lambda-runtime-core/src/runtime.rs | 8 +++++++- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index e864e79d..09f4ed2f 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -17,7 +17,7 @@ const API_CONTENT_TYPE: &str = "application/json"; const API_ERROR_CONTENT_TYPE: &str = "application/vnd.aws.lambda.error+json"; const RUNTIME_ERROR_HEADER: &str = "Lambda-Runtime-Function-Error-Type"; // TODO: Perhaps use a macro to generate this -const CLIENT_USER_AGENT: &str = "AWS_Lambda_Rust/0.1.0"; +const DEFAULT_AGENT: &str = "AWS_Lambda_Rust"; /// Enum of the headers returned by Lambda's `/next` API call. pub enum LambdaHeaders { @@ -126,13 +126,23 @@ pub struct RuntimeClient { _runtime: Runtime, http_client: Client, endpoint: Uri, + runtime_agent: String, } impl<'ev> RuntimeClient { /// Creates a new instance of the Runtime APIclient SDK. The http client has timeouts disabled and - /// will always send a `Connection: keep-alive` header. - pub fn new(host: &str, runtime: Option) -> Result { + /// will always send a `Connection: keep-alive` header. Optionally, the runtime client can receive + /// a user agent string. This string is used to make requests to the runtime APIs and is used to + /// identify the runtime being used by the function. For example, the `lambda_runtime_core` crate + /// uses `AWS_Lambda_Rust/0.1.0 (rustc/1.31.1-stable)`. The runtime client can also receive an + /// instance of Tokio Runtime to use. + pub fn new(host: &str, agent: Option, runtime: Option) -> Result { debug!("Starting new HttpRuntimeClient for {}", host); + let runtime_agent = match agent { + Some(a) => a, + None => DEFAULT_AGENT.to_owned(), + }; + // start a tokio core main event loop for hyper let runtime = match runtime { Some(r) => r, @@ -148,6 +158,7 @@ impl<'ev> RuntimeClient { _runtime: runtime, http_client, endpoint, + runtime_agent, }) } } @@ -327,7 +338,7 @@ impl<'ev> RuntimeClient { .method(Method::POST) .uri(uri.clone()) .header(header::CONTENT_TYPE, header::HeaderValue::from_static(API_CONTENT_TYPE)) - .header(header::USER_AGENT, header::HeaderValue::from_static(CLIENT_USER_AGENT)) + .header(header::USER_AGENT, self.runtime_agent.clone()) .body(Body::from(body.to_owned())) .unwrap() } @@ -341,7 +352,7 @@ impl<'ev> RuntimeClient { header::CONTENT_TYPE, header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) - .header(header::USER_AGENT, header::HeaderValue::from_static(CLIENT_USER_AGENT)) + .header(header::USER_AGENT, self.runtime_agent.clone()) .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static(ERROR_TYPE_UNHANDLED)) .body(Body::from(body)) .unwrap() diff --git a/lambda-runtime-client/src/lib.rs b/lambda-runtime-client/src/lib.rs index 2e922f12..85089ecc 100644 --- a/lambda-runtime-client/src/lib.rs +++ b/lambda-runtime-client/src/lib.rs @@ -32,7 +32,7 @@ //! } //! //! fn main() { -//! let client = RuntimeClient::new("http://localhost:8080", None) +//! let client = RuntimeClient::new("http://localhost:8080", None, None) //! .expect("Could not initialize client"); //! //! let (event_data, event_context) = client.next_event() diff --git a/lambda-runtime-core/build.rs b/lambda-runtime-core/build.rs index 21157584..c5bfb317 100644 --- a/lambda-runtime-core/build.rs +++ b/lambda-runtime-core/build.rs @@ -3,6 +3,7 @@ use std::env; use std::fs; use std::path::Path; use std::process::Command; +use std::io::Write; const RUNTIME_METADATA_FILE: &str = "runtime_release"; @@ -37,5 +38,16 @@ fn main() { .join(RUNTIME_METADATA_FILE); println!("Writing runtime metadata to: {}", metadata_path.to_str().unwrap()); println!("Runtime metadata: {}", agent); - fs::write(metadata_path, agent).expect("Could not write runtime metdata file"); + fs::write(metadata_path, agent.clone()).expect("Could not write runtime metdata file"); + + // next generate the metadata function for the runtime + let dest_path = Path::new(&out_dir).join("metadata.rs"); + let mut f = fs::File::create(&dest_path).unwrap(); + + f.write_all(format!(" +/// returns metdata information about the Lambda runtime +pub fn runtime_release() -> &'static str {{ + \"{}\" +}} +", agent).as_bytes()).unwrap(); } diff --git a/lambda-runtime-core/src/runtime.rs b/lambda-runtime-core/src/runtime.rs index 7e77684d..576e3fdb 100644 --- a/lambda-runtime-core/src/runtime.rs +++ b/lambda-runtime-core/src/runtime.rs @@ -11,6 +11,9 @@ use log::*; use std::{fmt::Display, marker::PhantomData}; use tokio::runtime::Runtime as TokioRuntime; +// include file generated during the build process +include!(concat!(env!("OUT_DIR"), "/metadata.rs")); + const MAX_RETRIES: i8 = 3; /// Creates a new runtime and begins polling for events using Lambda's Runtime APIs. @@ -85,7 +88,9 @@ pub fn start_with_config( } } - match RuntimeClient::new(&endpoint, runtime) { + let info = Option::from(runtime_release().to_owned()); + + match RuntimeClient::new(&endpoint, info, runtime) { Ok(client) => { start_with_runtime_client(f, function_config, client); } @@ -291,6 +296,7 @@ pub(crate) mod tests { .get_runtime_api_endpoint() .expect("Could not get runtime endpoint"), None, + None, ) .expect("Could not initialize client"); let handler = |_e: Vec, _c: context::Context| -> Result, HandlerError> { Ok(b"hello".to_vec()) }; From 253a481296f244eaf2ebe1cbc6e1fb30ab066c13 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 7 Jan 2019 09:36:42 -0800 Subject: [PATCH 32/43] Fixed format of build.rs --- lambda-runtime-core/build.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lambda-runtime-core/build.rs b/lambda-runtime-core/build.rs index c5bfb317..b5eb4bc9 100644 --- a/lambda-runtime-core/build.rs +++ b/lambda-runtime-core/build.rs @@ -1,9 +1,5 @@ use rustc_version::{Channel, VersionMeta}; -use std::env; -use std::fs; -use std::path::Path; -use std::process::Command; -use std::io::Write; +use std::{env, fs, io::Write, path::Path, process::Command}; const RUNTIME_METADATA_FILE: &str = "runtime_release"; @@ -44,10 +40,17 @@ fn main() { let dest_path = Path::new(&out_dir).join("metadata.rs"); let mut f = fs::File::create(&dest_path).unwrap(); - f.write_all(format!(" + f.write_all( + format!( + " /// returns metdata information about the Lambda runtime pub fn runtime_release() -> &'static str {{ \"{}\" }} -", agent).as_bytes()).unwrap(); +", + agent + ) + .as_bytes(), + ) + .unwrap(); } From db7f9943ca482c9421afbf82e4e2b71820ece6dd Mon Sep 17 00:00:00 2001 From: sapessi Date: Wed, 9 Jan 2019 19:32:02 -0800 Subject: [PATCH 33/43] Fixed critical issue in runtime client - request URIs were not generated correctly --- lambda-runtime-client/src/client.rs | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 09f4ed2f..7148fee7 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -125,8 +125,9 @@ pub struct EventContext { pub struct RuntimeClient { _runtime: Runtime, http_client: Client, - endpoint: Uri, + next_endpoint: Uri, runtime_agent: String, + host: String, } impl<'ev> RuntimeClient { @@ -150,15 +151,17 @@ impl<'ev> RuntimeClient { }; let http_client = Client::builder().executor(runtime.executor()).build_http(); - let endpoint = format!("http://{}/{}/runtime/invocation/next", host, RUNTIME_API_VERSION) + // we cached the parsed Uri since this never changes. + let next_endpoint = format!("http://{}/{}/runtime/invocation/next", host, RUNTIME_API_VERSION) .parse::() .context(ApiErrorKind::Unrecoverable("Could not parse API uri".to_string()))?; Ok(RuntimeClient { _runtime: runtime, http_client, - endpoint, + next_endpoint, runtime_agent, + host: host.to_owned(), }) } } @@ -173,7 +176,7 @@ impl<'ev> RuntimeClient { // the additional complexity. let resp = self .http_client - .get(self.endpoint.clone()) + .get(self.next_endpoint.clone()) .wait() .context(ApiErrorKind::Unrecoverable("Could not fetch next event".to_string()))?; @@ -230,7 +233,15 @@ impl<'ev> RuntimeClient { request_id, output.len() ); - let req = self.get_runtime_post_request(&self.endpoint, output); + let uri = format!( + "http://{}/{}/runtime/invocation/{}/response", + self.host, RUNTIME_API_VERSION, request_id + ) + .parse::() + .context(ApiErrorKind::Unrecoverable( + "Could not generate response uri".to_owned(), + ))?; + let req = self.get_runtime_post_request(&uri, output); let resp = self .http_client @@ -270,7 +281,15 @@ impl<'ev> RuntimeClient { request_id, e.error_message ); - let req = self.get_runtime_error_request(&self.endpoint, &e); + let uri = format!( + "http://{}/{}/runtime/invocation/{}/error", + self.host, RUNTIME_API_VERSION, request_id + ) + .parse::() + .context(ApiErrorKind::Unrecoverable( + "Could not generate response uri".to_owned(), + ))?; + let req = self.get_runtime_error_request(&uri, &e); let resp = self.http_client.request(req).wait().context(ApiErrorKind::Recoverable( "Could not post event error response".to_string(), From f0de223eece04e1f4e1cdbada2797bd9d6055911 Mon Sep 17 00:00:00 2001 From: sapessi Date: Wed, 9 Jan 2019 19:38:08 -0800 Subject: [PATCH 34/43] Fixed code issue - committed to fast earlier --- lambda-runtime-client/src/client.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 7148fee7..eb234df5 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -322,7 +322,13 @@ impl<'ev> RuntimeClient { /// to restart. pub fn fail_init(&self, e: &ErrorResponse) { error!("Calling fail_init Runtime API: {}", e.error_message); - let req = self.get_runtime_error_request(&self.endpoint, &e); + let uri = format!("http://{}/{}/runtime/init/error", self.host, RUNTIME_API_VERSION) + .parse::() + .map_err(|e| { + error!("Could not parse fail init URI: {}", e); + panic!("Killing runtime"); + }); + let req = self.get_runtime_error_request(&uri.unwrap(), &e); self.http_client .request(req) @@ -338,8 +344,8 @@ impl<'ev> RuntimeClient { } /// Returns the endpoint configured for this HTTP Runtime client. - pub fn get_endpoint(&self) -> String { - format!("{}", self.endpoint) + pub fn get_endpoint(&self) -> &str { + &self.host } /// Creates a Hyper `Request` object for the given `Uri` and `Body`. Sets the From d705def75f1033007233a8b5ec7a582ad05dcca3 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 21 Jan 2019 10:31:25 -0800 Subject: [PATCH 35/43] New error tests and failure featurs in Cargo. Still cannot get a backtrace out of failure :( --- lambda-runtime-client/Cargo.toml | 2 +- lambda-runtime-client/src/error.rs | 34 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lambda-runtime-client/Cargo.toml b/lambda-runtime-client/Cargo.toml index 5bf60f90..bf6eaf5e 100644 --- a/lambda-runtime-client/Cargo.toml +++ b/lambda-runtime-client/Cargo.toml @@ -24,4 +24,4 @@ serde_json = "^1" serde_derive = "^1" log = "0.4" lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } -failure = "^0.1" \ No newline at end of file +failure = { version = "^0.1", features = ["std", "backtrace", "derive"] } \ No newline at end of file diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 53b7d036..62fd41c6 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -146,3 +146,37 @@ impl From> for ApiError { Self { inner } } } + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + use failure::format_err; + use std::env; + + #[test] + fn does_not_produce_stack_trace() { + env::remove_var("RUST_BACKTRACE"); + let err = format_err!("Test error").compat(); + let resp_err = ErrorResponse::from(err); + assert_eq!(resp_err.stack_trace, None); + } + + /*#[test] skip this tests as it's broken right now + fn does_produce_stack_trace() { + env::set_var("RUST_FAILURE_BACKTRACE", "yes"); + env::set_var("RUST_BACKTRACE", "yes"); + let gen_err: fn() -> Result = || { + env::set_var("RUST_FAILURE_BACKTRACE", "yes"); + env::set_var("RUST_BACKTRACE", "yes"); + let _int = "hello".parse::()?; + Ok(_int) + }; + let err = gen_err(); + assert_eq!(true, err.is_err()); + let resp_err = ErrorResponse::from(err.err().unwrap()); + assert_eq!(false, resp_err.stack_trace.is_none()); + assert_eq!(true, resp_err.stack_trace.unwrap().len() > 1); + env::remove_var("RUST_BACKTRACE"); + env::remove_var("RUST_FAILURE_BACKTRACE"); + }*/ +} From a3aaedb8d332e8aa56b985046c1b11ae76000a29 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 21 Jan 2019 10:31:54 -0800 Subject: [PATCH 36/43] failure features import --- lambda-runtime-errors/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda-runtime-errors/Cargo.toml b/lambda-runtime-errors/Cargo.toml index c23a2a92..d14ffb98 100644 --- a/lambda-runtime-errors/Cargo.toml +++ b/lambda-runtime-errors/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.rs/lambda_runtime" [dependencies] log = "^0.4" -failure = "^0.1" +failure = { version = "^0.1", features = ["std", "backtrace", "derive"] } serde_json = "^1" [dev-dependencies] From fd35180ff82debb3cff0e42d4b97404964442cba Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 21 Jan 2019 10:33:06 -0800 Subject: [PATCH 37/43] Added doc comment on lambda! macro --- lambda-runtime-core/src/runtime.rs | 3 +++ lambda-runtime/src/lib.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lambda-runtime-core/src/runtime.rs b/lambda-runtime-core/src/runtime.rs index 576e3fdb..a957d5fe 100644 --- a/lambda-runtime-core/src/runtime.rs +++ b/lambda-runtime-core/src/runtime.rs @@ -31,6 +31,9 @@ where start_with_config(f, &EnvConfigProvider::default(), runtime) } +/// Initializes the Lambda runtime with the given handler. Optionally this macro can +/// also receive an instance of Tokio runtime for the Hyper HTTP client. If the Tokio +/// runtime is not passed the the Lambda Rust Runtime initializes a new one. #[macro_export] macro_rules! lambda { ($handler:ident) => { diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index c3fa5f73..03e1e050 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -109,6 +109,9 @@ where start_with_config(wrapped, &EnvConfigProvider::default(), runtime) } +/// Initializes the Lambda runtime with the given handler. Optionally this macro can +/// also receive an instance of Tokio runtime for the Hyper HTTP client. If the Tokio +/// runtime is not passed the the Lambda Rust Runtime initializes a new one. #[macro_export] macro_rules! lambda { ($handler:ident) => { From 32cd6e02fecc8029962aebd862373e4b63a0cec6 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 21 Jan 2019 14:19:11 -0800 Subject: [PATCH 38/43] Fixed failure dep in Cargo (CR feedback) --- lambda-runtime-client/Cargo.toml | 2 +- lambda-runtime-errors/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lambda-runtime-client/Cargo.toml b/lambda-runtime-client/Cargo.toml index bf6eaf5e..5bf60f90 100644 --- a/lambda-runtime-client/Cargo.toml +++ b/lambda-runtime-client/Cargo.toml @@ -24,4 +24,4 @@ serde_json = "^1" serde_derive = "^1" log = "0.4" lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } -failure = { version = "^0.1", features = ["std", "backtrace", "derive"] } \ No newline at end of file +failure = "^0.1" \ No newline at end of file diff --git a/lambda-runtime-errors/Cargo.toml b/lambda-runtime-errors/Cargo.toml index d14ffb98..c23a2a92 100644 --- a/lambda-runtime-errors/Cargo.toml +++ b/lambda-runtime-errors/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.rs/lambda_runtime" [dependencies] log = "^0.4" -failure = { version = "^0.1", features = ["std", "backtrace", "derive"] } +failure = "^0.1" serde_json = "^1" [dev-dependencies] From 0f54490542d560a2f8679e38953dd72c692e81c7 Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 21 Jan 2019 14:22:56 -0800 Subject: [PATCH 39/43] Removed unnecesary test (CR feedback) --- lambda-runtime-client/src/error.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 62fd41c6..06492ca9 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -160,23 +160,4 @@ pub(crate) mod tests { let resp_err = ErrorResponse::from(err); assert_eq!(resp_err.stack_trace, None); } - - /*#[test] skip this tests as it's broken right now - fn does_produce_stack_trace() { - env::set_var("RUST_FAILURE_BACKTRACE", "yes"); - env::set_var("RUST_BACKTRACE", "yes"); - let gen_err: fn() -> Result = || { - env::set_var("RUST_FAILURE_BACKTRACE", "yes"); - env::set_var("RUST_BACKTRACE", "yes"); - let _int = "hello".parse::()?; - Ok(_int) - }; - let err = gen_err(); - assert_eq!(true, err.is_err()); - let resp_err = ErrorResponse::from(err.err().unwrap()); - assert_eq!(false, resp_err.stack_trace.is_none()); - assert_eq!(true, resp_err.stack_trace.unwrap().len() > 1); - env::remove_var("RUST_BACKTRACE"); - env::remove_var("RUST_FAILURE_BACKTRACE"); - }*/ } From bb61d71fe93bf22b6d6d7ff648ab5c5a02b2913e Mon Sep 17 00:00:00 2001 From: sapessi Date: Mon, 21 Jan 2019 19:45:43 -0800 Subject: [PATCH 40/43] Another round of CR feedback --- lambda-runtime-client/Cargo.toml | 2 +- lambda-runtime-client/src/client.rs | 4 +- lambda-runtime-client/src/error.rs | 38 ++++++++++--------- lambda-runtime-core/examples/simple.rs | 2 +- lambda-runtime-core/src/env.rs | 2 +- lambda-runtime-core/src/runtime.rs | 3 -- lambda-runtime-errors-derive/Cargo.toml | 1 - lambda-runtime-errors/errorgen.py | 6 ++- lambda-runtime-errors/src/error_ext_impl.rs | 4 ++ lambda-runtime-errors/src/lib.rs | 5 +-- lambda-runtime/examples/basic.rs | 2 +- lambda-runtime/examples/custom_error.rs | 2 +- .../examples/custom_error_failure.rs | 2 +- lambda-runtime/examples/failure_error.rs | 2 +- .../examples/with_custom_runtime.rs | 2 +- lambda-runtime/src/lib.rs | 4 +- 16 files changed, 42 insertions(+), 39 deletions(-) diff --git a/lambda-runtime-client/Cargo.toml b/lambda-runtime-client/Cargo.toml index 5bf60f90..1ae05a91 100644 --- a/lambda-runtime-client/Cargo.toml +++ b/lambda-runtime-client/Cargo.toml @@ -21,7 +21,7 @@ tokio = "0.1" http = "0.1" serde = "^1" serde_json = "^1" -serde_derive = "^1" +serde_derive = "^1" log = "0.4" lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } failure = "^0.1" \ No newline at end of file diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index eb234df5..004913cc 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,4 +1,4 @@ -use crate::error::{ApiError, ApiErrorKind, ErrorResponse, ERROR_TYPE_UNHANDLED}; +use crate::error::{ApiError, ApiErrorKind, ErrorResponse}; use failure::ResultExt; use hyper::{ client::HttpConnector, @@ -378,7 +378,7 @@ impl<'ev> RuntimeClient { header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) .header(header::USER_AGENT, self.runtime_agent.clone()) - .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static(ERROR_TYPE_UNHANDLED)) + .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static("Unhandled")) .body(Body::from(body)) .unwrap() } diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 06492ca9..b8627d77 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -10,13 +10,6 @@ use std::{ option::Option, }; -/// Error type description for the `ErrorResponse` event. This type should be returned -/// for errors that were handled by the function code or framework. -#[allow(dead_code)] -pub(crate) const ERROR_TYPE_HANDLED: &str = "Handled"; -/// Error type description for the `ErrorResponse` event. This type is used for unhandled, -/// unexpcted errors. -pub(crate) const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; /// Error type for the error responses to the Runtime APIs. In the future, this library /// should use a customer-generated error code pub const RUNTIME_ERROR_TYPE: &str = "RustRuntimeError"; @@ -30,9 +23,8 @@ pub struct ErrorResponse { /// The error message generated by the application. #[serde(rename = "errorMessage")] pub error_message: String, - /// The error type for Lambda. This can be `Handled` or `Unhandled`. - /// Developers can use the `ERROR_TYPE_HANDLED` and `ERROR_TYPE_UNHANDLED` - /// constants to populate this field. + /// The error type for Lambda. Normally, this value is populated using the + /// `error_type()` method from the `LambdaErrorExt` trait. #[serde(rename = "errorType")] pub error_type: String, /// The stack trace for the exception as vector of strings. In the framework, @@ -49,8 +41,9 @@ impl ErrorResponse { /// # Arguments /// /// * `message` The error message to be returned to the APIs. Normally the error description() - /// * `err_type` The error type. Use the `ERROR_TYPE_HANDLED` and `ERROR_TYPE_UNHANDLED`. - /// * `code` A custom error code + /// * `err_type` An error type that identifies the root cause. Normally populated by the + /// `error_type()` method in the `LambdaErrorExt` trait. + /// * `backtrace` The stack trace for the error /// /// # Return /// A new instance of the `ErrorResponse` object. @@ -64,12 +57,13 @@ impl ErrorResponse { // if the env variable is enabled if let Some(stack) = backtrace { trace!("Begin backtrace collection"); - let trace_string = format!("{:?}", stack) - .lines() - .map(|s| s.to_string()) - .collect::>(); + err.stack_trace = Some( + format!("{:?}", stack) + .lines() + .map(|s| s.to_string()) + .collect::>(), + ); trace!("Completed backtrace collection"); - err.stack_trace = Option::from(trace_string); } err @@ -99,7 +93,7 @@ impl ApiError { } /// Failure context for the `ApiError` type. The kind is used to indicate whether the /// error is recoverable and should be retried or not. -#[derive(Clone, Eq, PartialEq, Debug, Fail)] +#[derive(Clone, PartialEq, Debug, Fail)] pub enum ApiErrorKind { /// Runtime implementations that receive recoverable errors should automatically /// retry requests @@ -160,4 +154,12 @@ pub(crate) mod tests { let resp_err = ErrorResponse::from(err); assert_eq!(resp_err.stack_trace, None); } + + #[test] + fn is_recoverable_eq_correctly() { + let rec_err = ApiError::from(ApiErrorKind::Recoverable("Some recoverable kind".to_owned())); + assert_eq!(true, rec_err.is_recoverable()); + let unrec_err = ApiError::from(ApiErrorKind::Unrecoverable("Some unrecovrable kind".to_owned())); + assert_eq!(false, unrec_err.is_recoverable()); + } } diff --git a/lambda-runtime-core/examples/simple.rs b/lambda-runtime-core/examples/simple.rs index 1644fb82..f83f3f34 100644 --- a/lambda-runtime-core/examples/simple.rs +++ b/lambda-runtime-core/examples/simple.rs @@ -4,7 +4,7 @@ use simple_logger; use std::error::Error; fn main() -> Result<(), Box> { - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler); Ok(()) diff --git a/lambda-runtime-core/src/env.rs b/lambda-runtime-core/src/env.rs index d21e05aa..94cde577 100644 --- a/lambda-runtime-core/src/env.rs +++ b/lambda-runtime-core/src/env.rs @@ -42,7 +42,7 @@ pub struct EnvConfigProvider; impl std::default::Default for EnvConfigProvider { fn default() -> Self { - EnvConfigProvider {} + EnvConfigProvider } } diff --git a/lambda-runtime-core/src/runtime.rs b/lambda-runtime-core/src/runtime.rs index dd82468b..175a2b7a 100644 --- a/lambda-runtime-core/src/runtime.rs +++ b/lambda-runtime-core/src/runtime.rs @@ -31,9 +31,6 @@ where start_with_config(f, &EnvConfigProvider::default(), runtime) } -/// Initializes the Lambda runtime with the given handler. Optionally this macro can -/// also receive an instance of Tokio runtime for the Hyper HTTP client. If the Tokio -/// runtime is not passed the the Lambda Rust Runtime initializes a new one. #[macro_export] /// Starts an event listener which will parse incoming events into the even type requested by /// `handler` and will invoke `handler` on each incoming event. Can optionally be passed a Tokio diff --git a/lambda-runtime-errors-derive/Cargo.toml b/lambda-runtime-errors-derive/Cargo.toml index 74aafdc2..55a2fb6c 100644 --- a/lambda-runtime-errors-derive/Cargo.toml +++ b/lambda-runtime-errors-derive/Cargo.toml @@ -11,7 +11,6 @@ repository = "https://github.com/awslabs/aws-lambda-rust-runtime" documentation = "https://docs.rs/lambda_runtime" [dependencies] -log = "^0.4" syn = "^0.15" synstructure = "^0.10" proc-macro2 = "^0.4" diff --git a/lambda-runtime-errors/errorgen.py b/lambda-runtime-errors/errorgen.py index 3f7efec0..04538651 100755 --- a/lambda-runtime-errors/errorgen.py +++ b/lambda-runtime-errors/errorgen.py @@ -111,7 +111,11 @@ def start_parsing(self): # code gen with open(GENERATED_FILE_NAME, "a") as f: - f.write("// Generated code, DO NOT MODIFY!\n\n") + f.write("""// Generated code, DO NOT MODIFY! +// This file contains the implementation of the LambdaErrorExt +// trait for most of the standard library errors as well as the +// implementation of the From trait for the HandlerError struct +// to support the same standard library errors.\n\n""") # use statements for err in parser.errors: diff --git a/lambda-runtime-errors/src/error_ext_impl.rs b/lambda-runtime-errors/src/error_ext_impl.rs index 8dae7cc7..04a46a6a 100644 --- a/lambda-runtime-errors/src/error_ext_impl.rs +++ b/lambda-runtime-errors/src/error_ext_impl.rs @@ -1,4 +1,8 @@ // Generated code, DO NOT MODIFY! +// This file contains the implementation of the LambdaErrorExt +// trait for most of the standard library errors as well as the +// implementation of the From trait for the HandlerError struct +// to support the same standard library errors. use crate::{HandlerError, LambdaErrorExt}; use std::{ diff --git a/lambda-runtime-errors/src/lib.rs b/lambda-runtime-errors/src/lib.rs index 3a26bd2d..b4cce3c4 100644 --- a/lambda-runtime-errors/src/lib.rs +++ b/lambda-runtime-errors/src/lib.rs @@ -25,10 +25,7 @@ pub trait LambdaErrorExt { impl LambdaErrorExt for Error { fn error_type(&self) -> &str { - if let Some(name) = self.find_root_cause().name() { - return name; - } - "FailureError" + self.find_root_cause().name().unwrap_or_else(|| "FailureError") } } diff --git a/lambda-runtime/examples/basic.rs b/lambda-runtime/examples/basic.rs index 480cf46d..1a64f628 100644 --- a/lambda-runtime/examples/basic.rs +++ b/lambda-runtime/examples/basic.rs @@ -18,7 +18,7 @@ struct CustomOutput { } fn main() -> Result<(), Box> { - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler); Ok(()) diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs index 066d2da5..35f0c0dd 100644 --- a/lambda-runtime/examples/custom_error.rs +++ b/lambda-runtime/examples/custom_error.rs @@ -46,7 +46,7 @@ struct CustomOutput { } fn main() -> Result<(), Box> { - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler); Ok(()) diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs index 12c72c83..8e8c33e8 100644 --- a/lambda-runtime/examples/custom_error_failure.rs +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -31,7 +31,7 @@ struct CustomOutput { } fn main() -> Result<(), Box> { - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler); Ok(()) diff --git a/lambda-runtime/examples/failure_error.rs b/lambda-runtime/examples/failure_error.rs index d159eb96..b426ecb2 100644 --- a/lambda-runtime/examples/failure_error.rs +++ b/lambda-runtime/examples/failure_error.rs @@ -17,7 +17,7 @@ struct CustomOutput { } fn main() -> Result<(), Box> { - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler); Ok(()) diff --git a/lambda-runtime/examples/with_custom_runtime.rs b/lambda-runtime/examples/with_custom_runtime.rs index f7ab817e..d0e6a0b7 100644 --- a/lambda-runtime/examples/with_custom_runtime.rs +++ b/lambda-runtime/examples/with_custom_runtime.rs @@ -20,7 +20,7 @@ struct CustomOutput { fn main() -> Result<(), Box> { let rt = Runtime::new()?; - simple_logger::init_with_level(log::Level::Debug).unwrap(); + simple_logger::init_with_level(log::Level::Debug)?; lambda!(my_handler, rt); Ok(()) diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 03e1e050..b8e0a597 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -110,8 +110,8 @@ where } /// Initializes the Lambda runtime with the given handler. Optionally this macro can -/// also receive an instance of Tokio runtime for the Hyper HTTP client. If the Tokio -/// runtime is not passed the the Lambda Rust Runtime initializes a new one. +/// also receive a customized instance of Tokio runtime to drive internal lambda operations +/// to completion #[macro_export] macro_rules! lambda { ($handler:ident) => { From 6891eaa8838020be71cb21f36beb1b50880f8d39 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 24 Jan 2019 09:28:51 -0800 Subject: [PATCH 41/43] Added comment to error header as per CR --- lambda-runtime-client/src/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 004913cc..63559bd2 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -378,6 +378,7 @@ impl<'ev> RuntimeClient { header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) .header(header::USER_AGENT, self.runtime_agent.clone()) + // this header is static for the runtime APIs and it's likely to go away in the future. .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static("Unhandled")) .body(Body::from(body)) .unwrap() From ee12d3781dfa391eb40337575afbedcdbb40ce4b Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 24 Jan 2019 09:29:25 -0800 Subject: [PATCH 42/43] Changed order of deps while cleaning up --- lambda-runtime-core/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lambda-runtime-core/Cargo.toml b/lambda-runtime-core/Cargo.toml index 0de51414..6578f563 100644 --- a/lambda-runtime-core/Cargo.toml +++ b/lambda-runtime-core/Cargo.toml @@ -15,13 +15,13 @@ log = "^0.4" hyper = "^0.12" tokio = "^0.1" backtrace = "^0.3" -lambda_runtime_client = { path = "../lambda-runtime-client", version = "^0.1" } -lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } chrono = "^0.4" failure = "^0.1" +lambda_runtime_client = { path = "../lambda-runtime-client", version = "^0.1" } +lambda_runtime_errors = { path = "../lambda-runtime-errors", version = "^0.1" } [dev-dependencies] -simple_logger = "^1" +simple_logger = "^1.0" [build-dependencies] rustc_version = "^0.2" \ No newline at end of file From 315c979a359d65d8edae9d974785643b139f7688 Mon Sep 17 00:00:00 2001 From: sapessi Date: Thu, 24 Jan 2019 09:29:58 -0800 Subject: [PATCH 43/43] Removed commented code as per CR --- lambda-runtime-errors-derive/src/lib.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lambda-runtime-errors-derive/src/lib.rs b/lambda-runtime-errors-derive/src/lib.rs index 0ee2fb9d..4018ccb6 100644 --- a/lambda-runtime-errors-derive/src/lib.rs +++ b/lambda-runtime-errors-derive/src/lib.rs @@ -5,23 +5,6 @@ use synstructure::decl_derive; decl_derive!([LambdaErrorExt, attributes()] => lambda_error_derive); fn lambda_error_derive(s: synstructure::Structure) -> TokenStream { - /* - if let Data::Struct(d) = &s.ast().data { - if let Fields::Named(fs) = &d.fields { - for f in fs.named.iter() { - let field_name = f.ident.clone(); - println!("!!Field name: {}", field_name.expect("Field must be named")); - } - } - }*/ - - for v in s.variants() { - println!("Variant: {}", v.ast().ident); - /*if let Some(id) = v.prefix { - println!("Variant: {}", id); - }*/ - } - let name = format!("{}", s.ast().ident); let err_impl = s.gen_impl(quote! {