1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::marker::PhantomData;
4use std::ops::Range;
5use std::str::FromStr;
6
7use http::{HeaderMap, Method, Uri, Version};
8use o3::buffer::{Owned, Shared};
9
10#[derive(Clone)]
11enum LocalFrameOwnerRef<'req> {
12 Shared(Shared),
13 Borrowed(&'req [u8]),
14}
15
16pub struct LocalFrameBytesRef<'req> {
17 owner: LocalFrameOwnerRef<'req>,
18 range: Range<usize>,
19 _marker: PhantomData<&'req [u8]>,
20}
21
22pub type LocalFrameBytes = LocalFrameBytesRef<'static>;
23
24impl<'req> LocalFrameBytesRef<'req> {
25 pub fn len(&self) -> usize {
26 self.range.end.saturating_sub(self.range.start)
27 }
28
29 pub fn is_empty(&self) -> bool {
30 self.range.is_empty()
31 }
32
33 pub fn from_shared(owner: Shared) -> Self {
34 let len = owner.len();
35 Self::from_shared_range(owner, 0..len)
36 }
37
38 pub fn from_shared_range(owner: Shared, range: Range<usize>) -> Self {
39 let len = owner.len();
40 assert!(range.start <= range.end, "invalid local frame range");
41 assert!(range.end <= len, "local frame range exceeds owner length");
42 Self {
43 owner: LocalFrameOwnerRef::Shared(owner),
44 range,
45 _marker: PhantomData,
46 }
47 }
48
49 #[allow(clippy::missing_safety_doc)]
50 pub unsafe fn assume_static(self) -> LocalFrameBytes {
51 unsafe { std::mem::transmute::<LocalFrameBytesRef<'req>, LocalFrameBytes>(self) }
53 }
54
55 pub fn from_slice(slice: &'req [u8]) -> Self {
56 Self {
57 owner: LocalFrameOwnerRef::Borrowed(slice),
58 range: 0..slice.len(),
59 _marker: PhantomData,
60 }
61 }
62
63 pub fn as_bytes(&self) -> &[u8] {
64 match &self.owner {
65 LocalFrameOwnerRef::Shared(bytes) => &bytes[self.range.clone()],
66 LocalFrameOwnerRef::Borrowed(slice) => &slice[self.range.clone()],
67 }
68 }
69
70 pub fn slice(mut self, range: Range<usize>) -> Self {
71 let new_start = self.range.start + range.start;
72 let new_end = self.range.start + range.end;
73 assert!(new_end <= self.range.end, "slice exceeds frame length");
74 self.range = new_start..new_end;
75 self
76 }
77
78 pub fn into_bytes(self) -> Shared {
79 match self.owner {
80 LocalFrameOwnerRef::Shared(bytes) => bytes.slice(self.range),
81 LocalFrameOwnerRef::Borrowed(slice) => Shared::copy_from_slice(&slice[self.range]),
82 }
83 }
84}
85
86impl<'req> Clone for LocalFrameBytesRef<'req> {
87 fn clone(&self) -> Self {
88 Self {
89 owner: self.owner.clone(),
90 range: self.range.clone(),
91 _marker: PhantomData,
92 }
93 }
94}
95
96impl<'req> AsRef<[u8]> for LocalFrameBytesRef<'req> {
97 fn as_ref(&self) -> &[u8] {
98 self.as_bytes()
99 }
100}
101
102const INLINE_PATH_PARAM_CAP: usize = 2;
103
104type PathParam = (Box<str>, Range<usize>);
105
106#[derive(Clone)]
107enum PathParamStorage {
108 Inline {
109 items: [Option<PathParam>; INLINE_PATH_PARAM_CAP],
110 len: u8,
111 },
112 Heap(Vec<PathParam>),
113}
114
115#[derive(Clone)]
116pub struct PathParamRanges(PathParamStorage);
117
118impl Default for PathParamRanges {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl PathParamRanges {
125 pub fn new() -> Self {
126 Self(PathParamStorage::Inline {
127 items: [const { None }; INLINE_PATH_PARAM_CAP],
128 len: 0,
129 })
130 }
131
132 pub fn with_capacity(cap: usize) -> Self {
133 if cap > INLINE_PATH_PARAM_CAP {
134 Self(PathParamStorage::Heap(Vec::with_capacity(cap)))
135 } else {
136 Self::new()
137 }
138 }
139
140 pub fn push(&mut self, key: Box<str>, range: Range<usize>) {
141 match &mut self.0 {
142 PathParamStorage::Heap(heap) => heap.push((key, range)),
143 PathParamStorage::Inline { items, len }
144 if usize::from(*len) < INLINE_PATH_PARAM_CAP =>
145 {
146 items[usize::from(*len)] = Some((key, range));
147 *len += 1;
148 }
149 PathParamStorage::Inline { items, len } => {
150 let mut heap = Vec::with_capacity(usize::from(*len).saturating_add(1));
151 for slot in items.iter_mut().take(usize::from(*len)) {
152 if let Some(item) = slot.take() {
153 heap.push(item);
154 }
155 }
156 heap.push((key, range));
157 self.0 = PathParamStorage::Heap(heap);
158 }
159 }
160 }
161
162 pub fn find_last(&self, key: &str) -> Option<&Range<usize>> {
163 match &self.0 {
164 PathParamStorage::Heap(heap) => heap
165 .iter()
166 .rev()
167 .find(|(k, _)| k.as_ref() == key)
168 .map(|(_, r)| r),
169 PathParamStorage::Inline { items, len } => items
170 .iter()
171 .take(usize::from(*len))
172 .rev()
173 .find_map(|slot| slot.as_ref().filter(|(k, _)| k.as_ref() == key))
174 .map(|(_, r)| r),
175 }
176 }
177}
178
179pub struct Request {
180 method: Method,
181 uri: Uri,
182 version: Version,
183 headers: HeaderMap,
184 body: Owned,
185 path_params: Vec<(Box<str>, Shared)>,
186 query_cache: RefCell<QueryCache>,
187}
188
189#[derive(Clone, Debug)]
190enum QueryCache {
191 Unparsed,
192 NoQuery,
193 Invalid,
194 Parsed(HashMap<String, String>),
195}
196
197pub struct PathParamsIter<'a> {
198 req: &'a Request,
199 idx: usize,
200}
201
202impl<'a> Iterator for PathParamsIter<'a> {
203 type Item = (&'a str, &'a [u8]);
204
205 fn next(&mut self) -> Option<Self::Item> {
206 let (key, value) = self.req.path_params.get(self.idx)?;
207 self.idx += 1;
208 Some((key.as_ref(), value.as_ref()))
209 }
210
211 fn size_hint(&self) -> (usize, Option<usize>) {
212 let remaining = self.req.path_params.len().saturating_sub(self.idx);
213 (remaining, Some(remaining))
214 }
215}
216
217impl ExactSizeIterator for PathParamsIter<'_> {}
218
219impl Request {
220 pub fn new(method: Method, uri: Uri) -> Self {
221 Self {
222 method,
223 uri,
224 version: Version::HTTP_11,
225 headers: HeaderMap::new(),
226 body: Owned::new(),
227 path_params: Vec::new(),
228 query_cache: RefCell::new(QueryCache::Unparsed),
229 }
230 }
231
232 #[allow(clippy::should_implement_trait)]
233 pub fn default() -> Self {
234 Self::new(Method::GET, Uri::from_static("/"))
235 }
236
237 pub fn method(&self) -> &Method {
238 &self.method
239 }
240 pub fn uri(&self) -> &Uri {
241 &self.uri
242 }
243 pub fn headers(&self) -> &HeaderMap {
244 &self.headers
245 }
246 pub fn headers_mut(&mut self) -> &mut HeaderMap {
247 &mut self.headers
248 }
249
250 pub fn body(&self) -> &Owned {
251 &self.body
252 }
253
254 pub fn body_mut(&mut self) -> &mut Owned {
255 &mut self.body
256 }
257
258 pub fn set_body(&mut self, body: impl Into<Owned>) {
259 self.body = body.into();
260 }
261
262 pub fn clear_body(&mut self) {
263 self.body.clear();
264 }
265
266 pub fn set_body_str(&mut self, body: &str) -> &mut Self {
267 self.body = Owned::from(body.as_bytes());
268 self
269 }
270
271 pub fn body_str(&self) -> Option<&str> {
272 std::str::from_utf8(self.body.as_ref()).ok()
273 }
274
275 pub fn path_param<T: AsRef<str>>(&self, key: T) -> Option<&str> {
276 self.path_param_bytes(key)
277 .and_then(|v| std::str::from_utf8(v).ok())
278 }
279
280 pub fn path_param_bytes<T: AsRef<str>>(&self, key: T) -> Option<&[u8]> {
281 let k = key.as_ref();
282 self.path_params
283 .iter()
284 .rev()
285 .find(|(key, _)| key.as_ref() == k)
286 .map(|(_, v)| v.as_ref())
287 }
288
289 pub fn path_params(&self) -> PathParamsIter<'_> {
290 PathParamsIter { req: self, idx: 0 }
291 }
292
293 pub fn path_params_len(&self) -> usize {
294 self.path_params.len()
295 }
296
297 pub fn set_path_params(&mut self, params: Vec<(Box<str>, Shared)>) -> &mut Self {
298 self.path_params = params;
299 self
300 }
301
302 pub fn insert_path_param(
303 &mut self,
304 key: impl AsRef<str>,
305 value: impl Into<Shared>,
306 ) -> &mut Self {
307 let k = key.as_ref();
308 let v = value.into();
309 for (key, value) in self.path_params.iter_mut().rev() {
310 if key.as_ref() == k {
311 *value = v;
312 return self;
313 }
314 }
315
316 self.path_params.push((Box::<str>::from(k), v));
317 self
318 }
319
320 fn decode_query_component(input: &[u8]) -> Option<String> {
321 if !input.contains(&b'%') && !input.contains(&b'+') {
322 return std::str::from_utf8(input).ok().map(|s| s.to_string());
323 }
324
325 let mut out = Vec::with_capacity(input.len());
326 let mut i = 0usize;
327 while i < input.len() {
328 match input[i] {
329 b'+' => {
330 out.push(b' ');
331 i += 1;
332 }
333 b'%' => {
334 if i + 2 >= input.len() {
335 return None;
336 }
337 let hi = Self::from_hex(input[i + 1])?;
338 let lo = Self::from_hex(input[i + 2])?;
339 out.push((hi << 4) | lo);
340 i += 3;
341 }
342 b => {
343 out.push(b);
344 i += 1;
345 }
346 }
347 }
348
349 String::from_utf8(out).ok()
350 }
351
352 fn from_hex(c: u8) -> Option<u8> {
353 match c {
354 b'0'..=b'9' => Some(c - b'0'),
355 b'a'..=b'f' => Some(c - b'a' + 10),
356 b'A'..=b'F' => Some(c - b'A' + 10),
357 _ => None,
358 }
359 }
360
361 fn query_kv_iter(&self) -> impl Iterator<Item = (&[u8], &[u8])> {
362 self.uri
363 .query()
364 .unwrap_or("")
365 .as_bytes()
366 .split(|b| *b == b'&')
367 .filter(|kv| !kv.is_empty())
368 .map(|kv| {
369 let mut it = kv.splitn(2, |b| *b == b'=');
370 let k = it.next().unwrap_or(&[]);
371 let v = it.next().unwrap_or(&[]);
372 (k, v)
373 })
374 }
375
376 pub fn query<T: AsRef<str>>(&self, key: T) -> Option<String> {
377 let key = key.as_ref().as_bytes();
378 for (k, v) in self.query_kv_iter() {
379 if k == key {
380 return Self::decode_query_component(v);
381 }
382 if (k.contains(&b'%') || k.contains(&b'+'))
383 && Self::decode_query_component(k).is_some_and(|decoded| decoded.as_bytes() == key)
384 {
385 return Self::decode_query_component(v);
386 }
387 }
388 None
389 }
390
391 pub fn query_bytes<T: AsRef<[u8]>>(&self, key: T) -> Option<&[u8]> {
392 let key = key.as_ref();
393 self.query_kv_iter()
394 .find(|(k, _)| *k == key)
395 .map(|(_, v)| v)
396 }
397
398 pub fn with_uri(&mut self, uri: Uri) -> &mut Self {
399 self.uri = uri;
400 *self.query_cache.get_mut() = QueryCache::Unparsed;
401 self
402 }
403
404 pub fn with_query<T: serde::Serialize>(
405 &mut self,
406 query: &T,
407 ) -> Result<&mut Self, serde_urlencoded::ser::Error> {
408 let query_string = serde_urlencoded::to_string(query)?;
409 let current_uri = self.uri.clone();
410
411 let mut parts = current_uri.into_parts();
412 parts.path_and_query = Some(match parts.path_and_query {
413 Some(path_and_query) => {
414 let path = path_and_query.path();
415 let new_path_and_query = format!("{}?{}", path, query_string);
416 http::uri::PathAndQuery::from_str(&new_path_and_query).map_err(|e| {
417 <serde_urlencoded::ser::Error as serde::ser::Error>::custom(format!(
418 "invalid path and query: {e}"
419 ))
420 })?
421 }
422 None => {
423 let new_path_and_query = format!("/?{query_string}");
424 http::uri::PathAndQuery::from_str(&new_path_and_query).map_err(|e| {
425 <serde_urlencoded::ser::Error as serde::ser::Error>::custom(format!(
426 "invalid path and query: {e}"
427 ))
428 })?
429 }
430 });
431
432 let new_uri = Uri::from_parts(parts).map_err(|e| {
433 <serde_urlencoded::ser::Error as serde::ser::Error>::custom(format!(
434 "invalid uri parts: {e}"
435 ))
436 })?;
437
438 self.uri = new_uri;
439 *self.query_cache.get_mut() = QueryCache::Unparsed;
440 Ok(self)
441 }
442
443 pub fn query_params_ref(&self) -> Option<std::cell::Ref<'_, HashMap<String, String>>> {
444 {
445 let mut cache = self.query_cache.borrow_mut();
446 if matches!(*cache, QueryCache::Unparsed) {
447 *cache = match self.uri.query() {
448 None => QueryCache::NoQuery,
449 Some(q) => match serde_urlencoded::from_str::<HashMap<String, String>>(q) {
450 Ok(m) => QueryCache::Parsed(m),
451 Err(_) => QueryCache::Invalid,
452 },
453 };
454 }
455 }
456
457 std::cell::Ref::filter_map(self.query_cache.borrow(), |c| match c {
458 QueryCache::Parsed(m) => Some(m),
459 QueryCache::Invalid => None,
460 QueryCache::NoQuery => None,
461 QueryCache::Unparsed => None,
462 })
463 .ok()
464 }
465}
466
467impl std::fmt::Debug for Request {
468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469 f.debug_struct("Request")
470 .field("method", &self.method)
471 .field("uri", &self.uri)
472 .field("version", &self.version)
473 .field("headers", self.headers())
474 .field("body_len", &self.body.len())
475 .field("path_params", &self.path_params.len())
476 .finish()
477 }
478}
479
480impl Clone for Request {
481 fn clone(&self) -> Self {
482 let query_cache = self.query_cache.borrow().clone();
483 Self {
484 method: self.method.clone(),
485 uri: self.uri.clone(),
486 version: self.version,
487 headers: self.headers.clone(),
488 body: self.body.clone(),
489 path_params: self.path_params.clone(),
490 query_cache: RefCell::new(query_cache),
491 }
492 }
493}
494
495#[cfg(test)]
496mod tests;