rocket/data/data.rs
1use crate::tokio::io::AsyncReadExt;
2use crate::data::data_stream::DataStream;
3use crate::data::{ByteUnit, StreamReader};
4
5/// The number of bytes to read into the "peek" buffer.
6pub const PEEK_BYTES: usize = 512;
7
8/// Type representing the body data of a request.
9///
10/// This type is the only means by which the body of a request can be retrieved.
11/// This type is not usually used directly. Instead, data guards (types that
12/// implement [`FromData`](crate::data::FromData)) are created indirectly via
13/// code generation by specifying the `data = "<var>"` route parameter as
14/// follows:
15///
16/// ```rust
17/// # #[macro_use] extern crate rocket;
18/// # type DataGuard = String;
19/// #[post("/submit", data = "<var>")]
20/// fn submit(var: DataGuard) { /* ... */ }
21/// # fn main() { }
22/// ```
23///
24/// Above, `DataGuard` can be any type that implements `FromData`. Note that
25/// `Data` itself implements `FromData`.
26///
27/// # Reading Data
28///
29/// Data may be read from a `Data` object by calling either the
30/// [`open()`](Data::open()) or [`peek()`](Data::peek()) methods.
31///
32/// The `open` method consumes the `Data` object and returns the raw data
33/// stream. The `Data` object is consumed for safety reasons: consuming the
34/// object ensures that holding a `Data` object means that all of the data is
35/// available for reading.
36///
37/// The `peek` method returns a slice containing at most 512 bytes of buffered
38/// body data. This enables partially or fully reading from a `Data` object
39/// without consuming the `Data` object.
40pub struct Data<'r> {
41 buffer: Vec<u8>,
42 is_complete: bool,
43 stream: StreamReader<'r>,
44}
45
46impl<'r> Data<'r> {
47 /// Create a `Data` from a recognized `stream`.
48 pub(crate) fn from<S: Into<StreamReader<'r>>>(stream: S) -> Data<'r> {
49 // TODO.async: This used to also set the read timeout to 5 seconds.
50 // Such a short read timeout is likely no longer necessary, but some
51 // kind of idle timeout should be implemented.
52
53 let stream = stream.into();
54 let buffer = Vec::with_capacity(PEEK_BYTES / 8);
55 Data { buffer, stream, is_complete: false }
56 }
57
58 /// This creates a `data` object from a local data source `data`.
59 #[inline]
60 pub(crate) fn local(data: Vec<u8>) -> Data<'r> {
61 Data {
62 buffer: data,
63 stream: StreamReader::empty(),
64 is_complete: true,
65 }
66 }
67
68 /// Returns the raw data stream, limited to `limit` bytes.
69 ///
70 /// The stream contains all of the data in the body of the request,
71 /// including that in the `peek` buffer. The method consumes the `Data`
72 /// instance. This ensures that a `Data` type _always_ represents _all_ of
73 /// the data in a request.
74 ///
75 /// # Example
76 ///
77 /// ```rust
78 /// use rocket::data::{Data, ToByteUnit};
79 ///
80 /// # const SIZE_LIMIT: u64 = 2 << 20; // 2MiB
81 /// fn handler(data: Data<'_>) {
82 /// let stream = data.open(2.mebibytes());
83 /// }
84 /// ```
85 pub fn open(self, limit: ByteUnit) -> DataStream<'r> {
86 DataStream::new(self.buffer, self.stream, limit.into())
87 }
88
89 /// Retrieve at most `num` bytes from the `peek` buffer without consuming
90 /// `self`.
91 ///
92 /// The peek buffer contains at most 512 bytes of the body of the request.
93 /// The actual size of the returned buffer is the `min` of the request's
94 /// body, `num` and `512`. The [`peek_complete`](#method.peek_complete)
95 /// method can be used to determine if this buffer contains _all_ of the
96 /// data in the body of the request.
97 ///
98 /// # Examples
99 ///
100 /// In a data guard:
101 ///
102 /// ```rust
103 /// use rocket::request::{self, Request, FromRequest};
104 /// use rocket::data::{Data, FromData, Outcome};
105 /// use rocket::http::Status;
106 /// # struct MyType;
107 /// # type MyError = String;
108 ///
109 /// #[rocket::async_trait]
110 /// impl<'r> FromData<'r> for MyType {
111 /// type Error = MyError;
112 ///
113 /// async fn from_data(r: &'r Request<'_>, mut data: Data<'r>) -> Outcome<'r, Self> {
114 /// if data.peek(2).await != b"hi" {
115 /// return Outcome::Forward((data, Status::BadRequest))
116 /// }
117 ///
118 /// /* .. */
119 /// # unimplemented!()
120 /// }
121 /// }
122 /// ```
123 ///
124 /// In a fairing:
125 ///
126 /// ```
127 /// use rocket::{Rocket, Request, Data, Response};
128 /// use rocket::fairing::{Fairing, Info, Kind};
129 /// # struct MyType;
130 ///
131 /// #[rocket::async_trait]
132 /// impl Fairing for MyType {
133 /// fn info(&self) -> Info {
134 /// Info {
135 /// name: "Data Peeker",
136 /// kind: Kind::Request
137 /// }
138 /// }
139 ///
140 /// async fn on_request(&self, req: &mut Request<'_>, data: &mut Data<'_>) {
141 /// if data.peek(2).await == b"hi" {
142 /// /* do something; body data starts with `"hi"` */
143 /// }
144 ///
145 /// /* .. */
146 /// # unimplemented!()
147 /// }
148 /// }
149 /// ```
150 pub async fn peek(&mut self, num: usize) -> &[u8] {
151 let num = std::cmp::min(PEEK_BYTES, num);
152 let mut len = self.buffer.len();
153 if len >= num {
154 return &self.buffer[..num];
155 }
156
157 while len < num {
158 match self.stream.read_buf(&mut self.buffer).await {
159 Ok(0) => { self.is_complete = true; break },
160 Ok(n) => len += n,
161 Err(e) => {
162 error_!("Failed to read into peek buffer: {:?}.", e);
163 break;
164 }
165 }
166 }
167
168 &self.buffer[..std::cmp::min(len, num)]
169 }
170
171 /// Returns true if the `peek` buffer contains all of the data in the body
172 /// of the request. Returns `false` if it does not or if it is not known if
173 /// it does.
174 ///
175 /// # Example
176 ///
177 /// ```rust
178 /// use rocket::data::Data;
179 ///
180 /// async fn handler(mut data: Data<'_>) {
181 /// if data.peek_complete() {
182 /// println!("All of the data: {:?}", data.peek(512).await);
183 /// }
184 /// }
185 /// ```
186 #[inline(always)]
187 pub fn peek_complete(&self) -> bool {
188 self.is_complete
189 }
190}