1#[cfg(feature = "lambda")]
2pub use vercel_runtime;
3#[cfg(feature = "lambda")]
4pub use tokio;
5
6pub use http;
7use http::{Request, Response};
8use std::sync::Arc;
9use std::future::Future;
10use std::pin::Pin;
11
12pub type Bytes = Vec<u8>;
13pub type PortableRequest = Request<Bytes>;
14pub type PortableResponse = Response<Bytes>;
15
16pub trait Handler: Send + Sync + 'static {
17 fn handle(&self, req: PortableRequest) -> Pin<Box<dyn Future<Output = PortableResponse> + Send + '_>>;
18}
19
20pub struct Server {
21 pub routes: Vec<(String, Arc<dyn Handler>)>,
22}
23
24impl Default for Server {
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl Server {
31 pub fn new() -> Self {
32 Self { routes: Vec::new() }
33 }
34
35 pub fn route<H: Handler>(&mut self, path: &str, handler: H) {
36 self.routes.push((path.to_string(), Arc::new(handler)));
37 }
38}
39
40#[cfg(feature = "actix")]
41pub mod actix_adapter {
42 use super::{Handler, Server};
43 use actix_web::{web, HttpRequest, HttpResponse};
44 use std::sync::Arc;
45
46
47 async fn actix_handler_wrapper(
48 req: HttpRequest,
49 body: web::Bytes,
50 handler: web::Data<Arc<dyn Handler>>,
51 ) -> HttpResponse {
52 let method = http::Method::try_from(req.method().as_str()).unwrap();
53
54 let mut builder = http::Request::builder()
55 .method(method)
56 .uri(req.uri().to_string());
57
58 for (k, v) in req.headers() {
59 builder = builder.header(k.as_str(), v.as_bytes());
60 }
61
62 let portable_req = builder.body(body.to_vec()).unwrap();
63 let portable_res = handler.handle(portable_req).await;
64
65 let mut res_builder = HttpResponse::build(
66 actix_web::http::StatusCode::from_u16(portable_res.status().as_u16()).unwrap(),
67 );
68
69 for (k, v) in portable_res.headers() {
70 res_builder.append_header((k.as_str(), v.as_bytes()));
71 }
72
73 res_builder.body(portable_res.into_body())
74 }
75
76 pub fn configure(server: &Server, cfg: &mut actix_web::web::ServiceConfig) {
77 for (path, handler) in &server.routes {
78 let handler_data = web::Data::new(Arc::clone(handler));
79 cfg.service(
80 web::resource(path)
81 .app_data(handler_data.clone())
82 .route(web::post().to(actix_handler_wrapper))
83 );
84 }
85 }
86}
87
88#[cfg(feature = "lambda")]
89pub mod lambda_adapter {
90 use super::Server;
91 use vercel_runtime::{run as vercel_run, AppState, Error, ResponseBody};
92 use std::future::Future;
93 use std::pin::Pin;
94 use std::sync::Arc;
95 use std::task::{Context, Poll};
96
97 type HyperRequest = hyper::Request<hyper::body::Incoming>;
98 type HyperResponse = hyper::Response<ResponseBody>;
99
100 struct ThreadloomService {
101 server: Arc<Server>,
102 }
103
104 impl Clone for ThreadloomService {
105 fn clone(&self) -> Self {
106 Self { server: Arc::clone(&self.server) }
107 }
108 }
109
110 impl tower::Service<(AppState, HyperRequest)> for ThreadloomService {
111 type Response = HyperResponse;
112 type Error = Error;
113 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
114
115 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
116 Poll::Ready(Ok(()))
117 }
118
119 fn call(&mut self, (_, req): (AppState, HyperRequest)) -> Self::Future {
120 let server = Arc::clone(&self.server);
121 Box::pin(async move {
122 use http_body_util::BodyExt;
123
124 let path = if let Some(route) = req.headers().get("x-threadloom-route") {
125 route.to_str().unwrap_or(req.uri().path()).to_string()
126 } else {
127 req.uri().path().to_string()
128 };
129
130 let (parts, body) = req.into_parts();
131 let bytes = body.collect().await
132 .map(|c| c.to_bytes().to_vec())
133 .unwrap_or_default();
134
135 let found_handler = server.routes.iter()
136 .find(|(route_path, _)| route_path == &path)
137 .map(|(_, h)| Arc::clone(h));
138
139 if let Some(h) = found_handler {
140 let portable_req = http::Request::from_parts(parts, bytes);
141 let res = h.handle(portable_req).await;
142 let (res_parts, res_bytes) = res.into_parts();
143 Ok(hyper::Response::from_parts(res_parts, ResponseBody::from(res_bytes)))
144 } else {
145 Ok(hyper::Response::builder()
146 .status(404)
147 .body(ResponseBody::from("Not Found"))
148 .unwrap())
149 }
150 })
151 }
152 }
153
154 pub async fn run(server: Server) -> Result<(), Error> {
155 let service = ThreadloomService { server: Arc::new(server) };
156 vercel_run(service).await
157 }
158}
159
160
161#[cfg(not(feature = "lambda"))]
162pub mod lambda_http {
163 #[derive(Debug)]
164 pub struct Error;
165}
166
167#[cfg(not(feature = "lambda"))]
168pub mod tokio {
169 pub mod runtime {
170 pub struct Builder;
171 impl Builder {
172 pub fn new_current_thread() -> Self { Self }
173 pub fn enable_all(&mut self) -> &mut Self { self }
174 pub fn build(&mut self) -> Result<Runtime, ()> { Ok(Runtime) }
175 }
176 pub struct Runtime;
177 impl Runtime {
178 pub fn block_on<F>(&self, _f: F) -> Result<(), crate::lambda_http::Error> { Ok(()) }
179 }
180 }
181}
182
183#[cfg(not(feature = "lambda"))]
184pub mod lambda_adapter {
185 use super::Server;
186 pub async fn run(_server: Server) -> Result<(), Box<dyn std::error::Error>> {
187 Ok(())
188 }
189}