Skip to content

Commit 3f84dfc

Browse files
committed
feat(lambda-http): implement tower::Service (part 2)
1 parent 195ea41 commit 3f84dfc

File tree

2 files changed

+33
-56
lines changed

2 files changed

+33
-56
lines changed

lambda-http/src/lib.rs

Lines changed: 31 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ extern crate maplit;
6363

6464
pub use http::{self, Response};
6565
pub use lambda_runtime::{self, Context};
66-
use lambda_runtime::{Error, LambdaEvent, Service};
66+
use lambda_runtime::{
67+
tower::util::{service_fn, ServiceFn},
68+
Error, LambdaEvent, Service,
69+
};
6770

6871
mod body;
6972
pub mod ext;
@@ -85,54 +88,37 @@ use std::{
8588
/// Type alias for `http::Request`s with a fixed [`Body`](enum.Body.html) type
8689
pub type Request = http::Request<Body>;
8790

88-
/// Functions serving as ALB and API Gateway REST and HTTP API handlers must conform to this type.
89-
///
90-
/// This can be viewed as a `lambda_runtime::Handler` constrained to `http` crate `Request` and `Response` types
91-
pub trait Handler<'a>: Sized {
92-
/// The type of Error that this Handler will return
93-
type Error;
94-
/// The type of Response this Handler will return
95-
type Response: IntoResponse;
96-
/// The type of Future this Handler will return
97-
type Fut: Future<Output = Result<Self::Response, Self::Error>> + 'a;
98-
/// Function used to execute handler behavior
99-
fn call(&mut self, event: Request, context: Context) -> Self::Fut;
91+
/// Wraps a function that takes 2 arguments into one that only takes a [`LambdaEvent`]
92+
fn handler_wrapper<A, Fut>(f: impl Fn(A, Context) -> Fut) -> impl Fn(LambdaEvent<A>) -> Fut {
93+
move |req| f(req.event, req.context)
10094
}
10195

102-
/// Adapts a [`Handler`](trait.Handler.html) to the `lambda_runtime::run` interface
103-
pub fn handler<'a, H: Handler<'a>>(handler: H) -> Adapter<'a, H> {
104-
Adapter {
105-
handler,
106-
_phantom_data: PhantomData,
107-
}
108-
}
109-
110-
/// An implementation of `Handler` for a given closure return a `Future` representing the computed response
111-
impl<'a, F, R, Fut> Handler<'a> for F
96+
/// Adapts a [`Service`] into another [`Service`].
97+
pub fn handler<'a, R, Fut>(
98+
f: impl Fn(Request, Context) -> Fut,
99+
) -> Adapter<'a, R, ServiceFn<impl Fn(LambdaEvent<Request>) -> Fut>>
112100
where
113-
F: Fn(Request, Context) -> Fut,
114101
R: IntoResponse,
115-
Fut: Future<Output = Result<R, Error>> + 'a,
102+
Fut: Future<Output = Result<R, Error>>,
116103
{
117-
type Response = R;
118-
type Error = Error;
119-
type Fut = Fut;
120-
fn call(&mut self, event: Request, context: Context) -> Self::Fut {
121-
(self)(event, context)
104+
Adapter {
105+
service: service_fn(handler_wrapper(f)),
106+
_phantom_data: PhantomData,
122107
}
123108
}
124109

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

131116
impl<'a, R, E> Future for TransformResponse<'a, R, E>
132117
where
133118
R: IntoResponse,
134119
{
135120
type Output = Result<LambdaResponse, E>;
121+
136122
fn poll(mut self: Pin<&mut Self>, cx: &mut TaskContext) -> Poll<Self::Output> {
137123
match self.fut.as_mut().poll(cx) {
138124
Poll::Ready(result) => Poll::Ready(
@@ -143,39 +129,29 @@ where
143129
}
144130
}
145131

146-
/// Exists only to satisfy the trait cover rule for `lambda_runtime::Handler` impl
147-
///
148-
/// User code should never need to interact with this type directly. Since `Adapter` implements `Handler`
149-
/// It serves as a opaque trait covering type.
150-
///
151-
/// See [this article](http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/)
152-
/// for a larger explanation of why this is necessary
153-
pub struct Adapter<'a, H: Handler<'a>> {
154-
handler: H,
155-
_phantom_data: PhantomData<&'a H>,
156-
}
157-
158-
impl<'a, H: Handler<'a>> Handler<'a> for Adapter<'a, H> {
159-
type Response = H::Response;
160-
type Error = H::Error;
161-
type Fut = H::Fut;
162-
fn call(&mut self, event: Request, context: Context) -> Self::Fut {
163-
self.handler.call(event, context)
164-
}
132+
#[doc(hidden)]
133+
pub struct Adapter<'a, R, S> {
134+
service: S,
135+
_phantom_data: PhantomData<&'a R>,
165136
}
166137

167-
impl<'a, 'b, H: Handler<'a>> Service<LambdaEvent<LambdaRequest<'b>>> for Adapter<'a, H> {
168-
type Error = H::Error;
138+
impl<'a, R, S> Service<LambdaEvent<LambdaRequest<'a>>> for Adapter<'a, R, S>
139+
where
140+
S: Service<LambdaEvent<Request>, Response = R, Error = Error> + Send,
141+
S::Future: Send + 'a,
142+
R: IntoResponse,
143+
{
169144
type Response = LambdaResponse;
170-
type Future = TransformResponse<'a, H::Response, H::Error>;
145+
type Error = Error;
146+
type Future = TransformResponse<'a, R, Self::Error>;
171147

172148
fn poll_ready(&mut self, _cx: &mut core::task::Context<'_>) -> core::task::Poll<Result<(), Self::Error>> {
173149
core::task::Poll::Ready(Ok(()))
174150
}
175151

176-
fn call(&mut self, req: LambdaEvent<LambdaRequest<'_>>) -> Self::Future {
152+
fn call(&mut self, req: LambdaEvent<LambdaRequest<'a>>) -> Self::Future {
177153
let request_origin = req.event.request_origin();
178-
let fut = Box::pin(self.handler.call(req.event.into(), req.context));
154+
let fut = Box::pin(self.service.call(LambdaEvent::new(req.event.into(), req.context)));
179155
TransformResponse { request_origin, fut }
180156
}
181157
}

lambda-integration-tests/src/bin/http-fn.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ async fn main() -> Result<(), Error> {
2323
.without_time()
2424
.init();
2525

26-
lambda_runtime::run(lambda_http::handler(handler)).await
26+
let handler = lambda_http::handler(handler);
27+
lambda_runtime::run(handler).await
2728
}

0 commit comments

Comments
 (0)