oxihttp_server/
extractor.rs1use std::collections::HashMap;
7
8use http::{HeaderMap, Method, Uri};
9use oxihttp_core::{Header, OxiHttpError};
10
11pub struct RequestParts<'a> {
15 pub method: &'a Method,
17 pub uri: &'a Uri,
19 pub headers: &'a HeaderMap,
21 pub path_params: &'a HashMap<String, String>,
23}
24
25pub trait FromRequestParts: Sized {
30 type Rejection: Into<OxiHttpError>;
32
33 fn from_request_parts(parts: &RequestParts<'_>) -> Result<Self, Self::Rejection>;
35}
36
37pub struct TypedHeader<H: Header>(pub H);
52
53impl<H: Header> FromRequestParts for TypedHeader<H> {
54 type Rejection = OxiHttpError;
55
56 fn from_request_parts(parts: &RequestParts<'_>) -> Result<Self, Self::Rejection> {
57 H::decode(parts.headers).map(TypedHeader)
58 }
59}
60
61#[cfg(test)]
66mod tests {
67 use super::*;
68 use http::HeaderValue;
69 use oxihttp_core::{Authorization, ContentType, Host};
70
71 fn make_parts<'a>(
72 method: &'a Method,
73 uri: &'a Uri,
74 headers: &'a HeaderMap,
75 path_params: &'a HashMap<String, String>,
76 ) -> RequestParts<'a> {
77 RequestParts {
78 method,
79 uri,
80 headers,
81 path_params,
82 }
83 }
84
85 #[test]
86 fn test_typed_header_extraction_via_request_parts() {
87 let method = Method::POST;
88 let uri: Uri = "/upload".parse().expect("parse uri");
89 let mut headers = HeaderMap::new();
90 headers.insert(
91 http::header::CONTENT_TYPE,
92 HeaderValue::from_static("application/json"),
93 );
94 let path_params = HashMap::new();
95 let parts = make_parts(&method, &uri, &headers, &path_params);
96
97 let TypedHeader(ct) =
98 TypedHeader::<ContentType>::from_request_parts(&parts).expect("should extract");
99 assert_eq!(ct, ContentType::Json);
100 }
101
102 #[test]
103 fn test_typed_header_missing_returns_error() {
104 let method = Method::POST;
105 let uri: Uri = "/upload".parse().expect("parse uri");
106 let headers = HeaderMap::new();
107 let path_params = HashMap::new();
108 let parts = make_parts(&method, &uri, &headers, &path_params);
109
110 let result = TypedHeader::<ContentType>::from_request_parts(&parts);
111 assert!(result.is_err(), "should fail when Content-Type is absent");
112 }
113
114 #[test]
115 fn test_typed_header_host_ok() {
116 let method = Method::GET;
117 let uri: Uri = "/".parse().expect("parse uri");
118 let mut headers = HeaderMap::new();
119 headers.insert(http::header::HOST, HeaderValue::from_static("example.com"));
120 let path_params = HashMap::new();
121 let parts = make_parts(&method, &uri, &headers, &path_params);
122
123 let TypedHeader(host) =
124 TypedHeader::<Host>::from_request_parts(&parts).expect("should extract");
125 assert_eq!(host, Host("example.com".to_string()));
126 }
127
128 #[test]
129 fn test_typed_header_authorization_ok() {
130 let method = Method::GET;
131 let uri: Uri = "/secure".parse().expect("parse uri");
132 let mut headers = HeaderMap::new();
133 headers.insert(
134 http::header::AUTHORIZATION,
135 HeaderValue::from_static("Bearer secret-token"),
136 );
137 let path_params = HashMap::new();
138 let parts = make_parts(&method, &uri, &headers, &path_params);
139
140 let TypedHeader(auth) =
141 TypedHeader::<Authorization>::from_request_parts(&parts).expect("should extract");
142 assert_eq!(auth, Authorization("Bearer secret-token".to_string()));
143 }
144}