armature_lambda/
request.rs1use bytes::Bytes;
4use lambda_http::Request;
5use std::collections::HashMap;
6
7pub struct LambdaRequest {
9 pub method: http::Method,
11 pub path: String,
13 pub query_string: Option<String>,
15 pub headers: HashMap<String, String>,
17 pub body: Bytes,
19 pub path_parameters: HashMap<String, String>,
21 pub stage_variables: HashMap<String, String>,
23 pub request_context: RequestContext,
25}
26
27#[derive(Debug, Clone, Default)]
29pub struct RequestContext {
30 pub request_id: Option<String>,
32 pub stage: Option<String>,
34 pub domain_name: Option<String>,
36 pub http_method: Option<String>,
38 pub source_ip: Option<String>,
40 pub user_agent: Option<String>,
42 pub authorizer_claims: HashMap<String, String>,
44}
45
46impl LambdaRequest {
47 pub fn from_lambda_request(request: Request) -> Self {
49 let (parts, body) = request.into_parts();
50
51 let mut headers = HashMap::new();
53 for (name, value) in parts.headers.iter() {
54 if let Ok(v) = value.to_str() {
55 headers.insert(name.to_string(), v.to_string());
56 }
57 }
58
59 let query_string = parts.uri.query().map(String::from);
61
62 let (path_parameters, request_context) = parts
64 .extensions
65 .get::<lambda_http::request::RequestContext>()
66 .map(|ctx| {
67 match ctx {
68 lambda_http::request::RequestContext::ApiGatewayV2(v2) => {
69 let params = HashMap::new();
71
72 let ctx = RequestContext {
73 request_id: v2.request_id.clone(),
74 stage: v2.stage.clone(),
75 domain_name: v2.domain_name.clone(),
76 http_method: Some(v2.http.method.to_string()),
77 source_ip: v2.http.source_ip.clone(),
78 user_agent: v2.http.user_agent.clone(),
79 authorizer_claims: HashMap::new(),
80 };
81 (params, ctx)
82 }
83 lambda_http::request::RequestContext::ApiGatewayV1(v1) => {
84 let params = HashMap::new();
85 let ctx = RequestContext {
86 request_id: v1.request_id.clone(),
87 stage: v1.stage.clone(),
88 domain_name: v1.domain_name.clone(),
89 http_method: Some(v1.http_method.to_string()),
90 source_ip: v1.identity.source_ip.clone(),
91 user_agent: v1.identity.user_agent.clone(),
92 authorizer_claims: extract_claims_v1(v1),
93 };
94 (params, ctx)
95 }
96 lambda_http::request::RequestContext::Alb(_) => {
97 (HashMap::new(), RequestContext::default())
98 }
99 _ => (HashMap::new(), RequestContext::default()),
100 }
101 })
102 .unwrap_or_default();
103
104 let body_bytes = match body {
106 lambda_http::Body::Empty => Bytes::new(),
107 lambda_http::Body::Text(s) => Bytes::from(s),
108 lambda_http::Body::Binary(b) => Bytes::from(b),
109 };
110
111 Self {
112 method: parts.method,
113 path: parts.uri.path().to_string(),
114 query_string,
115 headers,
116 body: body_bytes,
117 path_parameters,
118 stage_variables: HashMap::new(),
119 request_context,
120 }
121 }
122
123 pub fn header(&self, name: &str) -> Option<&str> {
125 self.headers
126 .get(&name.to_lowercase())
127 .or_else(|| self.headers.get(name))
128 .map(|s| s.as_str())
129 }
130
131 pub fn content_type(&self) -> Option<&str> {
133 self.header("content-type")
134 }
135
136 pub fn is_json(&self) -> bool {
138 self.content_type()
139 .map(|ct| ct.contains("application/json"))
140 .unwrap_or(false)
141 }
142
143 pub fn source_ip(&self) -> Option<&str> {
145 self.request_context.source_ip.as_deref()
146 }
147
148 pub fn claims(&self) -> &HashMap<String, String> {
150 &self.request_context.authorizer_claims
151 }
152
153 pub fn claim(&self, key: &str) -> Option<&str> {
155 self.request_context
156 .authorizer_claims
157 .get(key)
158 .map(|s| s.as_str())
159 }
160}
161
162fn extract_claims_v1(
164 _v1: &lambda_http::aws_lambda_events::apigw::ApiGatewayProxyRequestContext,
165) -> HashMap<String, String> {
166 HashMap::new()
169}