Skip to main content

tor_dirclient/
body.rs

1//! Declare a type to represent the encoding of a request or its body.
2
3use std::sync::Arc;
4
5/// The body of an HTTP request.
6#[derive(Clone, Debug, Default)]
7pub struct RequestBody(
8    /// Whenever we make a request with a body, it is an _upload_ request.
9    /// Therefore, we would like to make sure the uploaded document is shared
10    /// among all the requests that we make, to avoid copies.
11    //
12    // NOTE: We will someday to support uploading multiple documents in one request
13    // as we do for routerdescs and extrainfo documents.
14    // When we reach that point, we will want to alter this type, unless it turns out that
15    // we always encode the multiple documents as a single string.
16    Option<Arc<str>>,
17);
18
19impl RequestBody {
20    /// Create a new empty RequestBody.
21    pub fn new_empty() -> Self {
22        Self(None)
23    }
24
25    /// Return a reference to the bytes in this body, if there are any.
26    fn as_bytes(&self) -> Option<&[u8]> {
27        self.0.as_ref().map(|s| s.as_bytes())
28    }
29}
30
31impl From<Arc<str>> for RequestBody {
32    fn from(value: Arc<str>) -> Self {
33        Self((!value.is_empty()).then_some(value))
34    }
35}
36
37impl RequestBody {
38    /// Return true if this [`RequestBody`] is empty.
39    pub fn is_empty(&self) -> bool {
40        self.0.as_ref().is_none_or(|s| s.is_empty())
41    }
42
43    /// Return the number of bytes in this [`RequestBody`].
44    pub fn len(&self) -> usize {
45        self.0.as_ref().map(|s| s.len()).unwrap_or(0)
46    }
47}
48
49/// The encoding of a http request.
50///
51/// This type is meant to avoid copies in the case where we want
52/// to upload a document in the body of a request.
53#[derive(Clone, Debug, Default)]
54pub(crate) struct EncodedRequest {
55    /// The request header.  We generate this fresh for each request.
56    header: String,
57
58    /// The request body.  This can be shared by multiple requests
59    /// (and generally is, for uploads).
60    body: RequestBody,
61}
62
63impl EncodedRequest {
64    /// Create a new EncodedRequest from a header.
65    pub(crate) fn from_header(header: String) -> Self {
66        Self {
67            header,
68            body: RequestBody::default(),
69        }
70    }
71
72    /// Return a [`Vec`] holding all the contents of this [`RequestBody`].
73    ///
74    /// This method is testing-only: it can copy more than we would want.
75    #[cfg(test)]
76    pub(crate) fn to_owned(&self) -> Vec<u8> {
77        let mut v = self.header.as_bytes().to_owned();
78        v.extend_from_slice(self.body.as_bytes().unwrap_or(&[]));
79        v
80    }
81
82    /// Set the body in this request.
83    pub(crate) fn set_body(&mut self, body: RequestBody) {
84        self.body = body;
85    }
86
87    /// Return an iterator over the byte slices that make up this request.
88    pub(crate) fn iter(&self) -> impl Iterator<Item = &'_ [u8]> {
89        use std::iter;
90        iter::once(self.header.as_bytes()).chain(self.body.as_bytes())
91    }
92}