rocket/local/request.rs
1macro_rules! pub_request_impl {
2 ($import:literal $($prefix:tt $suffix:tt)?) =>
3{
4 /// Borrows the inner `Request` as seen by Rocket.
5 ///
6 /// Note that no routing has occurred and that there is no remote
7 /// address unless one has been explicitly set with
8 /// [`set_remote()`](Request::set_remote()).
9 ///
10 /// # Example
11 ///
12 /// ```rust
13 #[doc = $import]
14 ///
15 /// # Client::_test(|_, request, _| {
16 /// let request: LocalRequest = request;
17 /// let inner: &rocket::Request = request.inner();
18 /// # });
19 /// ```
20 #[inline(always)]
21 pub fn inner(&self) -> &Request<'c> {
22 self._request()
23 }
24
25 /// Mutably borrows the inner `Request` as seen by Rocket.
26 ///
27 /// Note that no routing has occurred and that there is no remote
28 /// address unless one has been explicitly set with
29 /// [`set_remote()`](Request::set_remote()).
30 ///
31 /// # Example
32 ///
33 /// ```rust
34 #[doc = $import]
35 ///
36 /// # Client::_test(|_, request, _| {
37 /// let mut request: LocalRequest = request;
38 /// let inner: &mut rocket::Request = request.inner_mut();
39 /// # });
40 /// ```
41 #[inline(always)]
42 pub fn inner_mut(&mut self) -> &mut Request<'c> {
43 self._request_mut()
44 }
45
46 /// Add a header to this request.
47 ///
48 /// Any type that implements `Into<Header>` can be used here. Among
49 /// others, this includes [`ContentType`] and [`Accept`].
50 ///
51 /// [`ContentType`]: crate::http::ContentType
52 /// [`Accept`]: crate::http::Accept
53 ///
54 /// # Examples
55 ///
56 /// Add the Content-Type header:
57 ///
58 /// ```rust
59 #[doc = $import]
60 /// use rocket::http::Header;
61 /// use rocket::http::ContentType;
62 ///
63 /// # Client::_test(|_, request, _| {
64 /// let request: LocalRequest = request;
65 /// let req = request
66 /// .header(ContentType::JSON)
67 /// .header(Header::new("X-Custom", "custom-value"));
68 /// # });
69 /// ```
70 #[inline]
71 pub fn header<H>(mut self, header: H) -> Self
72 where H: Into<crate::http::Header<'static>>
73 {
74 self._request_mut().add_header(header.into());
75 self
76 }
77
78 /// Adds a header to this request without consuming `self`.
79 ///
80 /// # Examples
81 ///
82 /// Add the Content-Type header:
83 ///
84 /// ```rust
85 #[doc = $import]
86 /// use rocket::http::ContentType;
87 ///
88 /// # Client::_test(|_, mut request, _| {
89 /// let mut request: LocalRequest = request;
90 /// request.add_header(ContentType::JSON);
91 /// # });
92 /// ```
93 #[inline]
94 pub fn add_header<H>(&mut self, header: H)
95 where H: Into<crate::http::Header<'static>>
96 {
97 self._request_mut().add_header(header.into());
98 }
99
100 /// Set the remote address of this request to `address`.
101 ///
102 /// `address` may be any type that [can be converted into a `Endpoint`].
103 /// If `address` fails to convert, the remote is left unchanged.
104 ///
105 /// [can be converted into a `Endpoint`]: crate::listener::Endpoint#conversions
106 ///
107 /// # Examples
108 ///
109 /// Set the remote address to "8.8.8.8:80":
110 ///
111 /// ```rust
112 /// use std::net::Ipv4Addr;
113 ///
114 #[doc = $import]
115 ///
116 /// # Client::_test(|_, request, _| {
117 /// let request: LocalRequest = request;
118 /// let req = request.remote("tcp:8.8.8.8:80");
119 ///
120 /// let remote = req.inner().remote().unwrap().tcp().unwrap();
121 /// assert_eq!(remote.ip(), Ipv4Addr::new(8, 8, 8, 8));
122 /// assert_eq!(remote.port(), 80);
123 /// # });
124 /// ```
125 #[inline]
126 pub fn remote<T>(mut self, endpoint: T) -> Self
127 where T: TryInto<crate::listener::Endpoint>
128 {
129 if let Ok(endpoint) = endpoint.try_into() {
130 self.set_remote(endpoint);
131 } else {
132 warn!("remote failed to convert");
133 }
134
135 self
136 }
137
138 /// Add a cookie to this request.
139 ///
140 /// # Examples
141 ///
142 /// Add `user_id` cookie:
143 ///
144 /// ```rust
145 #[doc = $import]
146 /// use rocket::http::Cookie;
147 ///
148 /// # Client::_test(|_, request, _| {
149 /// let request: LocalRequest = request;
150 /// let req = request
151 /// .cookie(("username", "sb"))
152 /// .cookie(("user_id", "12"));
153 /// # });
154 /// ```
155 #[inline]
156 pub fn cookie<'a, C>(mut self, cookie: C) -> Self
157 where C: Into<crate::http::Cookie<'a>>
158 {
159 self._request_mut().cookies_mut().add_original(cookie.into().into_owned());
160 self
161 }
162
163 /// Add all of the cookies in `cookies` to this request.
164 ///
165 /// # Example
166 ///
167 /// ```rust
168 #[doc = $import]
169 /// use rocket::http::Cookie;
170 ///
171 /// # Client::_test(|_, request, _| {
172 /// let request: LocalRequest = request;
173 /// let cookies = vec![("a", "b"), ("c", "d")];
174 /// let req = request.cookies(cookies);
175 /// # });
176 /// ```
177 #[inline]
178 pub fn cookies<'a, C, I>(mut self, cookies: I) -> Self
179 where C: Into<crate::http::Cookie<'a>>,
180 I: IntoIterator<Item = C>
181 {
182 for cookie in cookies {
183 let cookie: crate::http::Cookie<'_> = cookie.into();
184 self._request_mut().cookies_mut().add_original(cookie.into_owned());
185 }
186
187 self
188 }
189
190 /// Add a [private cookie] to this request.
191 ///
192 /// [private cookie]: crate::http::CookieJar::add_private()
193 ///
194 /// # Examples
195 ///
196 /// Add `user_id` as a private cookie:
197 ///
198 /// ```rust
199 #[doc = $import]
200 /// use rocket::http::Cookie;
201 ///
202 /// # Client::_test(|_, request, _| {
203 /// let request: LocalRequest = request;
204 /// let req = request.private_cookie(("user_id", "sb"));
205 /// # });
206 /// ```
207 #[cfg(feature = "secrets")]
208 #[cfg_attr(nightly, doc(cfg(feature = "secrets")))]
209 #[inline]
210 pub fn private_cookie<C>(mut self, cookie: C) -> Self
211 where C: Into<crate::http::Cookie<'static>>
212 {
213 self._request_mut().cookies_mut().add_original_private(cookie.into());
214 self
215 }
216
217 /// Set mTLS client certificates to send along with the request.
218 ///
219 /// If the request already contained certificates, they are replaced with
220 /// those in `reader.`
221 ///
222 /// `reader` is expected to be PEM-formatted and contain X509 certificates.
223 /// If it contains more than one certificate, the entire chain is set on the
224 /// request. If it contains items other than certificates, the certificate
225 /// chain up to the first non-certificate item is set on the request. If
226 /// `reader` is syntactically invalid PEM, certificates are cleared on the
227 /// request.
228 ///
229 /// The type `C` can be anything that implements [`std::io::Read`]. This
230 /// includes: `&[u8]`, `File`, `&File`, `Stdin`, and so on. To read a file
231 /// in at compile-time, use [`include_bytes!()`].
232 ///
233 /// ```rust
234 /// use std::fs::File;
235 ///
236 #[doc = $import]
237 /// use rocket::fs::relative;
238 ///
239 /// # Client::_test(|_, request, _| {
240 /// let request: LocalRequest = request;
241 /// let path = relative!("../../examples/tls/private/ed25519_cert.pem");
242 /// let req = request.identity(File::open(path).unwrap());
243 /// # });
244 /// ```
245 #[cfg(feature = "mtls")]
246 #[cfg_attr(nightly, doc(cfg(feature = "mtls")))]
247 pub fn identity<C: std::io::Read>(mut self, reader: C) -> Self {
248 use std::sync::Arc;
249 use crate::listener::Certificates;
250
251 let mut reader = std::io::BufReader::new(reader);
252 self._request_mut().connection.peer_certs = rustls_pemfile::certs(&mut reader)
253 .collect::<Result<Vec<_>, _>>()
254 .map(|certs| Arc::new(Certificates::from(certs)))
255 .ok();
256
257 self
258 }
259
260 /// Sets the body data of the request.
261 ///core/lib/src/local/request.rs
262 /// # Examples
263 ///
264 /// ```rust
265 #[doc = $import]
266 /// use rocket::http::ContentType;
267 ///
268 /// # Client::_test(|_, request, _| {
269 /// let request: LocalRequest = request;
270 /// let req = request
271 /// .header(ContentType::Text)
272 /// .body("Hello, world!");
273 /// # });
274 /// ```
275 #[inline]
276 pub fn body<S: AsRef<[u8]>>(mut self, body: S) -> Self {
277 // TODO: For CGI, we want to be able to set the body to be stdin
278 // without actually reading everything into a vector. Can we allow
279 // that here while keeping the simplicity? Looks like it would
280 // require us to reintroduce a NetStream::Local(Box<Read>) or
281 // something like that.
282 *self._body_mut() = body.as_ref().into();
283 self
284 }
285
286 /// Sets the body to `value` serialized as JSON with `Content-Type`
287 /// [`ContentType::JSON`](crate::http::ContentType::JSON).
288 ///
289 /// If `value` fails to serialize, the body is set to empty. The
290 /// `Content-Type` header is _always_ set.
291 ///
292 /// # Examples
293 ///
294 /// ```rust
295 #[doc = $import]
296 /// use rocket::serde::Serialize;
297 /// use rocket::http::ContentType;
298 ///
299 /// #[derive(Serialize)]
300 /// struct Task {
301 /// id: usize,
302 /// complete: bool,
303 /// }
304 ///
305 /// # Client::_test(|_, request, _| {
306 /// let task = Task { id: 10, complete: false };
307 ///
308 /// let request: LocalRequest = request;
309 /// let req = request.json(&task);
310 /// assert_eq!(req.content_type(), Some(&ContentType::JSON));
311 /// # });
312 /// ```
313 #[cfg(feature = "json")]
314 #[cfg_attr(nightly, doc(cfg(feature = "json")))]
315 pub fn json<T: crate::serde::Serialize>(self, value: &T) -> Self {
316 let json = serde_json::to_vec(&value).unwrap_or_default();
317 self.header(crate::http::ContentType::JSON).body(json)
318 }
319
320 /// Sets the body to `value` serialized as MessagePack with `Content-Type`
321 /// [`ContentType::MsgPack`](crate::http::ContentType::MsgPack).
322 ///
323 /// If `value` fails to serialize, the body is set to empty. The
324 /// `Content-Type` header is _always_ set.
325 ///
326 /// # Examples
327 ///
328 /// ```rust
329 #[doc = $import]
330 /// use rocket::serde::Serialize;
331 /// use rocket::http::ContentType;
332 ///
333 /// #[derive(Serialize)]
334 /// struct Task {
335 /// id: usize,
336 /// complete: bool,
337 /// }
338 ///
339 /// # Client::_test(|_, request, _| {
340 /// let task = Task { id: 10, complete: false };
341 ///
342 /// let request: LocalRequest = request;
343 /// let req = request.msgpack(&task);
344 /// assert_eq!(req.content_type(), Some(&ContentType::MsgPack));
345 /// # });
346 /// ```
347 #[cfg(feature = "msgpack")]
348 #[cfg_attr(nightly, doc(cfg(feature = "msgpack")))]
349 pub fn msgpack<T: crate::serde::Serialize>(self, value: &T) -> Self {
350 let msgpack = rmp_serde::to_vec(value).unwrap_or_default();
351 self.header(crate::http::ContentType::MsgPack).body(msgpack)
352 }
353
354 /// Set the body (data) of the request without consuming `self`.
355 ///
356 /// # Examples
357 ///
358 /// Set the body to be a JSON structure; also sets the Content-Type.
359 ///
360 /// ```rust
361 #[doc = $import]
362 /// use rocket::http::ContentType;
363 ///
364 /// # Client::_test(|_, request, _| {
365 /// let request: LocalRequest = request;
366 /// let mut request = request.header(ContentType::JSON);
367 /// request.set_body(r#"{ "key": "value", "array": [1, 2, 3] }"#);
368 /// # });
369 /// ```
370 #[inline]
371 pub fn set_body<S: AsRef<[u8]>>(&mut self, body: S) {
372 *self._body_mut() = body.as_ref().into();
373 }
374
375 /// Dispatches the request, returning the response.
376 ///
377 /// This method consumes `self` and is the preferred mechanism for
378 /// dispatching.
379 ///
380 /// # Example
381 ///
382 /// ```rust
383 #[doc = $import]
384 ///
385 /// # Client::_test(|_, request, _| {
386 /// let request: LocalRequest = request;
387 /// let response = request.dispatch();
388 /// # });
389 /// ```
390 #[inline(always)]
391 pub $($prefix)? fn dispatch(self) -> LocalResponse<'c> {
392 self._dispatch()$(.$suffix)?
393 }
394
395 #[cfg(test)]
396 #[allow(dead_code)]
397 fn _ensure_impls_exist() {
398 fn is_clone_debug<T: Clone + std::fmt::Debug>() {}
399 is_clone_debug::<Self>();
400
401 fn is_deref_req<'a, T: std::ops::Deref<Target = Request<'a>>>() {}
402 is_deref_req::<Self>();
403
404 fn is_deref_mut_req<'a, T: std::ops::DerefMut<Target = Request<'a>>>() {}
405 is_deref_mut_req::<Self>();
406 }
407}}