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}}