1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
#![deny(missing_docs)] //! Enriches the `lambda` crate with [`http`](https://github.com/hyperium/http) //! types targeting AWS [ALB](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html), [API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) REST and HTTP API lambda integrations. //! //! This crate abstracts over all of these trigger events using standard [`http`](https://github.com/hyperium/http) types minimizing the mental overhead //! of understanding the nuances and variation between trigger details allowing you to focus more on your application while also giving you to the maximum flexibility to //! transparently use whichever lambda trigger suits your application and cost optimiztions best. //! //! # Examples //! //! ## Hello World //! //! `lambda_http` handlers adapt to the standard `lambda::Handler` interface using the [`handler`](fn.handler.html) function. //! //! The simplest case of an http handler is a function of an `http::Request` to a type that can be lifted into an `http::Response`. //! You can learn more about these types [here](trait.IntoResponse.html). //! //! Adding an `#[lambda(http)]` attribute to a `#[tokio::run]`-decorated `main` function will setup and run the Lambda function. //! //! Note: this comes at the expense of any onetime initialization your lambda task might find value in. //! The full body of your `main` function will be executed on **every** invocation of your lambda task. //! //! ```rust,no_run //! use netlify_lambda_http::{ //! lambda::{lambda, Context}, //! IntoResponse, Request, //! }; //! type Error = Box<dyn std::error::Error + Send + Sync + 'static>; //! //! #[lambda(http)] //! #[tokio::main] //! async fn main(_: Request, _: Context) -> Result<impl IntoResponse, Error> { //! Ok("👋 world!") //! } //! ``` //! //! ## Hello World, Without Macros //! //! For cases where your lambda might benfit from one time function initializiation might //! prefer a plain `main` function and invoke `netlify_lambda::run` explicitly in combination with the [`handler`](fn.handler.html) function. //! Depending on the runtime cost of your dependency bootstrapping, this can reduce the overall latency of your functions execution path. //! //! ```rust,no_run //! use netlify_lambda_http::{handler, lambda}; //! //! type Error = Box<dyn std::error::Error + Send + Sync + 'static>; //! //! #[tokio::main] //! async fn main() -> Result<(), Error> { //! // initialize dependencies once here for the lifetime of your //! // lambda task //! netlify_lambda::run(handler(|request, context| async { Ok("👋 world!") })).await?; //! Ok(()) //! } //! //! ``` //! //! ## Leveraging trigger provided data //! //! You can also access information provided directly from the underlying trigger events, like query string parameters, //! with the [`RequestExt`](trait.RequestExt.html) trait. //! //! ```rust,no_run //! use netlify_lambda_http::{handler, lambda::{self, Context}, IntoResponse, Request, RequestExt}; //! //! type Error = Box<dyn std::error::Error + Send + Sync + 'static>; //! //! #[tokio::main] //! async fn main() -> Result<(), Error> { //! netlify_lambda::run(handler(hello)).await?; //! Ok(()) //! } //! //! async fn hello( //! request: Request, //! _: Context //! ) -> Result<impl IntoResponse, Error> { //! Ok(format!( //! "hello {}", //! request //! .query_string_parameters() //! .get("name") //! .unwrap_or_else(|| "stranger") //! )) //! } //! ``` // only externed because maplit doesn't seem to play well with 2018 edition imports #[cfg(test)] #[macro_use] extern crate maplit; pub use http::{self, Response}; use netlify_lambda::Handler as LambdaHandler; pub use netlify_lambda::{self as lambda, Context}; pub use netlify_lambda_attributes::lambda; use aws_lambda_events::encodings::Body; use aws_lambda_events::event::apigw::ApiGatewayProxyRequest; pub mod ext; pub mod request; mod response; mod strmap; pub use crate::{ext::RequestExt, response::IntoResponse, strmap::StrMap}; use crate::{ request::{self as lambda_request, LambdaRequest, RequestOrigin}, response::LambdaResponse, }; use std::{ future::Future, pin::Pin, task::{Context as TaskContext, Poll}, }; /// Error type that lambdas may result in pub(crate) type Error = Box<dyn std::error::Error + Send + Sync + 'static>; /// Type alias for `http::Request`s with a fixed [`Body`](enum.Body.html) type 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::Handler` constrained to `http` crate `Request` and `Response` types pub trait Handler: 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>> + 'static; /// Function used to execute handler behavior fn call(&mut self, event: Request, context: Context) -> Self::Fut; } /// An implementation of `Handler` for a given closure return a `Future` representing the computed response impl<F, R, Fut> Handler for F where F: Fn(Request, Context) -> Fut, R: IntoResponse, Fut: Future<Output = Result<R, Error>> + Send + 'static, { type Response = R; type Error = Error; type Fut = Fut; fn call(&mut self, event: Request, context: Context) -> Self::Fut { (self)(event, context) } } #[doc(hidden)] pub struct TransformResponse<R, E> { request_origin: RequestOrigin, fut: Pin<Box<dyn Future<Output = Result<R, E>>>>, } impl<R, E> Future for TransformResponse<R, E> where R: IntoResponse, { 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, } } } /// Adapts a [`Handler`](trait.Handler.html) to the `netlify_lambda::run` interface /// /// This is an abstract interface that tries to deserialize the request payload /// in any possible [`request::LambdaRequest`] value. pub fn handler<H: Handler>(handler: H) -> Adapter<H> { Adapter { handler } } /// Exists only to satisfy the trait cover rule for `lambda::Handler` impl /// /// User code should never need to interact with this type directly. Since `Adapter` implements `Handler` /// It serves as a opaque trait covering type. /// /// 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> { handler: H, } impl<H: Handler> Handler for Adapter<H> { type Response = H::Response; type Error = H::Error; type Fut = H::Fut; fn call(&mut self, event: Request, context: Context) -> Self::Fut { self.handler.call(event, context) } } impl<H: Handler> LambdaHandler<LambdaRequest, LambdaResponse> for Adapter<H> { type Error = H::Error; type Fut = TransformResponse<H::Response, Self::Error>; fn call(&mut self, event: LambdaRequest, context: Context) -> Self::Fut { let request_origin = event.request_origin(); let fut = Box::pin(self.handler.call(event.into(), context)); TransformResponse { request_origin, fut } } } /// Adapts a [`Handler`](trait.Handler.html) to the `netlify_lambda::run` interface /// /// This is a concrete interface that tries to deserialize the request payload /// into an AWS API Gateway Proxy definition. This definition is the same that the /// AWS Invoke API uses to send invocation requests to lambda. pub fn proxy_handler<H: Handler>(handler: H) -> ProxyAdapter<H> { ProxyAdapter { handler } } /// Exists only to satisfy the trait cover rule for `lambda::Handler` impl /// /// User code should never need to interact with this type directly. Since `ProxyAdapter` implements `Handler` /// It serves as a opaque trait covering type. /// /// 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 ProxyAdapter<H: Handler> { handler: H, } impl<H: Handler> Handler for ProxyAdapter<H> { type Response = H::Response; type Error = H::Error; type Fut = H::Fut; fn call(&mut self, event: Request, context: Context) -> Self::Fut { self.handler.call(event, context) } } impl<H: Handler> LambdaHandler<ApiGatewayProxyRequest, LambdaResponse> for ProxyAdapter<H> { type Error = H::Error; type Fut = TransformResponse<H::Response, Self::Error>; fn call(&mut self, event: ApiGatewayProxyRequest, context: Context) -> Self::Fut { let request_origin = RequestOrigin::ApiGatewayV1; let req = lambda_request::into_proxy_request(event); let fut = Box::pin(self.handler.call(req, context)); TransformResponse { request_origin, fut } } }