1use crate::error::Result;
7use bytes::Bytes;
8use http::{HeaderMap, Method};
9use std::sync::Arc;
10
11pub type RequestInterceptor = Arc<dyn Fn(&mut RequestData) + Send + Sync>;
13
14pub type ResponseInterceptor = Arc<dyn Fn(&ResponseData) + Send + Sync>;
16
17#[derive(Debug)]
19pub struct RequestData {
20 pub method: Method,
22 pub url: String,
24 pub headers: HeaderMap,
26 pub body: Option<Bytes>,
28}
29
30impl RequestData {
31 pub fn new(method: Method, url: String, headers: HeaderMap, body: Option<Bytes>) -> Self {
33 Self {
34 method,
35 url,
36 headers,
37 body,
38 }
39 }
40
41 pub fn set_header(&mut self, name: &str, value: &str) -> Result<()> {
43 use http::header::{HeaderName, HeaderValue};
44 let name = HeaderName::from_bytes(name.as_bytes())
45 .map_err(|_| crate::error::Error::InvalidHeader {
46 name: name.to_string(),
47 value: value.to_string(),
48 })?;
49 let value = HeaderValue::from_str(value)
50 .map_err(|_| crate::error::Error::InvalidHeader {
51 name: name.to_string(),
52 value: value.to_string(),
53 })?;
54 self.headers.insert(name, value);
55 Ok(())
56 }
57
58 pub fn remove_header(&mut self, name: &str) {
60 if let Ok(name) = http::HeaderName::from_bytes(name.as_bytes()) {
61 self.headers.remove(&name);
62 }
63 }
64
65 pub fn get_header(&self, name: &str) -> Option<&str> {
67 let name = http::HeaderName::from_bytes(name.as_bytes()).ok()?;
68 self.headers.get(&name)?.to_str().ok()
69 }
70}
71
72#[derive(Debug)]
74pub struct ResponseData {
75 pub status: u16,
77 pub headers: HeaderMap,
79 pub body_size: usize,
81 pub duration_ms: u64,
83}
84
85impl ResponseData {
86 pub fn new(status: u16, headers: HeaderMap, body_size: usize, duration_ms: u64) -> Self {
88 Self {
89 status,
90 headers,
91 body_size,
92 duration_ms,
93 }
94 }
95
96 pub fn get_header(&self, name: &str) -> Option<&str> {
98 let name = http::HeaderName::from_bytes(name.as_bytes()).ok()?;
99 self.headers.get(&name)?.to_str().ok()
100 }
101
102 pub fn is_success(&self) -> bool {
104 self.status >= 200 && self.status < 300
105 }
106
107 pub fn is_redirect(&self) -> bool {
109 self.status >= 300 && self.status < 400
110 }
111
112 pub fn is_client_error(&self) -> bool {
114 self.status >= 400 && self.status < 500
115 }
116
117 pub fn is_server_error(&self) -> bool {
119 self.status >= 500 && self.status < 600
120 }
121}
122
123#[derive(Clone, Default)]
125pub struct Interceptors {
126 pub(crate) request: Vec<RequestInterceptor>,
128 pub(crate) response: Vec<ResponseInterceptor>,
130}
131
132impl Interceptors {
133 pub fn new() -> Self {
135 Self {
136 request: Vec::new(),
137 response: Vec::new(),
138 }
139 }
140
141 pub fn add_request<F>(&mut self, interceptor: F)
143 where
144 F: Fn(&mut RequestData) + Send + Sync + 'static,
145 {
146 self.request.push(Arc::new(interceptor));
147 }
148
149 pub fn add_response<F>(&mut self, interceptor: F)
151 where
152 F: Fn(&ResponseData) + Send + Sync + 'static,
153 {
154 self.response.push(Arc::new(interceptor));
155 }
156
157 pub(crate) fn apply_request(&self, data: &mut RequestData) {
159 for interceptor in &self.request {
160 interceptor(data);
161 }
162 }
163
164 pub(crate) fn apply_response(&self, data: &ResponseData) {
166 for interceptor in &self.response {
167 interceptor(data);
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_request_data_set_header() {
178 let mut data = RequestData::new(
179 Method::GET,
180 "https://example.com".to_string(),
181 HeaderMap::new(),
182 None,
183 );
184
185 data.set_header("User-Agent", "avx-http/0.2.0").unwrap();
186 assert_eq!(data.get_header("User-Agent"), Some("avx-http/0.2.0"));
187 }
188
189 #[test]
190 fn test_request_data_remove_header() {
191 let mut headers = HeaderMap::new();
192 headers.insert(
193 http::header::USER_AGENT,
194 http::HeaderValue::from_static("test"),
195 );
196
197 let mut data = RequestData::new(
198 Method::GET,
199 "https://example.com".to_string(),
200 headers,
201 None,
202 );
203
204 assert!(data.get_header("User-Agent").is_some());
205 data.remove_header("User-Agent");
206 assert!(data.get_header("User-Agent").is_none());
207 }
208
209 #[test]
210 fn test_response_data_status_checks() {
211 let data = ResponseData::new(200, HeaderMap::new(), 0, 0);
212 assert!(data.is_success());
213 assert!(!data.is_client_error());
214
215 let data = ResponseData::new(404, HeaderMap::new(), 0, 0);
216 assert!(data.is_client_error());
217 assert!(!data.is_success());
218
219 let data = ResponseData::new(500, HeaderMap::new(), 0, 0);
220 assert!(data.is_server_error());
221 assert!(!data.is_success());
222 }
223
224 #[test]
225 fn test_interceptors_add() {
226 let mut interceptors = Interceptors::new();
227
228 interceptors.add_request(|data| {
229 let _ = data.set_header("X-Test", "value");
230 });
231
232 interceptors.add_response(|data| {
233 println!("Response status: {}", data.status);
234 });
235
236 assert_eq!(interceptors.request.len(), 1);
237 assert_eq!(interceptors.response.len(), 1);
238 }
239
240 #[test]
241 fn test_interceptors_apply_request() {
242 let mut interceptors = Interceptors::new();
243
244 interceptors.add_request(|data| {
245 let _ = data.set_header("X-Custom", "test");
246 });
247
248 let mut data = RequestData::new(
249 Method::GET,
250 "https://example.com".to_string(),
251 HeaderMap::new(),
252 None,
253 );
254
255 interceptors.apply_request(&mut data);
256 assert_eq!(data.get_header("X-Custom"), Some("test"));
257 }
258}