1use std::cell::RefCell;
19use std::collections::HashMap;
20use std::rc::Rc;
21
22use serde_json::Value;
23
24use super::request::Request;
25
26#[derive(Debug, Clone, Default)]
28pub struct SecurityDetails {
29 pub protocol: String,
30 pub subject_name: String,
31 pub issuer: String,
32 pub valid_from: f64,
33 pub valid_to: f64,
34}
35
36#[derive(Debug, Clone, Default)]
38pub struct RemoteAddress {
39 pub ip: String,
40 pub port: u16,
41}
42
43pub struct Response {
47 url: RefCell<String>,
48 status: RefCell<Option<u16>>,
49 status_text: RefCell<String>,
50 headers: RefCell<HashMap<String, String>>,
51 from_cache: RefCell<bool>,
52 from_service_worker: RefCell<bool>,
53 security_details: RefCell<Option<SecurityDetails>>,
54 remote_address: RefCell<Option<RemoteAddress>>,
55 request: RefCell<Option<Rc<Request>>>,
56 body: RefCell<Option<Vec<u8>>>,
57 body_text: RefCell<Option<String>>,
58 body_json: RefCell<Option<Value>>,
59}
60
61impl std::fmt::Debug for Response {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.debug_struct("Response")
64 .field("url", &self.url.borrow())
65 .field("status", &self.status.borrow())
66 .field("from_cache", &self.from_cache.borrow())
67 .field("from_service_worker", &self.from_service_worker.borrow())
68 .finish()
69 }
70}
71
72impl Response {
73 pub fn new() -> Self {
77 Self {
78 url: RefCell::new(String::new()),
79 status: RefCell::new(None),
80 status_text: RefCell::new(String::new()),
81 headers: RefCell::new(HashMap::new()),
82 from_cache: RefCell::new(false),
83 from_service_worker: RefCell::new(false),
84 security_details: RefCell::new(None),
85 remote_address: RefCell::new(None),
86 request: RefCell::new(None),
87 body: RefCell::new(None),
88 body_text: RefCell::new(None),
89 body_json: RefCell::new(None),
90 }
91 }
92
93 pub fn url(&self) -> String {
97 self.url.borrow().clone()
98 }
99
100 pub fn set_url(&self, url: impl Into<String>) {
104 *self.url.borrow_mut() = url.into();
105 }
106
107 pub fn status(&self) -> Option<u16> {
111 *self.status.borrow()
112 }
113
114 pub fn set_status(&self, s: u16) {
118 *self.status.borrow_mut() = Some(s);
119 }
120
121 pub fn status_text(&self) -> String {
125 self.status_text.borrow().clone()
126 }
127
128 pub fn set_status_text(&self, t: impl Into<String>) {
132 *self.status_text.borrow_mut() = t.into();
133 }
134
135 pub fn ok(&self) -> bool {
139 matches!(self.status.borrow().as_ref(), Some(s) if (200..300).contains(s))
140 }
141
142 pub fn headers(&self) -> HashMap<String, String> {
146 self.headers.borrow().clone()
147 }
148
149 pub fn set_headers(&self, h: HashMap<String, String>) {
153 *self.headers.borrow_mut() = h;
154 }
155
156 pub fn add_header(&self, name: impl Into<String>, value: impl Into<String>) {
160 self.headers.borrow_mut().insert(name.into(), value.into());
161 }
162
163 pub fn from_cache(&self) -> bool {
167 *self.from_cache.borrow()
168 }
169
170 pub fn set_from_cache(&self, v: bool) {
174 *self.from_cache.borrow_mut() = v;
175 }
176
177 pub fn from_service_worker(&self) -> bool {
181 *self.from_service_worker.borrow()
182 }
183
184 pub fn set_from_service_worker(&self, v: bool) {
188 *self.from_service_worker.borrow_mut() = v;
189 }
190
191 pub fn security_details(&self) -> Option<SecurityDetails> {
195 self.security_details.borrow().clone()
196 }
197
198 pub fn set_security_details(&self, s: SecurityDetails) {
202 *self.security_details.borrow_mut() = Some(s);
203 }
204
205 pub fn remote_address(&self) -> Option<RemoteAddress> {
209 self.remote_address.borrow().clone()
210 }
211
212 pub fn set_remote_address(&self, a: RemoteAddress) {
216 *self.remote_address.borrow_mut() = Some(a);
217 }
218
219 pub fn request(&self) -> Option<Rc<Request>> {
223 self.request.borrow().clone()
224 }
225
226 pub fn set_request(&self, r: Rc<Request>) {
230 *self.request.borrow_mut() = Some(r);
231 }
232
233 pub fn body(&self) -> Option<Vec<u8>> {
237 self.body.borrow().clone()
238 }
239
240 pub fn set_body(&self, b: Vec<u8>) {
244 *self.body.borrow_mut() = Some(b);
245 }
246
247 pub fn body_text(&self) -> Option<String> {
251 self.body_text.borrow().clone()
252 }
253
254 pub fn set_body_text(&self, t: impl Into<String>) {
258 *self.body_text.borrow_mut() = Some(t.into());
259 }
260
261 pub fn body_json(&self) -> Option<Value> {
265 self.body_json.borrow().clone()
266 }
267
268 pub fn set_body_json(&self, v: Value) {
272 *self.body_json.borrow_mut() = Some(v);
273 }
274}
275
276impl Default for Response {
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn url_round_trip() {
288 let r = Response::new();
289 assert_eq!(r.url(), "");
290 r.set_url("https://example.com");
291 assert_eq!(r.url(), "https://example.com");
292 }
293
294 #[test]
295 fn status_and_ok() {
296 let r = Response::new();
297 assert!(r.status().is_none());
298 assert!(!r.ok());
299 r.set_status(200);
300 assert_eq!(r.status(), Some(200));
301 assert!(r.ok());
302 r.set_status(404);
303 assert!(!r.ok());
304 r.set_status(299);
305 assert!(r.ok());
306 r.set_status(300);
307 assert!(!r.ok());
308 }
309
310 #[test]
311 fn status_text_round_trip() {
312 let r = Response::new();
313 assert_eq!(r.status_text(), "");
314 r.set_status_text("Not Found");
315 assert_eq!(r.status_text(), "Not Found");
316 }
317
318 #[test]
319 fn headers_round_trip() {
320 let r = Response::new();
321 assert_eq!(r.headers().len(), 0);
322 r.add_header("content-type", "text/html");
323 assert_eq!(r.headers().len(), 1);
324 assert_eq!(
325 r.headers().get("content-type"),
326 Some(&"text/html".to_string())
327 );
328 }
329
330 #[test]
331 fn from_cache_default_false() {
332 let r = Response::new();
333 assert!(!r.from_cache());
334 r.set_from_cache(true);
335 assert!(r.from_cache());
336 }
337
338 #[test]
339 fn from_service_worker_default_false() {
340 let r = Response::new();
341 assert!(!r.from_service_worker());
342 r.set_from_service_worker(true);
343 assert!(r.from_service_worker());
344 }
345
346 #[test]
347 fn security_details_round_trip() {
348 let r = Response::new();
349 assert!(r.security_details().is_none());
350 r.set_security_details(SecurityDetails {
351 protocol: "TLS 1.3".into(),
352 subject_name: "example.com".into(),
353 issuer: "Let's Encrypt".into(),
354 valid_from: 0.0,
355 valid_to: 0.0,
356 });
357 let s = r.security_details().unwrap();
358 assert_eq!(s.protocol, "TLS 1.3");
359 assert_eq!(s.subject_name, "example.com");
360 }
361
362 #[test]
363 fn remote_address_round_trip() {
364 let r = Response::new();
365 assert!(r.remote_address().is_none());
366 r.set_remote_address(RemoteAddress {
367 ip: "127.0.0.1".into(),
368 port: 8080,
369 });
370 let a = r.remote_address().unwrap();
371 assert_eq!(a.ip, "127.0.0.1");
372 assert_eq!(a.port, 8080);
373 }
374
375 #[test]
376 fn body_round_trip() {
377 let r = Response::new();
378 assert!(r.body().is_none());
379 r.set_body(vec![1, 2, 3]);
380 assert_eq!(r.body(), Some(vec![1, 2, 3]));
381 }
382
383 #[test]
384 fn body_text_round_trip() {
385 let r = Response::new();
386 assert!(r.body_text().is_none());
387 r.set_body_text("hello");
388 assert_eq!(r.body_text(), Some("hello".into()));
389 }
390
391 #[test]
392 fn body_json_round_trip() {
393 let r = Response::new();
394 assert!(r.body_json().is_none());
395 r.set_body_json(serde_json::json!({"k": "v"}));
396 assert_eq!(r.body_json().unwrap()["k"], "v");
397 }
398
399 #[test]
400 fn request_link() {
401 let r = Response::new();
402 let req = Rc::new(Request::new("REQ-1"));
403 r.set_request(req);
404 assert_eq!(r.request().unwrap().id(), "REQ-1");
405 }
406}