diff --git a/lambda-http/src/lib.rs b/lambda-http/src/lib.rs index 3581002f..4d52d277 100644 --- a/lambda-http/src/lib.rs +++ b/lambda-http/src/lib.rs @@ -134,7 +134,7 @@ pub struct Adapter<'a, R, S> { impl<'a, R, S, E> From for Adapter<'a, R, S> where S: Service, - S::Future: 'a, + S::Future: Send + 'a, R: IntoResponse, { fn from(service: S) -> Self { @@ -148,7 +148,7 @@ where impl<'a, R, S, E> Service> for Adapter<'a, R, S> where S: Service, - S::Future: 'a, + S::Future: Send + 'a, R: IntoResponse, { type Response = LambdaResponse; @@ -176,9 +176,65 @@ where pub async fn run<'a, R, S, E>(handler: S) -> Result<(), Error> where S: Service, - S::Future: 'a, + S::Future: Send + 'a, R: IntoResponse, E: std::fmt::Debug + std::fmt::Display, { lambda_runtime::run(Adapter::from(handler)).await } + +#[cfg(test)] +mod test_adapter { + use std::task::{Context, Poll}; + + use crate::{ + http::{Response, StatusCode}, + lambda_runtime::LambdaEvent, + request::LambdaRequest, + response::LambdaResponse, + tower::{util::BoxService, Service, ServiceBuilder, ServiceExt}, + Adapter, Body, Request, + }; + + // A middleware that logs requests before forwarding them to another service + struct LogService { + inner: S, + } + + impl Service> for LogService + where + S: Service>, + { + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx) + } + + fn call(&mut self, event: LambdaEvent) -> Self::Future { + // Log the request + println!("Lambda event: {:#?}", event); + + self.inner.call(event) + } + } + + /// This tests that `Adapter` can be used in a `tower::Service` where the user + /// may require additional middleware between `lambda_runtime::run` and where + /// the `LambdaEvent` is converted into a `Request`. + #[test] + fn adapter_is_boxable() { + let _service: BoxService, LambdaResponse, http::Error> = ServiceBuilder::new() + .layer_fn(|service| { + // This could be any middleware that logs, inspects, or manipulates + // the `LambdaEvent` before it's converted to a `Request` by `Adapter`. + + LogService { inner: service } + }) + .layer_fn(Adapter::from) + .service_fn(|_event: Request| async move { Response::builder().status(StatusCode::OK).body(Body::Empty) }) + .boxed(); + } +} diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index 27d54cf2..e1e6985e 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -61,7 +61,7 @@ impl LambdaRequest { } /// RequestFuture type -pub type RequestFuture<'a, R, E> = Pin> + 'a>>; +pub type RequestFuture<'a, R, E> = Pin> + Send + 'a>>; /// Represents the origin from which the lambda was requested from. #[doc(hidden)] diff --git a/lambda-http/src/response.rs b/lambda-http/src/response.rs index 556106d3..c8593a62 100644 --- a/lambda-http/src/response.rs +++ b/lambda-http/src/response.rs @@ -129,7 +129,7 @@ pub trait IntoResponse { impl IntoResponse for Response where - B: ConvertBody + 'static, + B: ConvertBody + Send + 'static, { fn into_response(self) -> ResponseFuture { let (parts, body) = self.into_parts(); @@ -180,7 +180,7 @@ impl IntoResponse for serde_json::Value { } } -pub type ResponseFuture = Pin>>>; +pub type ResponseFuture = Pin> + Send>>; pub trait ConvertBody { fn convert(self, parts: HeaderMap) -> BodyFuture; @@ -188,7 +188,8 @@ pub trait ConvertBody { impl ConvertBody for B where - B: HttpBody + Unpin + 'static, + B: HttpBody + Unpin + Send + 'static, + B::Data: Send, B::Error: fmt::Debug, { fn convert(self, headers: HeaderMap) -> BodyFuture { @@ -227,7 +228,8 @@ where fn convert_to_binary(body: B) -> BodyFuture where - B: HttpBody + Unpin + 'static, + B: HttpBody + Unpin + Send + 'static, + B::Data: Send, B::Error: fmt::Debug, { Box::pin(async move { Body::from(to_bytes(body).await.expect("unable to read bytes from body").to_vec()) }) @@ -235,7 +237,8 @@ where fn convert_to_text(body: B, content_type: &str) -> BodyFuture where - B: HttpBody + Unpin + 'static, + B: HttpBody + Unpin + Send + 'static, + B::Data: Send, B::Error: fmt::Debug, { let mime_type = content_type.parse::(); @@ -260,7 +263,7 @@ where }) } -pub type BodyFuture = Pin>>; +pub type BodyFuture = Pin + Send>>; #[cfg(test)] mod tests {