1
//! Declare a type to represent the encoding of a request or its body.
2

            
3
use std::sync::Arc;
4

            
5
/// The body of an HTTP request.
6
#[derive(Clone, Debug, Default)]
7
pub 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

            
19
impl 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
3784
    fn as_bytes(&self) -> Option<&[u8]> {
27
3866
        self.0.as_ref().map(|s| s.as_bytes())
28
3784
    }
29
}
30

            
31
impl From<Arc<str>> for RequestBody {
32
3368
    fn from(value: Arc<str>) -> Self {
33
3368
        Self((!value.is_empty()).then_some(value))
34
3368
    }
35
}
36

            
37
impl RequestBody {
38
    /// Return true if this [`RequestBody`] is empty.
39
424
    pub fn is_empty(&self) -> bool {
40
426
        self.0.as_ref().is_none_or(|s| s.is_empty())
41
424
    }
42

            
43
    /// Return the number of bytes in this [`RequestBody`].
44
3364
    pub fn len(&self) -> usize {
45
3446
        self.0.as_ref().map(|s| s.len()).unwrap_or(0)
46
3364
    }
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)]
54
pub(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

            
63
impl EncodedRequest {
64
    /// Create a new EncodedRequest from a header.
65
3784
    pub(crate) fn from_header(header: String) -> Self {
66
3784
        Self {
67
3784
            header,
68
3784
            body: RequestBody::default(),
69
3784
        }
70
3784
    }
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
32
    pub(crate) fn to_owned(&self) -> Vec<u8> {
77
32
        let mut v = self.header.as_bytes().to_owned();
78
32
        v.extend_from_slice(self.body.as_bytes().unwrap_or(&[]));
79
32
        v
80
32
    }
81

            
82
    /// Set the body in this request.
83
3784
    pub(crate) fn set_body(&mut self, body: RequestBody) {
84
3784
        self.body = body;
85
3784
    }
86

            
87
    /// Return an iterator over the byte slices that make up this request.
88
3752
    pub(crate) fn iter(&self) -> impl Iterator<Item = &'_ [u8]> {
89
        use std::iter;
90
3752
        iter::once(self.header.as_bytes()).chain(self.body.as_bytes())
91
3752
    }
92
}