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}