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
#![forbid(unsafe_code)]
#![deny(
    clippy::dbg_macro,
    missing_copy_implementations,
    rustdoc::missing_crate_level_docs,
    missing_debug_implementations,
    missing_docs,
    nonstandard_style,
    unused_qualifications
)]

/*!
# Trillium server adapter for aws lambda

```rust,no_run
trillium_aws_lambda::run(|conn: trillium::Conn| async move {
    conn.ok("hello lambda")
});
```
*/

use lamedh_runtime::{Context, Handler as AwsHandler};
use std::{future::Future, pin::Pin, sync::Arc};
use tokio::runtime;
use trillium::{Conn, Handler};
use trillium_http::{Conn as HttpConn, Synthetic};

mod context;
pub use context::LambdaConnExt;
use context::LambdaContext;

mod request;
use request::LambdaRequest;

mod response;
use response::{AlbMultiHeadersResponse, AlbResponse, LambdaResponse};

#[derive(Debug)]
struct HandlerWrapper<H>(Arc<H>);

impl<H: Handler> AwsHandler<LambdaRequest, LambdaResponse> for HandlerWrapper<H> {
    type Error = std::io::Error;
    type Fut = Pin<Box<dyn Future<Output = Result<LambdaResponse, Self::Error>> + Send + 'static>>;

    fn call(&mut self, request: LambdaRequest, context: Context) -> Self::Fut {
        Box::pin(handler_fn(request, context, Arc::clone(&self.0)))
    }
}

async fn run_handler(conn: HttpConn<Synthetic>, handler: Arc<impl Handler>) -> Conn {
    let conn = handler.run(conn.into()).await;
    handler.before_send(conn).await
}

async fn handler_fn(
    request: LambdaRequest,
    context: Context,
    handler: Arc<impl Handler>,
) -> std::io::Result<LambdaResponse> {
    match request {
        LambdaRequest::Alb(request) => {
            let mut conn = request.into_conn().await;
            conn.state_mut().insert(LambdaContext::new(context));
            let conn = run_handler(conn, handler).await;
            Ok(LambdaResponse::Alb(AlbResponse::from_conn(conn).await))
        }

        LambdaRequest::AlbMultiHeaders(request) => {
            let mut conn = request.into_conn().await;
            conn.state_mut().insert(LambdaContext::new(context));
            let conn = run_handler(conn, handler).await;
            Ok(LambdaResponse::AlbMultiHeaders(
                AlbMultiHeadersResponse::from_conn(conn).await,
            ))
        }
    }
}
/**
# Runs a trillium handler on an already-running tokio runtime

This function will poll pending until the server shuts down.
*/
pub async fn run_async(mut handler: impl Handler) {
    let mut info = "aws lambda".into();
    handler.init(&mut info).await;
    lamedh_runtime::run(HandlerWrapper(Arc::new(handler)))
        .await
        .unwrap()
}

/**
# Runs a trillium handler in a sync context

This function creates a new tokio runtime and executes the handler on
it for aws lambda.

This function will block the current thread until the server shuts
down
*/

pub fn run(handler: impl Handler) {
    runtime::Builder::new_current_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(run_async(handler));
}