1use crate::request::LambdaHttpEvent;
6use core::convert::TryFrom;
7use core::future::Future;
8use lambda_runtime::{Error as LambdaError, LambdaEvent, Service as LambdaService};
9use std::pin::Pin;
10
11pub async fn run_actix_on_lambda<F, I, S, B>(factory: F) -> Result<(), LambdaError>
39where
40 F: Fn() -> I + Send + Clone + 'static,
41 I: actix_service::IntoServiceFactory<S, actix_http::Request>,
42 S: actix_service::ServiceFactory<
43 actix_http::Request,
44 Config = actix_web::dev::AppConfig,
45 Response = actix_web::dev::ServiceResponse<B>,
46 Error = actix_web::Error,
47 > + 'static,
48 S::InitError: std::fmt::Debug,
49 B: actix_web::body::MessageBody,
50 B::Error: std::fmt::Display,
51 <B as actix_web::body::MessageBody>::Error: std::fmt::Debug,
52{
53 let srv = factory().into_factory();
55 let new_svc = srv
56 .new_service(actix_web::dev::AppConfig::default())
57 .await
58 .unwrap();
59
60 lambda_runtime::run(ActixHandler(new_svc)).await?;
61
62 Ok(())
63}
64
65struct ActixHandler<S, B>(S)
67where
68 S: actix_service::Service<
69 actix_http::Request,
70 Response = actix_web::dev::ServiceResponse<B>,
71 Error = actix_web::Error,
72 > + 'static,
73 B: actix_web::body::MessageBody,
74 B::Error: std::fmt::Display,
75 <B as actix_web::body::MessageBody>::Error: std::fmt::Debug;
76
77impl<S, B> LambdaService<LambdaEvent<LambdaHttpEvent<'_>>> for ActixHandler<S, B>
78where
79 S: actix_service::Service<
80 actix_http::Request,
81 Response = actix_web::dev::ServiceResponse<B>,
82 Error = actix_web::Error,
83 > + 'static,
84 B: actix_web::body::MessageBody,
85 B::Error: std::fmt::Display,
86 <B as actix_web::body::MessageBody>::Error: std::fmt::Debug,
87{
88 type Response = serde_json::Value;
89 type Error = actix_web::Error;
90 type Future = Pin<Box<dyn Future<Output = Result<serde_json::Value, Self::Error>>>>;
91
92 fn poll_ready(
94 &mut self,
95 cx: &mut core::task::Context<'_>,
96 ) -> core::task::Poll<Result<(), Self::Error>> {
97 self.0.poll_ready(cx)
98 }
99
100 fn call(&mut self, req: LambdaEvent<LambdaHttpEvent<'_>>) -> Self::Future {
104 use serde_json::json;
105
106 let event = req.payload;
107 let _context = req.context;
108
109 let client_br = event.client_supports_brotli();
111 let multi_value = event.multi_value();
113
114 let actix_request = actix_http::Request::try_from(event);
116
117 let svc_call = actix_request.map(|req| self.0.call(req));
119
120 let fut = async move {
121 match svc_call {
122 Ok(svc_fut) => {
123 if let Ok(response) = svc_fut.await {
125 api_gateway_response_from_actix_web(response, client_br, multi_value)
127 .await
128 .or_else(|_err| {
129 Ok(json!({
130 "isBase64Encoded": false,
131 "statusCode": 500u16,
132 "headers": { "content-type": "text/plain"},
133 "body": "Internal Server Error"
134 }))
135 })
136 } else {
137 Ok(json!({
139 "isBase64Encoded": false,
140 "statusCode": 500u16,
141 "headers": { "content-type": "text/plain"},
142 "body": "Internal Server Error"
143 }))
144 }
145 }
146 Err(_request_err) => {
147 Ok(json!({
149 "isBase64Encoded": false,
150 "statusCode": 400u16,
151 "headers": { "content-type": "text/plain"},
152 "body": "Bad Request"
153 }))
154 }
155 }
156 };
157 Box::pin(fut)
158 }
159}
160
161impl TryFrom<LambdaHttpEvent<'_>> for actix_http::Request {
162 type Error = LambdaError;
163
164 fn try_from(event: LambdaHttpEvent) -> Result<Self, Self::Error> {
166 use actix_web::http::Method;
167
168 let method = Method::try_from(event.method())?;
170 let req = actix_web::test::TestRequest::with_uri(&event.path_query()).method(method);
171
172 let req = if let Some(source_ip) = event.source_ip() {
174 req.peer_addr(std::net::SocketAddr::from((source_ip, 0u16)))
175 } else {
176 req
177 };
178
179 let req = event
181 .headers()
182 .into_iter()
183 .fold(req, |req, (k, v)| req.insert_header((k, &v as &str)));
184
185 let req = req.set_payload(event.body()?);
187
188 Ok(req.to_request())
189 }
190}
191
192impl<B> crate::brotli::ResponseCompression for actix_web::dev::ServiceResponse<B> {
193 fn content_encoding<'a>(&'a self) -> Option<&'a str> {
195 self.headers()
196 .get(actix_web::http::header::CONTENT_ENCODING)
197 .and_then(|val| val.to_str().ok())
198 }
199
200 fn content_type<'a>(&'a self) -> Option<&'a str> {
202 self.headers()
203 .get(actix_web::http::header::CONTENT_TYPE)
204 .and_then(|val| val.to_str().ok())
205 }
206}
207
208async fn api_gateway_response_from_actix_web<B: actix_web::body::MessageBody>(
210 response: actix_web::dev::ServiceResponse<B>,
211 client_support_br: bool,
212 multi_value: bool,
213) -> Result<serde_json::Value, B::Error> {
214 use crate::brotli::ResponseCompression;
215 use actix_web::http::header::SET_COOKIE;
216 use serde_json::json;
217
218 let status_code = response.status().as_u16();
220
221 let mut cookies = Vec::<String>::new();
223 let mut headers = serde_json::Map::new();
224 for (k, v) in response.headers() {
225 if let Ok(value_str) = v.to_str() {
226 if multi_value {
227 if let Some(values) = headers.get_mut(k.as_str()) {
229 if let Some(value_ary) = values.as_array_mut() {
230 value_ary.push(json!(value_str));
231 }
232 } else {
233 headers.insert(k.as_str().to_string(), json!([value_str]));
234 }
235 } else {
236 if k == SET_COOKIE {
238 cookies.push(value_str.to_string());
239 } else {
240 headers.insert(k.as_str().to_string(), json!(value_str));
241 }
242 }
243 }
244 }
245
246 let compress = client_support_br && response.can_brotli_compress();
248 let body_bytes = actix_web::body::to_bytes(response.into_body()).await?;
249 let body_base64 = if compress {
250 if multi_value {
251 headers.insert("content-encoding".to_string(), json!(["br"]));
252 } else {
253 headers.insert("content-encoding".to_string(), json!("br"));
254 }
255 crate::brotli::compress_response_body(&body_bytes)
256 } else {
257 base64::encode(body_bytes)
258 };
259
260 if multi_value {
261 Ok(json!({
262 "isBase64Encoded": true,
263 "statusCode": status_code,
264 "multiValueHeaders": headers,
265 "body": body_base64
266 }))
267 } else {
268 Ok(json!({
269 "isBase64Encoded": true,
270 "statusCode": status_code,
271 "cookies": cookies,
272 "headers": headers,
273 "body": body_base64
274 }))
275 }
276}
277
278#[cfg(test)]
279mod tests {
280 use super::*;
281 use crate::{request::LambdaHttpEvent, test_consts::*};
282
283 fn prepare_request(event_str: &str) -> actix_http::Request {
285 let reqjson: LambdaHttpEvent = serde_json::from_str(event_str).unwrap();
286 actix_http::Request::try_from(reqjson).unwrap()
287 }
288
289 #[test]
290 fn test_path_decode() {
291 let req = prepare_request(API_GATEWAY_V2_GET_ROOT_NOQUERY);
292 assert_eq!(req.uri().path(), "/");
293 let req = prepare_request(API_GATEWAY_REST_GET_ROOT_NOQUERY);
294 assert_eq!(req.uri().path(), "/stage/");
295
296 let req = prepare_request(API_GATEWAY_V2_GET_SOMEWHERE_NOQUERY);
297 assert_eq!(req.uri().path(), "/somewhere");
298 let req = prepare_request(API_GATEWAY_REST_GET_SOMEWHERE_NOQUERY);
299 assert_eq!(req.uri().path(), "/stage/somewhere");
300
301 let req = prepare_request(API_GATEWAY_V2_GET_SPACEPATH_NOQUERY);
302 assert_eq!(req.uri().path(), "/path%20with/space");
303 let req = prepare_request(API_GATEWAY_REST_GET_SPACEPATH_NOQUERY);
304 assert_eq!(req.uri().path(), "/stage/path%20with/space");
305
306 let req = prepare_request(API_GATEWAY_V2_GET_PERCENTPATH_NOQUERY);
307 assert_eq!(req.uri().path(), "/path%25with/percent");
308 let req = prepare_request(API_GATEWAY_REST_GET_PERCENTPATH_NOQUERY);
309 assert_eq!(req.uri().path(), "/stage/path%25with/percent");
310
311 let req = prepare_request(API_GATEWAY_V2_GET_UTF8PATH_NOQUERY);
312 assert_eq!(
313 req.uri().path(),
314 "/%E6%97%A5%E6%9C%AC%E8%AA%9E/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D"
315 );
316 let req = prepare_request(API_GATEWAY_REST_GET_UTF8PATH_NOQUERY);
317 assert_eq!(
318 req.uri().path(),
319 "/stage/%E6%97%A5%E6%9C%AC%E8%AA%9E/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D"
320 );
321 }
322
323 #[test]
324 fn test_query_decode() {
325 let req = prepare_request(API_GATEWAY_V2_GET_ROOT_ONEQUERY);
326 assert_eq!(req.uri().query(), Some("key=value"));
327 let req = prepare_request(API_GATEWAY_REST_GET_ROOT_ONEQUERY);
328 assert_eq!(req.uri().query(), Some("key=value"));
329
330 let req = prepare_request(API_GATEWAY_V2_GET_SOMEWHERE_ONEQUERY);
331 assert_eq!(req.uri().query(), Some("key=value"));
332 let req = prepare_request(API_GATEWAY_REST_GET_SOMEWHERE_ONEQUERY);
333 assert_eq!(req.uri().query(), Some("key=value"));
334
335 let req = prepare_request(API_GATEWAY_V2_GET_SOMEWHERE_TWOQUERY);
336 assert_eq!(req.uri().query(), Some("key1=value1&key2=value2"));
337 let req = prepare_request(API_GATEWAY_REST_GET_SOMEWHERE_TWOQUERY);
338 assert!(
339 req.uri().query() == Some("key1=value1&key2=value2")
340 || req.uri().query() == Some("key2=value2&key1=value1")
341 );
342
343 let req = prepare_request(API_GATEWAY_V2_GET_SOMEWHERE_SPACEQUERY);
344 assert_eq!(req.uri().query(), Some("key=value1+value2"));
345 let req = prepare_request(API_GATEWAY_REST_GET_SOMEWHERE_SPACEQUERY);
346 assert_eq!(req.uri().query(), Some("key=value1%20value2"));
347
348 let req = prepare_request(API_GATEWAY_V2_GET_SOMEWHERE_UTF8QUERY);
349 assert_eq!(req.uri().query(), Some("key=%E6%97%A5%E6%9C%AC%E8%AA%9E"));
350 let req = prepare_request(API_GATEWAY_REST_GET_SOMEWHERE_UTF8QUERY);
351 assert_eq!(req.uri().query(), Some("key=%E6%97%A5%E6%9C%AC%E8%AA%9E"));
352 }
353
354 #[test]
355 fn test_remote_ip_decode() {
356 use std::net::IpAddr;
357 use std::str::FromStr;
358
359 let req = prepare_request(API_GATEWAY_V2_GET_ROOT_ONEQUERY);
360 assert_eq!(
361 req.peer_addr().unwrap().ip(),
362 IpAddr::from_str("1.2.3.4").unwrap()
363 );
364 let req = prepare_request(API_GATEWAY_REST_GET_ROOT_ONEQUERY);
365 assert_eq!(
366 req.peer_addr().unwrap().ip(),
367 IpAddr::from_str("1.2.3.4").unwrap()
368 );
369
370 let req = prepare_request(API_GATEWAY_V2_GET_REMOTE_IPV6);
371 assert_eq!(
372 req.peer_addr().unwrap().ip(),
373 IpAddr::from_str("2404:6800:400a:80c::2004").unwrap()
374 );
375 let req = prepare_request(API_GATEWAY_REST_GET_REMOTE_IPV6);
376 assert_eq!(
377 req.peer_addr().unwrap().ip(),
378 IpAddr::from_str("2404:6800:400a:80c::2004").unwrap()
379 );
380 }
381
382 #[tokio::test]
383 async fn test_form_post() {
384 use actix_web::http::Method;
385
386 let req = prepare_request(API_GATEWAY_V2_POST_FORM_URLENCODED);
387 assert_eq!(req.method(), Method::POST);
388 let req = prepare_request(API_GATEWAY_REST_POST_FORM_URLENCODED);
389 assert_eq!(req.method(), Method::POST);
390
391 let req = prepare_request(API_GATEWAY_V2_POST_FORM_URLENCODED_B64);
393 assert_eq!(req.method(), Method::POST);
394 let req = prepare_request(API_GATEWAY_REST_POST_FORM_URLENCODED_B64);
395 assert_eq!(req.method(), Method::POST);
396 }
397
398 #[test]
399 fn test_parse_header() {
400 let req = prepare_request(API_GATEWAY_V2_GET_ROOT_NOQUERY);
401 assert_eq!(req.head().headers.get("x-forwarded-port").unwrap(), &"443");
402 assert_eq!(
403 req.head().headers.get("x-forwarded-proto").unwrap(),
404 &"https"
405 );
406 let req = prepare_request(API_GATEWAY_REST_GET_ROOT_NOQUERY);
407 assert_eq!(req.head().headers.get("x-forwarded-port").unwrap(), &"443");
408 assert_eq!(
409 req.head().headers.get("x-forwarded-proto").unwrap(),
410 &"https"
411 );
412 }
413
414 #[test]
415 fn test_parse_cookies() {
416 let req = prepare_request(API_GATEWAY_V2_GET_ROOT_NOQUERY);
417 assert_eq!(req.head().headers.get("cookie"), None);
418
419 let req = prepare_request(API_GATEWAY_V2_GET_ONE_COOKIE);
420 assert_eq!(req.head().headers.get("cookie").unwrap(), &"cookie1=value1");
421 let req = prepare_request(API_GATEWAY_REST_GET_ONE_COOKIE);
422 assert_eq!(req.head().headers.get("cookie").unwrap(), &"cookie1=value1");
423
424 let req = prepare_request(API_GATEWAY_V2_GET_TWO_COOKIES);
425 assert!(
426 req.head().headers.get("cookie").unwrap() == &"cookie2=value2; cookie1=value1"
427 || req.head().headers.get("cookie").unwrap() == &"cookie1=value1; cookie2=value2"
428 );
429 let req = prepare_request(API_GATEWAY_REST_GET_TWO_COOKIES);
430 assert!(
431 req.head().headers.get("cookie").unwrap() == &"cookie2=value2; cookie1=value1"
432 || req.head().headers.get("cookie").unwrap() == &"cookie1=value1; cookie2=value2"
433 );
434 }
435}