1use std::net::SocketAddr;
2use std::{borrow::Cow, collections::HashMap};
3
4use crate::{Method, Url};
5
6#[cfg(feature = "json")]
7use crate::ResponseLike;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
12#[cfg_attr(feature = "json", derive(serde::Serialize))]
13pub struct Request {
14 pub ip: SocketAddr,
16 pub url: String,
19 pub method: Method,
21 pub body: Vec<u8>,
25 pub headers: HashMap<String, String>,
27}
28
29impl Request {
30 pub fn new(bytes: &[u8], ip: SocketAddr) -> Option<Self> {
33 let mut lines = bytes.split(|&byte| byte == b'\n');
34
35 let first_line = String::from_utf8(lines.next()?.to_vec()).ok()?;
36 let mut parts = first_line.split_whitespace();
37
38 let method = Method::from(parts.next()?);
39 let url = parts.next()?.into();
40
41 let mut headers = HashMap::with_capacity(12);
43
44 let mut in_body = false;
45 let mut body = Vec::new();
46
47 for line in lines {
48 match (in_body, line == b"\r") {
49 (false, true) => in_body = true,
50 (true, _) => {
51 body.extend_from_slice(line);
52 body.push(0x0a )
53 }
54 _ => {
55 if let Some((key, value)) = Self::parse_header(line) {
56 headers.insert(key, value);
57 }
58 }
59 }
60 }
61
62 body.pop(); Some(Self {
65 ip,
66 url,
67 method,
68 body,
69 headers,
70 })
71 }
72
73 fn parse_header(line: &[u8]) -> Option<(String, String)> {
74 let pos = line.iter().position(|&byte| byte == b':')?;
75 let (key, rest) = line.split_at(pos);
76 let value = &rest[1..];
77
78 Some((
79 String::from_utf8_lossy(key).trim().to_string(),
80 String::from_utf8_lossy(value).trim().to_string(),
81 ))
82 }
83
84 pub fn get_header(&self, key: &str) -> Option<&str> {
86 self.headers.get(key).map(|s| s.as_str())
87 }
88
89 pub fn get_header_or(&self, key: &str, default: &'static str) -> &str {
91 self.get_header(key).unwrap_or(default)
92 }
93
94 pub fn has_header(&self, key: &str) -> bool {
96 self.headers.contains_key(key)
97 }
98
99 pub fn set_header<T: ToString, K: ToString>(&mut self, k: T, v: K) {
101 self.headers.insert(k.to_string(), v.to_string());
102 }
103
104 pub fn len(&self) -> usize {
106 self.body.len()
107 }
108
109 pub fn is_empty(&self) -> bool {
111 self.body.is_empty()
112 }
113
114 pub fn text(&self) -> Cow<'_, str> {
117 String::from_utf8_lossy(&self.body)
118 }
119
120 #[cfg(feature = "json")]
125 pub fn json<T>(&self) -> serde_json::Result<T>
126 where
127 T: for<'a> serde::de::Deserialize<'a>,
128 {
129 serde_json::from_slice(&self.body)
130 }
131
132 #[cfg(feature = "json")]
157 pub fn force_json<T>(&self) -> Result<T, crate::Response>
158 where
159 T: for<'a> serde::de::Deserialize<'a>,
160 {
161 self.json().map_err(|e| e.to_response())
162 }
163
164 pub fn parse_url(&self) -> Url {
167 self.url.as_str().into()
168 }
169
170 pub fn pretty_ip(&self) -> String {
172 crate::util::format_addr(self.ip)
173 }
174}