Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ serde_json = { version = "1.0" }

[dev-dependencies]
serde = { version = "1", features = ["derive"] }
serial_test = "2.0"

[features]
default = ["ureq"]
Expand Down
2 changes: 1 addition & 1 deletion examples/echo-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl EventHandler for EchoEventHandler {

fn on_event<Ctx: LambdaContext>(
&mut self,
event: &str,
event: String,
context: &Ctx,
) -> Result<Self::EventOutput, Self::EventError> {
// Get the aws request id
Expand Down
5 changes: 3 additions & 2 deletions src/api/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ pub trait EventHandler: Sized {
/// Processes each incoming lambda event and returns a [`Result`] with the lambda's output.
/// # Arguments
///
/// * `event` - The JSON event as a string slice, should be deserialized by the implementation.
/// * `event` - The JSON event as a string, should be deserialized by the implementation.
/// * `context` - A shared reference to the current event context.
///
/// `Ctx` Defines the context object type, typically a [`crate::data::context::EventContext`].
fn on_event<Ctx: LambdaContext>(
&mut self,
event: &str,
event: String,
context: &Ctx,
) -> Result<Self::EventOutput, Self::EventError>;
}
2 changes: 1 addition & 1 deletion src/api/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,6 @@ pub trait LambdaAPIResponse {
}

fn is_err(&self) -> bool {
matches!(self.get_status_code(), 400..=499 | 500..=599)
matches!(self.get_status_code(), 400..=599)
}
}
4 changes: 2 additions & 2 deletions src/api/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ pub trait Transport: Default {
&self,
url: &str,
body: Option<&str>,
headers: Option<(Vec<&str>, Vec<&str>)>,
headers: Option<&[(&str, &str)]>,
) -> Result<Self::Response, Error>;
/// Sends an HTTP POST request to the specified `url` with the optional `body` and `headers`.
fn post(
&self,
url: &str,
body: Option<&str>,
headers: Option<(Vec<&str>, Vec<&str>)>,
headers: Option<&[(&str, &str)]>,
) -> Result<Self::Response, Error>;
}
21 changes: 7 additions & 14 deletions src/backends/ureq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,8 @@ impl LambdaAPIResponse for ureq::Response {

#[inline]
fn get_deadline(&self) -> Option<u64> {
match self.header(AWS_DEADLINE_MS) {
Some(ms) => match ms.parse::<u64>() {
Ok(val) => Some(val),
Err(_) => None,
},
None => None,
}
self.header(AWS_DEADLINE_MS)
.and_then(|ms| ms.parse::<u64>().ok())
}

#[inline]
Expand Down Expand Up @@ -84,14 +79,12 @@ impl UreqTransport {
method: &str,
url: &str,
body: Option<&str>,
headers: Option<(Vec<&str>, Vec<&str>)>,
headers: Option<&[(&str, &str)]>,
) -> Result<ureq::Response, Error> {
let mut req = self.agent.request(method, url);
if let Some(headers) = headers {
let (keys, values) = headers;
let len = std::cmp::min(keys.len(), values.len());
for i in 0..len {
req = req.set(keys[i], values[i]);
for (key, value) in headers.iter() {
req = req.set(key, value);
}
}
if let Some(body) = body {
Expand All @@ -110,7 +103,7 @@ impl Transport for UreqTransport {
&self,
url: &str,
body: Option<&str>,
headers: Option<(Vec<&str>, Vec<&str>)>,
headers: Option<&[(&str, &str)]>,
) -> Result<Self::Response, Error> {
self.request("GET", url, body, headers)
}
Expand All @@ -119,7 +112,7 @@ impl Transport for UreqTransport {
&self,
url: &str,
body: Option<&str>,
headers: Option<(Vec<&str>, Vec<&str>)>,
headers: Option<&[(&str, &str)]>,
) -> Result<Self::Response, Error> {
self.request("POST", url, body, headers)
}
Expand Down
2 changes: 2 additions & 0 deletions src/data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@

/// Defines the interface of the context object and provides an implementation for it.
pub mod context;
#[cfg(test)]
mod tests;
47 changes: 47 additions & 0 deletions src/data/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#[cfg(test)]
mod tests {
use crate::api::{InitializationType, LambdaContext, LambdaContextSetter, LambdaEnvVars};
use crate::data::context::EventContext;
use serial_test::serial;
use std::env;

#[test]
#[serial]
fn test_context_default_from_env() {
// Set some env vars
env::set_var("AWS_LAMBDA_FUNCTION_NAME", "my-func");
env::set_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128");
env::set_var("AWS_LAMBDA_INITIALIZATION_TYPE", "on-demand");
env::set_var("_HANDLER", "index.handler");

let ctx = EventContext::default();

assert_eq!(ctx.get_lambda_function_name(), Some("my-func"));
assert_eq!(ctx.get_lambda_function_memory_size(), Some(128));
assert!(matches!(
ctx.get_lambda_initialization_type(),
InitializationType::OnDemand
));
assert_eq!(ctx.get_handler_location(), Some("index.handler"));

// Clean up
env::remove_var("AWS_LAMBDA_FUNCTION_NAME");
env::remove_var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE");
env::remove_var("AWS_LAMBDA_INITIALIZATION_TYPE");
env::remove_var("_HANDLER");
}

#[test]
fn test_context_setters() {
let mut ctx = EventContext::default();

ctx.set_aws_request_id(Some("req-123"));
assert_eq!(ctx.get_aws_request_id(), Some("req-123"));

ctx.set_invoked_function_arn(Some("arn:aws:lambda:us-east-1:123456789012:function:my-func"));
assert_eq!(
ctx.get_invoked_function_arn(),
Some("arn:aws:lambda:us-east-1:123456789012:function:my-func")
);
}
}
17 changes: 12 additions & 5 deletions src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use crate::api::{
use crate::data::context::EventContext;
use crate::error::{Error, CONTAINER_ERR};

#[cfg(test)]
mod tests;

// Already handles any panic inducing errors
macro_rules! handle_response {
($resp:expr) => {
Expand Down Expand Up @@ -117,7 +120,10 @@ where
// Failing to get the next event will either panic (on server error) or continue with an error (on client-error codes).
let next_invo = match self.next_invocation() {
// TODO - perhaps log the error
Err(_e) => continue,
Err(e) => {
eprintln!("Failed to get next invocation: {}", e);
continue;
}
Ok(resp) => resp,
};

Expand All @@ -126,12 +132,11 @@ where
let event = next_invo.get_body().unwrap();

// Execute the event handler
// TODO - pass the event an an owned String
let lambda_output = self
.handler
.as_mut()
.unwrap()
.on_event(&event, &self.context);
.on_event(event, &self.context);
let request_id = self.context.get_aws_request_id().unwrap();

// TODO - figure out what we'd like to do with the result returned from success/client-err api responses (e.g: log, run a user defined callback...)
Expand Down Expand Up @@ -211,7 +216,8 @@ where
"http://{}/{}/runtime/init/error",
self.api_base, self.version
);
let headers = error_type.map(|et| (vec![AWS_FUNC_ERR_TYPE], vec![et]));
let headers_storage = error_type.map(|et| [(AWS_FUNC_ERR_TYPE, et)]);
let headers = headers_storage.as_ref().map(|h| &h[..]);
let resp = self.transport.post(&url, error_req, headers)?;
handle_response!(resp);

Expand All @@ -228,7 +234,8 @@ where
"http://{}/{}/runtime/invocation/{}/error",
self.api_base, self.version, request_id
);
let headers = error_type.map(|et| (vec![AWS_FUNC_ERR_TYPE], vec![et]));
let headers_storage = error_type.map(|et| [(AWS_FUNC_ERR_TYPE, et)]);
let headers = headers_storage.as_ref().map(|h| &h[..]);
let resp = self.transport.post(&url, error_req, headers)?;
handle_response!(resp);

Expand Down
Loading