Skip to main content

trillium/
request_body.rs

1use crate::{Error, Transport};
2use futures_lite::AsyncRead;
3use trillium_http::ReceivedBody;
4use trillium_macros::AsyncRead;
5
6/// A received request body
7///
8/// This type represents a body that will be read from the underlying transport
9///
10/// ```rust
11/// # use trillium_testing::TestServer;
12/// # trillium_testing::block_on(async move {
13/// let app = TestServer::new(|mut conn: trillium::Conn| async move {
14///     let body = conn.request_body(); // 100-continue sent lazily on first read if needed
15///     let Ok(body_string) = body.with_max_len(1024).read_string().await else {
16///         return conn.with_status(500);
17///     };
18///
19///     conn.with_body(format!("received: {body_string}"))
20/// })
21/// .await;
22///
23/// app.get("/").await.assert_body("received: ");
24/// app.post("/")
25///     .with_body("hello")
26///     .await
27///     .assert_body("received: hello");
28/// # });
29/// ```
30///
31/// ## Bounds checking
32///
33/// Every `RequestBody` has a maximum length beyond which it will return an error, expressed as a
34/// u64. To override this on the specific `RequestBody`, use [`RequestBody::with_max_len`] or
35/// [`RequestBody::set_max_len`]
36#[derive(AsyncRead, Debug)]
37pub struct RequestBody<'a>(ReceivedBody<'a, Box<dyn Transport>>);
38
39impl RequestBody<'_> {
40    /// Similar to [`RequestBody::read_string`], but returns the raw bytes. This is useful for
41    /// bodies that are not text.
42    ///
43    /// You can use this in conjunction with `encoding` if you need different handling of malformed
44    /// character encoding than the lossy conversion provided by [`RequestBody::read_string`].
45    ///
46    /// An empty or nonexistent body will yield an empty Vec, not an error.
47    ///
48    /// # Errors
49    ///
50    /// This will return an error if there is an IO error on the underlying transport such as a
51    /// disconnect
52    ///
53    /// This will also return an error if the length exceeds the maximum length. To configure the
54    /// value on this specific request body, use [`RequestBody::with_max_len`] or
55    /// [`RequestBody::set_max_len`]
56    pub async fn read_bytes(self) -> Result<Vec<u8>, Error> {
57        self.0.read_bytes().await
58    }
59
60    /// # Reads the entire body to `String`.
61    ///
62    /// This uses the encoding determined by the content-type (mime) charset. If an encoding problem
63    /// is encountered, the String returned by [`RequestBody::read_string`] will contain utf8
64    /// replacement characters.
65    ///
66    /// Note that this can only be performed once per Conn, as the underlying data is not cached
67    /// anywhere. This is the only copy of the body contents.
68    ///
69    /// An empty or nonexistent body will yield an empty String, not an error
70    ///
71    /// # Errors
72    ///
73    /// This will return an error if there is an IO error on the
74    /// underlying transport such as a disconnect
75    ///
76    ///
77    /// This will also return an error if the length exceeds the maximum length. To configure the
78    /// value on this specific request body, use [`RequestBody::with_max_len`] or
79    /// [`RequestBody::set_max_len`].
80    pub async fn read_string(self) -> Result<String, Error> {
81        self.0.read_string().await
82    }
83
84    /// Set the maximum content length to read, returning self
85    ///
86    /// This protects against an memory-use denial-of-service attack wherein an untrusted peer sends
87    /// an unbounded request body. This is especially important when using
88    /// [`RequestBody::read_string`] and [`RequestBody::read_bytes`] instead of streaming with
89    /// `AsyncRead`.
90    ///
91    /// The default value can be found documented [in the trillium-http
92    /// crate](https://docs.trillium.rs/trillium_http/struct.httpconfig#received_body_max_len)
93    #[must_use]
94    pub fn with_max_len(mut self, max_len: u64) -> Self {
95        self.0.set_max_len(max_len);
96        self
97    }
98
99    /// Set the maximum content length to read
100    ///
101    /// This protects against an memory-use denial-of-service attack wherein an untrusted peer sends
102    /// an unbounded request body. This is especially important when using
103    /// [`RequestBody::read_string`] and [`RequestBody::read_bytes`] instead of streaming with
104    /// `AsyncRead`.
105    ///
106    /// The default value can be found documented [in the trillium-http
107    /// crate](https://docs.trillium.rs/trillium_http/struct.httpconfig#received_body_max_len)
108    pub fn set_max_len(&mut self, max_len: u64) -> &mut Self {
109        self.0.set_max_len(max_len);
110        self
111    }
112
113    /// The content-length of this body, if available.
114    ///
115    /// This value usually is derived from the content-length header. If the request that this body
116    /// is attached to uses transfer-encoding chunked, this will be None.
117    pub fn content_length(&self) -> Option<u64> {
118        self.0.content_length()
119    }
120}
121
122impl<'a> From<RequestBody<'a>> for ReceivedBody<'a, Box<dyn Transport>> {
123    fn from(value: RequestBody<'a>) -> Self {
124        value.0
125    }
126}
127
128impl<'a> From<ReceivedBody<'a, Box<dyn Transport>>> for RequestBody<'a> {
129    fn from(received_body: ReceivedBody<'a, Box<dyn Transport>>) -> Self {
130        Self(received_body)
131    }
132}