Skip to content

Removed 'static bound requirements for the lambda_http crate and create a shared resources example for the crate #333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 8, 2021
Merged
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
4 changes: 1 addition & 3 deletions lambda-http/examples/hello-http.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use lambda_http::{
handler,
lambda_runtime::{self, Context},
lambda_runtime::{self, Context, Error},
IntoResponse, Request, RequestExt, Response,
};

type Error = Box<dyn std::error::Error + Send + Sync + 'static>;

#[tokio::main]
async fn main() -> Result<(), Error> {
lambda_runtime::run(handler(func)).await?;
Expand Down
39 changes: 39 additions & 0 deletions lambda-http/examples/shared-resources-example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use lambda_http::{
handler,
lambda_runtime::{self, Context, Error},
IntoResponse, Request, RequestExt, Response,
};

struct SharedClient {
name: &'static str,
}

impl SharedClient {
fn response(&self, req_id: String, first_name: &str) -> String {
format!("{}: Client ({}) invoked by {}.", req_id, self.name, first_name)
}
}

#[tokio::main]
async fn main() -> Result<(), Error> {
// Create the "client" and a reference to it, so that we can pass this into the handler closure below.
let shared_client = SharedClient {
name: "random_client_name_1",
};
let shared_client_ref = &shared_client;

// Define a closure here that makes use of the shared client.
let handler_func_closure = move |event: Request, ctx: Context| async move {
Ok(match event.query_string_parameters().get("first_name") {
Some(first_name) => shared_client_ref.response(ctx.request_id, first_name).into_response(),
_ => Response::builder()
.status(400)
.body("Empty first name".into())
.expect("failed to render response"),
})
};

// Pass the closure to the runtime here.
lambda_runtime::run(handler(handler_func_closure)).await?;
Ok(())
}
31 changes: 18 additions & 13 deletions lambda-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ use crate::{
};
use std::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context as TaskContext, Poll},
};
Expand All @@ -87,28 +88,31 @@ pub type Request = http::Request<Body>;
/// Functions serving as ALB and API Gateway REST and HTTP API handlers must conform to this type.
///
/// This can be viewed as a `lambda_runtime::Handler` constrained to `http` crate `Request` and `Response` types
pub trait Handler: Sized {
pub trait Handler<'a>: Sized {
/// The type of Error that this Handler will return
type Error;
/// The type of Response this Handler will return
type Response: IntoResponse;
/// The type of Future this Handler will return
type Fut: Future<Output = Result<Self::Response, Self::Error>> + Send + 'static;
type Fut: Future<Output = Result<Self::Response, Self::Error>> + Send + 'a;
/// Function used to execute handler behavior
fn call(&self, event: Request, context: Context) -> Self::Fut;
}

/// Adapts a [`Handler`](trait.Handler.html) to the `lambda_runtime::run` interface
pub fn handler<H: Handler>(handler: H) -> Adapter<H> {
Adapter { handler }
pub fn handler<'a, H: Handler<'a>>(handler: H) -> Adapter<'a, H> {
Adapter {
handler,
_pd: PhantomData,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give more context on what this is for? I'm not sure I understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, if you look down below it's because I added the explicit lifetime to the Adapter struct, because it holds a Handler that requires a lifetime parameter. Because the 'a lifetime is unused in Adapter, otherwise, the compiler complains about the unused lifetime parameter we supplied to it. The PhantomData is just a zero-sized compiler dummy value that holds the 'a lifetime and "uses" the lifetime to make the compiler happy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay. That makes sense now thank you.

}
}

/// An implementation of `Handler` for a given closure return a `Future` representing the computed response
impl<F, R, Fut> Handler for F
impl<'a, F, R, Fut> Handler<'a> for F
where
F: Fn(Request, Context) -> Fut,
R: IntoResponse,
Fut: Future<Output = Result<R, Error>> + Send + 'static,
Fut: Future<Output = Result<R, Error>> + Send + 'a,
{
type Response = R;
type Error = Error;
Expand All @@ -119,12 +123,12 @@ where
}

#[doc(hidden)]
pub struct TransformResponse<R, E> {
pub struct TransformResponse<'a, R, E> {
request_origin: RequestOrigin,
fut: Pin<Box<dyn Future<Output = Result<R, E>> + Send>>,
fut: Pin<Box<dyn Future<Output = Result<R, E>> + Send + 'a>>,
}

impl<R, E> Future for TransformResponse<R, E>
impl<'a, R, E> Future for TransformResponse<'a, R, E>
where
R: IntoResponse,
{
Expand All @@ -146,11 +150,12 @@ where
///
/// See [this article](http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/)
/// for a larger explaination of why this is nessessary
pub struct Adapter<H: Handler> {
pub struct Adapter<'a, H: Handler<'a>> {
handler: H,
_pd: PhantomData<&'a H>,
}

impl<H: Handler> Handler for Adapter<H> {
impl<'a, H: Handler<'a>> Handler<'a> for Adapter<'a, H> {
type Response = H::Response;
type Error = H::Error;
type Fut = H::Fut;
Expand All @@ -159,9 +164,9 @@ impl<H: Handler> Handler for Adapter<H> {
}
}

impl<H: Handler> LambdaHandler<LambdaRequest<'_>, LambdaResponse> for Adapter<H> {
impl<'a, H: Handler<'a>> LambdaHandler<LambdaRequest<'a>, LambdaResponse> for Adapter<'a, H> {
type Error = H::Error;
type Fut = TransformResponse<H::Response, Self::Error>;
type Fut = TransformResponse<'a, H::Response, Self::Error>;

fn call(&self, event: LambdaRequest<'_>, context: Context) -> Self::Fut {
let request_origin = event.request_origin();
Expand Down