Skip to content

feat(lambda-http): accept http_body::Body in responses #491

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 12 commits into from
Jul 13, 2022
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ INTEG_EXTENSIONS := extension-fn extension-trait logs-trait
# Using musl to run extensions on both AL1 and AL2
INTEG_ARCH := x86_64-unknown-linux-musl

define uppercase
$(shell sed -r 's/(^|-)(\w)/\U\2/g' <<< $(1))
endef

pr-check:
cargo +1.54.0 check --all
cargo +stable fmt --all -- --check
Expand All @@ -15,7 +19,7 @@ pr-check:

integration-tests:
# Build Integration functions
cross build --release --target $(INTEG_ARCH) -p lambda_integration_tests
cargo zigbuild --release --target $(INTEG_ARCH) -p lambda_integration_tests
rm -rf ./build
mkdir -p ./build
${MAKE} ${MAKEOPTS} $(foreach function,${INTEG_FUNCTIONS_BUILD}, build-integration-function-${function})
Expand All @@ -37,7 +41,7 @@ build-integration-function-%:

build-integration-extension-%:
mkdir -p ./build/$*/extensions
cp -v ./target/$(INTEG_ARCH)/release/$* ./build/$*/extensions/$*
cp -v ./target/$(INTEG_ARCH)/release/$* ./build/$*/extensions/$(call uppercase,$*)

invoke-integration-function-%:
aws lambda invoke --function-name $$(aws cloudformation describe-stacks --stack-name $(INTEG_STACK_NAME) \
Expand All @@ -56,4 +60,3 @@ invoke-integration-api-%:
curl -X POST -d '{"command": "hello"}' $(API_URL)/trait/post
curl -X POST -d '{"command": "hello"}' $(API_URL)/al2/post
curl -X POST -d '{"command": "hello"}' $(API_URL)/al2-trait/post

6 changes: 3 additions & 3 deletions examples/http-basic-lambda/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use lambda_http::{run, service_fn, Error, IntoResponse, Request, Response};
use lambda_http::{run, service_fn, Body, Error, Request, Response};

/// This is the main body for the function.
/// Write your code inside it.
/// There are some code examples in the Runtime repository:
/// - https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples
async fn function_handler(_event: Request) -> Result<impl IntoResponse, Error> {
async fn function_handler(_event: Request) -> Result<Response<Body>, Error> {
// Extract some useful information from the request

// Return something that implements IntoResponse.
// It will be serialized to the right response event automatically by the runtime
let resp = Response::builder()
.status(200)
.header("content-type", "text/html")
.body("Hello AWS Lambda HTTP request")
.body("Hello AWS Lambda HTTP request".into())
.map_err(Box::new)?;
Ok(resp)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/http-cors/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ async fn main() -> Result<(), Error> {

async fn func(event: Request) -> Result<Response<Body>, Error> {
Ok(match event.query_string_parameters().first("first_name") {
Some(first_name) => format!("Hello, {}!", first_name).into_response(),
Some(first_name) => format!("Hello, {}!", first_name).into_response().await,
_ => Response::builder()
.status(400)
.body("Empty first name".into())
Expand Down
2 changes: 1 addition & 1 deletion examples/http-query-parameters/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use lambda_http::{run, service_fn, Error, IntoResponse, Request, RequestExt, Res
async fn function_handler(event: Request) -> Result<impl IntoResponse, Error> {
// Extract some useful information from the request
Ok(match event.query_string_parameters().first("first_name") {
Some(first_name) => format!("Hello, {}!", first_name).into_response(),
Some(first_name) => format!("Hello, {}!", first_name).into_response().await,
_ => Response::builder()
.status(400)
.body("Empty first name".into())
Expand Down
4 changes: 3 additions & 1 deletion examples/http-raw-path/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ async fn main() -> Result<(), Error> {
}

async fn func(event: Request) -> Result<impl IntoResponse, Error> {
let res = format!("The raw path for this request is: {}", event.raw_http_path()).into_response();
let res = format!("The raw path for this request is: {}", event.raw_http_path())
.into_response()
.await;

Ok(res)
}
9 changes: 6 additions & 3 deletions examples/http-shared-resource/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ async fn main() -> Result<(), Error> {
// Define a closure here that makes use of the shared client.
let handler_func_closure = move |event: Request| async move {
Result::<Response<Body>, Error>::Ok(match event.query_string_parameters().first("first_name") {
Some(first_name) => shared_client_ref
.response(event.lambda_context().request_id, first_name)
.into_response(),
Some(first_name) => {
shared_client_ref
.response(event.lambda_context().request_id, first_name)
.into_response()
.await
}
_ => Response::builder()
.status(400)
.body("Empty first name".into())
Expand Down
3 changes: 3 additions & 0 deletions lambda-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ base64 = "0.13.0"
bytes = "1"
http = "0.2"
http-body = "0.4"
hyper = "0.14"
lambda_runtime = { path = "../lambda-runtime", version = "0.5" }
serde = { version = "^1", features = ["derive"] }
serde_json = "^1"
serde_urlencoded = "0.7.0"
query_map = { version = "0.5", features = ["url-query"] }
mime = "0.3.16"
encoding_rs = "0.8.31"

[dependencies.aws_lambda_events]
version = "^0.6.3"
Expand Down
29 changes: 20 additions & 9 deletions lambda-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ extern crate maplit;
pub use http::{self, Response};
use lambda_runtime::LambdaEvent;
pub use lambda_runtime::{self, service_fn, tower, Context, Error, Service};
use request::RequestFuture;
use response::ResponseFuture;

pub mod ext;
pub mod request;
Expand All @@ -91,9 +93,9 @@ pub type Request = http::Request<Body>;
///
/// This is used by the `Adapter` wrapper and is completely internal to the `lambda_http::run` function.
#[doc(hidden)]
pub struct TransformResponse<'a, R, E> {
request_origin: RequestOrigin,
fut: Pin<Box<dyn Future<Output = Result<R, E>> + 'a>>,
pub enum TransformResponse<'a, R, E> {
Request(RequestOrigin, RequestFuture<'a, R, E>),
Response(RequestOrigin, ResponseFuture),
}

impl<'a, R, E> Future for TransformResponse<'a, R, E>
Expand All @@ -103,11 +105,19 @@ where
type Output = Result<LambdaResponse, E>;

fn poll(mut self: Pin<&mut Self>, cx: &mut TaskContext) -> Poll<Self::Output> {
match self.fut.as_mut().poll(cx) {
Poll::Ready(result) => Poll::Ready(
result.map(|resp| LambdaResponse::from_response(&self.request_origin, resp.into_response())),
),
Poll::Pending => Poll::Pending,
match *self {
TransformResponse::Request(ref mut origin, ref mut request) => match request.as_mut().poll(cx) {
Poll::Ready(Ok(resp)) => {
*self = TransformResponse::Response(origin.clone(), resp.into_response());
self.poll(cx)
}
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
Poll::Pending => Poll::Pending,
},
TransformResponse::Response(ref mut origin, ref mut response) => match response.as_mut().poll(cx) {
Poll::Ready(resp) => Poll::Ready(Ok(LambdaResponse::from_response(origin, resp))),
Poll::Pending => Poll::Pending,
},
}
}
}
Expand Down Expand Up @@ -153,7 +163,8 @@ where
let request_origin = req.payload.request_origin();
let event: Request = req.payload.into();
let fut = Box::pin(self.service.call(event.with_lambda_context(req.context)));
TransformResponse { request_origin, fut }

TransformResponse::Request(request_origin, fut)
}
}

Expand Down
7 changes: 6 additions & 1 deletion lambda-http/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use http::header::HeaderName;
use query_map::QueryMap;
use serde::Deserialize;
use serde_json::error::Error as JsonError;
use std::future::Future;
use std::pin::Pin;
use std::{io::Read, mem};

/// Internal representation of an Lambda http event from
Expand Down Expand Up @@ -56,9 +58,12 @@ impl LambdaRequest {
}
}

/// RequestFuture type
pub type RequestFuture<'a, R, E> = Pin<Box<dyn Future<Output = Result<R, E>> + 'a>>;

/// Represents the origin from which the lambda was requested from.
#[doc(hidden)]
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum RequestOrigin {
/// API Gateway request origin
#[cfg(feature = "apigw_rest")]
Expand Down
Loading