torch_web/extractors/
headers.rs1use std::pin::Pin;
6use std::future::Future;
7use crate::{Request, extractors::{FromRequestParts, ExtractionError}};
8use http::{HeaderMap, HeaderName, HeaderValue};
9
10pub struct Headers(pub HeaderMap);
25
26impl FromRequestParts for Headers {
27 type Error = ExtractionError;
28
29 fn from_request_parts(
30 req: &mut Request,
31 ) -> Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + Send + 'static>> {
32 let headers = req.headers().clone();
33
34 Box::pin(async move {
35 Ok(Headers(headers))
36 })
37 }
38}
39
40pub struct HeaderExtractor {
52 name: String,
53 value: Option<String>,
54}
55
56impl HeaderExtractor {
57 pub fn new(name: &str) -> Self {
58 Self {
59 name: name.to_string(),
60 value: None,
61 }
62 }
63
64 pub fn get(&self) -> Option<&str> {
65 self.value.as_deref()
66 }
67}
68
69pub struct UserAgent(pub Option<String>);
84
85impl FromRequestParts for UserAgent {
86 type Error = ExtractionError;
87
88 fn from_request_parts(
89 req: &mut Request,
90 ) -> Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + Send + 'static>> {
91 let user_agent = req.headers()
92 .get("user-agent")
93 .and_then(|v| v.to_str().ok())
94 .map(|s| s.to_string());
95
96 Box::pin(async move {
97 Ok(UserAgent(user_agent))
98 })
99 }
100}
101
102pub struct Authorization(pub Option<String>);
121
122impl FromRequestParts for Authorization {
123 type Error = ExtractionError;
124
125 fn from_request_parts(
126 req: &mut Request,
127 ) -> Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + Send + 'static>> {
128 let auth = req.headers()
129 .get("authorization")
130 .and_then(|v| v.to_str().ok())
131 .map(|s| s.to_string());
132
133 Box::pin(async move {
134 Ok(Authorization(auth))
135 })
136 }
137}
138
139pub struct ContentType(pub Option<String>);
161
162impl FromRequestParts for ContentType {
163 type Error = ExtractionError;
164
165 fn from_request_parts(
166 req: &mut Request,
167 ) -> Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + Send + 'static>> {
168 let content_type = req.headers()
169 .get("content-type")
170 .and_then(|v| v.to_str().ok())
171 .map(|s| s.to_string());
172
173 Box::pin(async move {
174 Ok(ContentType(content_type))
175 })
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::Request;
183
184 #[tokio::test]
185 async fn test_headers_extraction() {
186 let mut req = Request::new();
187 req.headers_mut().insert("x-custom", "test-value".parse().unwrap());
188
189 let result = Headers::from_request_parts(&mut req).await;
190 assert!(result.is_ok());
191
192 let Headers(headers) = result.unwrap();
193 assert_eq!(headers.get("x-custom").unwrap(), "test-value");
194 }
195
196 #[tokio::test]
197 async fn test_user_agent_extraction() {
198 let mut req = Request::new();
199 req.headers_mut().insert("user-agent", "Mozilla/5.0".parse().unwrap());
200
201 let result = UserAgent::from_request_parts(&mut req).await;
202 assert!(result.is_ok());
203
204 let UserAgent(user_agent) = result.unwrap();
205 assert_eq!(user_agent, Some("Mozilla/5.0".to_string()));
206 }
207
208 #[tokio::test]
209 async fn test_missing_user_agent() {
210 let mut req = Request::new();
211
212 let result = UserAgent::from_request_parts(&mut req).await;
213 assert!(result.is_ok());
214
215 let UserAgent(user_agent) = result.unwrap();
216 assert_eq!(user_agent, None);
217 }
218
219 }