Skip to main content

sark_core/request/
mod.rs

1//! Validated request ownership and projection.
2
3use std::{mem, ops, ops::Deref};
4
5use o3::{
6    buffer::{bytes::scope, pool, pool::owners, storage},
7    mem::rc,
8};
9use sark_http::{
10    field::metadata::{
11        packed,
12        section::storage::{Block, Bytes, Scope},
13    },
14    payload::owned,
15    request,
16    request::{Bound, Error, Form, Id, Layout, span},
17};
18
19use crate::{body, request::frame::proof};
20
21pub mod field;
22pub mod frame;
23#[doc(hidden)]
24pub mod mode;
25pub mod path;
26pub mod raw;
27mod view;
28
29use request::Source;
30pub use view::View;
31
32#[derive(Clone, Copy, PartialEq, Eq, Debug)]
33pub enum BindError {
34    Layout(request::Error),
35    Body(body::Error),
36    Retain(rc::Error),
37}
38
39#[repr(transparent)]
40#[derive(Clone, Copy)]
41pub struct BodyLen {
42    len: usize,
43}
44
45impl BodyLen {
46    pub const fn new(len: usize) -> Self {
47        Self { len }
48    }
49
50    pub const fn len(&self) -> usize {
51        self.len
52    }
53
54    pub const fn is_empty(&self) -> bool {
55        self.len == 0
56    }
57}
58
59#[doc(hidden)]
60pub struct Parts<'d, P = proof::Maybe> {
61    head: Head<storage::Shared, P>,
62    id: Id<'d>,
63}
64
65impl<'d, P: proof::Proof> Parts<'d, P> {
66    pub fn from_head(id: Id<'d>, head: Head<storage::Shared, P>) -> Self {
67        Self { head, id }
68    }
69
70    pub fn try_frame_at(
71        &self,
72        range: ops::Range<usize>,
73    ) -> Result<request::Bytes<'d>, frame::Error<rc::Error>> {
74        let start = range.start;
75        let end = range.end;
76        let capacity = self.head.as_ref().len();
77        if start > end {
78            return Err(frame::Error::Order {
79                start,
80                end,
81                capacity,
82            });
83        }
84        let frame = self
85            .head
86            .owner()
87            .try_get(start..end)
88            .map_err(frame::Error::Retain)?
89            .ok_or(frame::Error::Bounds {
90                start,
91                end,
92                capacity,
93            })?;
94        Ok(request::Bytes::from_shared(self.id, frame))
95    }
96
97    #[doc(hidden)]
98    pub const fn id(&self) -> Id<'d> {
99        self.id
100    }
101}
102
103#[doc(hidden)]
104pub struct Retained<'d, P = proof::Maybe> {
105    parts: Parts<'d, P>,
106    body: body::Body<owned::Segments<'d>>,
107}
108
109impl<'d, P: proof::Proof> Retained<'d, P> {
110    pub fn from_head(
111        id: Id<'d>,
112        head: Head<storage::Shared, P>,
113        body: body::Body<owned::Segments<'d>>,
114    ) -> Self {
115        Self {
116            parts: Parts::from_head(id, head),
117            body,
118        }
119    }
120
121    pub const fn body_len(&self) -> usize {
122        self.body.len()
123    }
124
125    pub fn into_body(self) -> request::Segments<'d> {
126        request::Segments::new(self.parts.id, self.body.into_body())
127    }
128
129    pub fn into_parts(self) -> (Parts<'d, P>, body::Body<owned::Segments<'d>>) {
130        (self.parts, self.body)
131    }
132}
133
134impl<'d, P> Deref for Retained<'d, P> {
135    type Target = Parts<'d, P>;
136
137    fn deref(&self) -> &Self::Target {
138        &self.parts
139    }
140}
141
142const _: () = assert!(mem::size_of::<View<'static, 'static>>() <= 10 * mem::size_of::<usize>());
143const _: () = assert!(mem::size_of::<Parts<'static>>() <= 10 * mem::size_of::<usize>());
144const _: () = assert!(mem::size_of::<Retained<'static>>() <= 14 * mem::size_of::<usize>());
145const _: () =
146    assert!(mem::size_of::<Parts<'static, proof::Peer>>() <= mem::size_of::<Parts<'static>>());
147const _: () = assert!(
148    mem::size_of::<Retained<'static, proof::Peer>>() <= mem::size_of::<Retained<'static>>()
149);
150
151/// A validated head over immutable wire storage.
152///
153pub struct Head<O, P = proof::Maybe> {
154    owner: O,
155    target: request::Target,
156    authority: P,
157    route: Route,
158}
159
160const _: () = {
161    assert!(mem::size_of::<Head<&'static [u8]>>() == 5 * mem::size_of::<usize>());
162    assert!(mem::size_of::<Option<Head<&'static [u8]>>>() == mem::size_of::<Head<&[u8]>>());
163    assert!(
164        mem::size_of::<Head<&'static [u8], proof::Peer>>() <= mem::size_of::<Head<&'static [u8]>>()
165    );
166    assert!(!mem::needs_drop::<Head<&'static [u8]>>());
167};
168
169impl<O: Source, P: proof::Proof> Head<O, P> {
170    pub fn bind(owner: O, layout: request::Layout) -> Result<Self, BindError> {
171        Self::try_bound(request::Bound::try_bind(owner, layout).map_err(BindError::Layout)?)
172    }
173
174    pub fn borrow(&self) -> Head<&[u8], P> {
175        Head {
176            owner: self.owner.as_ref(),
177            target: self.target,
178            authority: self.authority,
179            route: self.route,
180        }
181    }
182}
183
184impl<O: Source, P> AsRef<[u8]> for Head<O, P> {
185    fn as_ref(&self) -> &[u8] {
186        self.owner.as_ref()
187    }
188}
189
190impl<O, P> Head<O, P> {
191    pub const fn form(&self) -> request::Form {
192        self.target.form()
193    }
194
195    pub(in crate::request) const fn route(&self) -> Projection {
196        self.route.path()
197    }
198    pub fn into_owner(self) -> O {
199        self.owner
200    }
201
202    pub const fn owner(&self) -> &O {
203        &self.owner
204    }
205}
206
207impl<O> Head<O, proof::Peer> {
208    pub(in crate::request) const fn peer(&self) -> span::Span {
209        self.authority.get()
210    }
211}
212
213impl<P, const PREFIX: usize> From<Head<packed::Decoded<o3::buffer::pool::Span, PREFIX>, P>>
214    for Head<Bytes, P>
215{
216    fn from(head: Head<packed::Decoded<o3::buffer::pool::Span, PREFIX>, P>) -> Self {
217        Self {
218            owner: head.owner.into_bytes(),
219            target: head.target,
220            authority: head.authority,
221            route: head.route,
222        }
223    }
224}
225
226impl<P: proof::Proof> Head<Bytes, P> {
227    pub fn from_path(
228        owner: Bytes,
229        target: ops::Range<usize>,
230        path_len: usize,
231        peer: Option<ops::Range<usize>>,
232    ) -> Result<Self, BindError> {
233        let error = |cause| BindError::Layout(request::Error::Span(cause));
234        let target = span::Span::from_range(target).map_err(error)?;
235        let route = span::Span::checked(target.start(), path_len).map_err(error)?;
236        let target =
237            request::Target::from_span(request::Form::Path, target).map_err(BindError::Layout)?;
238        let peer = peer
239            .map(span::Span::from_range)
240            .transpose()
241            .map_err(error)?;
242        let layout = request::Layout::new(target, peer, Some(route));
243        Self::bind(owner, layout)
244    }
245
246    pub fn retain(self) -> Head<storage::Shared, P> {
247        Head {
248            owner: self.owner.into(),
249            target: self.target,
250            authority: self.authority,
251            route: self.route,
252        }
253    }
254}
255
256#[derive(Clone, Copy)]
257enum Route {
258    Frame(span::Span),
259    Literal(Literal),
260}
261
262#[derive(Clone, Copy)]
263enum Literal {
264    Root,
265    Star,
266}
267
268pub(in crate::request) enum Projection {
269    Frame(ops::Range<usize>),
270    Root,
271    Star,
272}
273
274impl Route {
275    const fn path(self) -> Projection {
276        match self {
277            Self::Frame(span) => Projection::Frame(span.start()..span.end()),
278            Self::Literal(Literal::Root) => Projection::Root,
279            Self::Literal(Literal::Star) => Projection::Star,
280        }
281    }
282}
283
284const _: () = assert!(mem::size_of::<Route>() == mem::size_of::<span::Span>());
285
286impl<O: Scope, P> Head<O, P> {
287    pub fn scope<T>(self, use_head: impl for<'id> FnOnce(Head<O::Scoped<'id>, P>) -> T) -> T {
288        let Self {
289            owner,
290            target,
291            authority: peer,
292            route,
293        } = self;
294        owner.scope(|owner| {
295            use_head(Head {
296                owner,
297                target,
298                authority: peer,
299                route,
300            })
301        })
302    }
303}
304
305impl<P>
306    Head<
307        Bytes<scope::Owner<'_, pool::Span<owners::Plain<pool::Layout>, pool::Owned, pool::Frozen>>>,
308        P,
309    >
310{
311    pub fn into_inner(self) -> Head<Bytes, P> {
312        Head {
313            owner: self.owner.into_inner(),
314            target: self.target,
315            authority: self.authority,
316            route: self.route,
317        }
318    }
319}
320
321impl<O: Source, P: proof::Proof> Head<O, P> {
322    pub fn try_bound(bound: Bound<O>) -> Result<Self, BindError> {
323        let (owner, layout) = bound.into_parts();
324        // SAFETY: Bound retains the immutable owner validated with this layout.
325        unsafe { Self::try_parts(owner, layout) }
326    }
327
328    /// # Safety
329    /// `layout` must be valid for the exact immutable bytes retained by `owner`.
330    unsafe fn try_parts(owner: O, layout: Layout) -> Result<Self, BindError> {
331        let target = layout.target();
332        let proof = P::try_from(layout).map_err(BindError::Layout)?;
333        let route = match target.form() {
334            Form::Path | Form::Extended => {
335                let route = layout
336                    .route()
337                    .ok_or(BindError::Layout(Error::InvalidRange))?;
338                Route::Frame(route)
339            }
340            Form::Root => Route::Literal(Literal::Root),
341            Form::Asterisk | Form::Authority => Route::Literal(Literal::Star),
342        };
343        Ok(Self {
344            owner,
345            target,
346            authority: proof,
347            route,
348        })
349    }
350}
351
352impl<'a, P> Head<&'a [u8], P> {
353    pub fn path(&self) -> &'a [u8] {
354        match self.route() {
355            Projection::Frame(range) => {
356                // SAFETY: bind validates this route against the retained owner.
357                unsafe { self.owner.get_unchecked(range) }
358            }
359            Projection::Root => b"/",
360            Projection::Star => b"*",
361        }
362    }
363}
364
365impl<'a, P: proof::Proof> Head<&'a [u8], P> {
366    pub fn authority(&self) -> Option<&'a [u8]> {
367        self.authority.span().map(|span| {
368            // SAFETY: bind validates this authority against the retained owner.
369            unsafe { self.owner.get_unchecked(span.start()..span.end()) }
370        })
371    }
372}