rocket/request/request.rs
1use std::{io, fmt};
2use std::ops::RangeFrom;
3use std::sync::{Arc, atomic::Ordering};
4use std::borrow::Cow;
5use std::str::FromStr;
6use std::future::Future;
7use std::net::IpAddr;
8
9use http::Version;
10use rocket_http::HttpVersion;
11use state::{TypeMap, InitCell};
12use futures::future::BoxFuture;
13use ref_swap::OptionRefSwap;
14
15use crate::{Rocket, Route, Orbit};
16use crate::request::{FromParam, FromSegments, FromRequest, Outcome, AtomicMethod};
17use crate::form::{self, ValueField, FromForm};
18use crate::data::Limits;
19
20use crate::http::ProxyProto;
21use crate::http::{Method, Header, HeaderMap, ContentType, Accept, MediaType, CookieJar, Cookie};
22use crate::http::uri::{fmt::Path, Origin, Segments, Host, Authority};
23use crate::listener::{Certificates, Endpoint};
24
25/// The type of an incoming web request.
26///
27/// This should be used sparingly in Rocket applications. In particular, it
28/// should likely only be used when writing [`FromRequest`] implementations. It
29/// contains all of the information for a given web request except for the body
30/// data. This includes the HTTP method, URI, cookies, headers, and more.
31#[derive(Clone)]
32pub struct Request<'r> {
33 method: AtomicMethod,
34 uri: Origin<'r>,
35 headers: HeaderMap<'r>,
36 pub(crate) version: Option<HttpVersion>,
37 pub(crate) errors: Vec<RequestError>,
38 pub(crate) connection: ConnectionMeta,
39 pub(crate) state: RequestState<'r>,
40}
41
42/// Information derived from an incoming connection, if any.
43#[derive(Clone, Default)]
44pub(crate) struct ConnectionMeta {
45 pub peer_endpoint: Option<Endpoint>,
46 #[cfg_attr(not(feature = "mtls"), allow(dead_code))]
47 pub peer_certs: Option<Arc<Certificates<'static>>>,
48}
49
50impl ConnectionMeta {
51 pub fn new(endpoint: io::Result<Endpoint>, certs: Option<Certificates<'_>>) -> Self {
52 ConnectionMeta {
53 peer_endpoint: endpoint.ok(),
54 peer_certs: certs.map(|c| c.into_owned()).map(Arc::new),
55 }
56 }
57}
58
59/// Information derived from the request.
60pub(crate) struct RequestState<'r> {
61 pub rocket: &'r Rocket<Orbit>,
62 pub route: OptionRefSwap<'r, Route>,
63 pub cookies: CookieJar<'r>,
64 pub accept: InitCell<Option<Accept>>,
65 pub content_type: InitCell<Option<ContentType>>,
66 pub cache: Arc<TypeMap![Send + Sync]>,
67 pub host: Option<Host<'r>>,
68}
69
70impl Clone for RequestState<'_> {
71 fn clone(&self) -> Self {
72 RequestState {
73 rocket: self.rocket,
74 route: OptionRefSwap::new(self.route.load(Ordering::Acquire)),
75 cookies: self.cookies.clone(),
76 accept: self.accept.clone(),
77 content_type: self.content_type.clone(),
78 cache: self.cache.clone(),
79 host: self.host.clone(),
80 }
81 }
82}
83
84impl<'r> Request<'r> {
85 /// Create a new `Request` with the given `method` and `uri`.
86 #[inline(always)]
87 pub(crate) fn new<'s: 'r>(
88 rocket: &'r Rocket<Orbit>,
89 method: Method,
90 uri: Origin<'s>,
91 version: Option<HttpVersion>,
92 ) -> Request<'r> {
93 Request {
94 uri,
95 method: AtomicMethod::new(method),
96 headers: HeaderMap::new(),
97 version,
98 errors: Vec::new(),
99 connection: ConnectionMeta::default(),
100 state: RequestState {
101 rocket,
102 route: OptionRefSwap::new(None),
103 cookies: CookieJar::new(None, rocket),
104 accept: InitCell::new(),
105 content_type: InitCell::new(),
106 cache: Arc::new(<TypeMap![Send + Sync]>::new()),
107 host: None,
108 }
109 }
110 }
111
112 /// Retrieve http protocol version, when applicable.
113 ///
114 /// # Example
115 ///
116 /// ```rust
117 /// use rocket::http::HttpVersion;
118 ///
119 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
120 /// # let mut req = c.get("/");
121 /// # req.override_version(HttpVersion::Http11);
122 /// assert_eq!(req.version(), Some(HttpVersion::Http11));
123 /// ```
124 pub fn version(&self) -> Option<HttpVersion> {
125 self.version
126 }
127
128 /// Retrieve the method from `self`.
129 ///
130 /// # Example
131 ///
132 /// ```rust
133 /// use rocket::http::Method;
134 ///
135 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
136 /// # let get = |uri| c.get(uri);
137 /// # let post = |uri| c.post(uri);
138 /// assert_eq!(get("/").method(), Method::Get);
139 /// assert_eq!(post("/").method(), Method::Post);
140 /// ```
141 #[inline(always)]
142 pub fn method(&self) -> Method {
143 self.method.load()
144 }
145
146 /// Set the method of `self` to `method`.
147 ///
148 /// # Example
149 ///
150 /// ```rust
151 /// use rocket::http::Method;
152 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
153 /// # let mut req = c.get("/");
154 /// # let request = req.inner_mut();
155 ///
156 /// assert_eq!(request.method(), Method::Get);
157 ///
158 /// request.set_method(Method::Post);
159 /// assert_eq!(request.method(), Method::Post);
160 /// ```
161 #[inline(always)]
162 pub fn set_method(&mut self, method: Method) {
163 self.method.set(method);
164 }
165
166 /// Borrow the [`Origin`] URI from `self`.
167 ///
168 /// # Example
169 ///
170 /// ```rust
171 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
172 /// # let get = |uri| c.get(uri);
173 /// assert_eq!(get("/hello/rocketeer").uri().path(), "/hello/rocketeer");
174 /// assert_eq!(get("/hello").uri().query(), None);
175 /// ```
176 #[inline(always)]
177 pub fn uri(&self) -> &Origin<'r> {
178 &self.uri
179 }
180
181 /// Set the URI in `self` to `uri`.
182 ///
183 /// # Example
184 ///
185 /// ```rust
186 /// use rocket::http::uri::Origin;
187 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
188 /// # let mut req = c.get("/");
189 /// # let request = req.inner_mut();
190 ///
191 /// let uri = Origin::parse("/hello/Sergio?type=greeting").unwrap();
192 /// request.set_uri(uri);
193 /// assert_eq!(request.uri().path(), "/hello/Sergio");
194 /// assert_eq!(request.uri().query().unwrap(), "type=greeting");
195 ///
196 /// let new_uri = request.uri().map_path(|p| format!("/foo{}", p)).unwrap();
197 /// request.set_uri(new_uri);
198 /// assert_eq!(request.uri().path(), "/foo/hello/Sergio");
199 /// assert_eq!(request.uri().query().unwrap(), "type=greeting");
200 /// ```
201 #[inline(always)]
202 pub fn set_uri(&mut self, uri: Origin<'r>) {
203 self.uri = uri;
204 }
205
206 /// Returns the [`Host`] identified in the request, if any.
207 ///
208 /// If the request is made via HTTP/1.1 (or earlier), this method returns
209 /// the value in the `HOST` header without the deprecated `user_info`
210 /// component. Otherwise, this method returns the contents of the
211 /// `:authority` pseudo-header request field.
212 ///
213 /// Note that this method _only_ reflects the `HOST` header in the _initial_
214 /// request and not any changes made thereafter. To change the value
215 /// returned by this method, use [`Request::set_host()`].
216 ///
217 /// # ⚠️ DANGER ⚠️
218 ///
219 /// Using the user-controlled `host` to construct URLs is a security hazard!
220 /// _Never_ do so without first validating the host against a whitelist. For
221 /// this reason, Rocket disallows constructing host-prefixed URIs with
222 /// [`uri!`]. _Always_ use [`uri!`] to construct URIs.
223 ///
224 /// [`uri!`]: crate::uri!
225 ///
226 /// # Example
227 ///
228 /// Retrieve the raw host, unusable to construct safe URIs:
229 ///
230 /// ```rust
231 /// use rocket::http::uri::Host;
232 /// # use rocket::uri;
233 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
234 /// # let mut req = c.get("/");
235 /// # let request = req.inner_mut();
236 ///
237 /// assert_eq!(request.host(), None);
238 ///
239 /// request.set_host(Host::from(uri!("rocket.rs")));
240 /// let host = request.host().unwrap();
241 /// assert_eq!(host.domain(), "rocket.rs");
242 /// assert_eq!(host.port(), None);
243 ///
244 /// request.set_host(Host::from(uri!("rocket.rs:2392")));
245 /// let host = request.host().unwrap();
246 /// assert_eq!(host.domain(), "rocket.rs");
247 /// assert_eq!(host.port(), Some(2392));
248 /// ```
249 ///
250 /// Retrieve the raw host, check it against a whitelist, and construct a
251 /// URI:
252 ///
253 /// ```rust
254 /// # #[macro_use] extern crate rocket;
255 /// # type Token = String;
256 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
257 /// # let mut req = c.get("/");
258 /// # let request = req.inner_mut();
259 /// use rocket::http::uri::Host;
260 ///
261 /// // A sensitive URI we want to prefix with safe hosts.
262 /// #[get("/token?<secret>")]
263 /// fn token(secret: Token) { /* .. */ }
264 ///
265 /// // Whitelist of known hosts. In a real setting, you might retrieve this
266 /// // list from config at ignite-time using tools like `AdHoc::config()`.
267 /// const WHITELIST: [Host<'static>; 3] = [
268 /// Host::new(uri!("rocket.rs")),
269 /// Host::new(uri!("rocket.rs:443")),
270 /// Host::new(uri!("guide.rocket.rs:443")),
271 /// ];
272 ///
273 /// // A request with a host of "rocket.rs". Note the case-insensitivity.
274 /// request.set_host(Host::from(uri!("ROCKET.rs")));
275 /// let prefix = request.host().and_then(|h| h.to_absolute("https", &WHITELIST));
276 ///
277 /// // `rocket.rs` is in the whitelist, so we'll get back a `Some`.
278 /// assert!(prefix.is_some());
279 /// if let Some(prefix) = prefix {
280 /// // We can use this prefix to safely construct URIs.
281 /// let uri = uri!(prefix, token("some-secret-token"));
282 /// assert_eq!(uri, "https://ROCKET.rs/token?secret=some-secret-token");
283 /// }
284 ///
285 /// // A request with a host of "attacker-controlled.com".
286 /// request.set_host(Host::from(uri!("attacker-controlled.com")));
287 /// let prefix = request.host().and_then(|h| h.to_absolute("https", &WHITELIST));
288 ///
289 /// // `attacker-controlled.come` is _not_ on the whitelist.
290 /// assert!(prefix.is_none());
291 /// assert!(request.host().is_some());
292 /// ```
293 #[inline(always)]
294 pub fn host(&self) -> Option<&Host<'r>> {
295 self.state.host.as_ref()
296 }
297
298 /// Sets the host of `self` to `host`.
299 ///
300 /// # Example
301 ///
302 /// Set the host to `rocket.rs:443`.
303 ///
304 /// ```rust
305 /// use rocket::http::uri::Host;
306 /// # use rocket::uri;
307 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
308 /// # let mut req = c.get("/");
309 /// # let request = req.inner_mut();
310 ///
311 /// assert_eq!(request.host(), None);
312 ///
313 /// request.set_host(Host::from(uri!("rocket.rs:443")));
314 /// let host = request.host().unwrap();
315 /// assert_eq!(host.domain(), "rocket.rs");
316 /// assert_eq!(host.port(), Some(443));
317 /// ```
318 #[inline(always)]
319 pub fn set_host(&mut self, host: Host<'r>) {
320 self.state.host = Some(host);
321 }
322
323 /// Returns the raw address of the remote connection that initiated this
324 /// request if the address is known. If the address is not known, `None` is
325 /// returned.
326 ///
327 /// Because it is common for proxies to forward connections for clients, the
328 /// remote address may contain information about the proxy instead of the
329 /// client. For this reason, proxies typically set a "X-Real-IP" header
330 /// [`ip_header`](crate::Config::ip_header) with the client's true IP. To
331 /// extract this IP from the request, use the [`real_ip()`] or
332 /// [`client_ip()`] methods.
333 ///
334 /// [`real_ip()`]: #method.real_ip
335 /// [`client_ip()`]: #method.client_ip
336 ///
337 /// # Example
338 ///
339 /// ```rust
340 /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
341 /// use rocket::listener::Endpoint;
342 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
343 /// # let mut req = c.get("/");
344 /// # let request = req.inner_mut();
345 ///
346 /// assert_eq!(request.remote(), None);
347 ///
348 /// let localhost = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8111);
349 /// request.set_remote(Endpoint::Tcp(localhost));
350 /// assert_eq!(request.remote().unwrap().tcp().unwrap(), localhost);
351 /// ```
352 #[inline(always)]
353 pub fn remote(&self) -> Option<&Endpoint> {
354 self.connection.peer_endpoint.as_ref()
355 }
356
357 /// Sets the remote address of `self` to `address`.
358 ///
359 /// # Example
360 ///
361 /// Set the remote address to be 127.0.0.1:8111:
362 ///
363 /// ```rust
364 /// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
365 /// use rocket::listener::Endpoint;
366 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
367 /// # let mut req = c.get("/");
368 /// # let request = req.inner_mut();
369 ///
370 /// assert_eq!(request.remote(), None);
371 ///
372 /// let localhost = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8111);
373 /// request.set_remote(Endpoint::Tcp(localhost));
374 /// assert_eq!(request.remote().unwrap().tcp().unwrap(), localhost);
375 /// ```
376 #[inline(always)]
377 pub fn set_remote(&mut self, endpoint: Endpoint) {
378 self.connection.peer_endpoint = Some(endpoint);
379 }
380
381 /// Returns the IP address of the configured
382 /// [`ip_header`](crate::Config::ip_header) of the request if such a header
383 /// is configured, exists and contains a valid IP address.
384 ///
385 /// # Example
386 ///
387 /// ```rust
388 /// use std::net::Ipv4Addr;
389 /// use rocket::http::Header;
390 ///
391 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
392 /// # let req = c.get("/");
393 /// assert_eq!(req.real_ip(), None);
394 ///
395 /// // `ip_header` defaults to `X-Real-IP`.
396 /// let req = req.header(Header::new("X-Real-IP", "127.0.0.1"));
397 /// assert_eq!(req.real_ip(), Some(Ipv4Addr::LOCALHOST.into()));
398 /// ```
399 pub fn real_ip(&self) -> Option<IpAddr> {
400 let ip_header = self.rocket().config.ip_header.as_ref()?.as_str();
401 self.headers()
402 .get_one(ip_header)
403 .and_then(|ip| {
404 ip.parse()
405 .map_err(|_| warn!(value = ip, "'{ip_header}' header is malformed"))
406 .ok()
407 })
408 }
409
410 /// Returns the [`ProxyProto`] associated with the current request.
411 ///
412 /// The value is determined by inspecting the header named
413 /// [`proxy_proto_header`](crate::Config::proxy_proto_header), if
414 /// configured, and parsing it case-insensitivity. If the parameter isn't
415 /// configured or the request doesn't contain a header named as indicated,
416 /// this method returns `None`.
417 ///
418 /// # Example
419 ///
420 /// ```rust
421 /// use rocket::http::{Header, ProxyProto};
422 ///
423 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
424 /// # let req = c.get("/");
425 /// // By default, no `proxy_proto_header` is configured.
426 /// let req = req.header(Header::new("x-forwarded-proto", "https"));
427 /// assert_eq!(req.proxy_proto(), None);
428 ///
429 /// // We can configure one by setting the `proxy_proto_header` parameter.
430 /// // Here we set it to `x-forwarded-proto`, considered de-facto standard.
431 /// # let figment = rocket::figment::Figment::from(rocket::Config::debug_default());
432 /// let figment = figment.merge(("proxy_proto_header", "x-forwarded-proto"));
433 /// # let c = rocket::local::blocking::Client::debug(rocket::custom(figment)).unwrap();
434 /// # let req = c.get("/");
435 /// let req = req.header(Header::new("x-forwarded-proto", "https"));
436 /// assert_eq!(req.proxy_proto(), Some(ProxyProto::Https));
437 ///
438 /// # let req = c.get("/");
439 /// let req = req.header(Header::new("x-forwarded-proto", "HTTP"));
440 /// assert_eq!(req.proxy_proto(), Some(ProxyProto::Http));
441 ///
442 /// # let req = c.get("/");
443 /// let req = req.header(Header::new("x-forwarded-proto", "xproto"));
444 /// assert_eq!(req.proxy_proto(), Some(ProxyProto::Unknown("xproto".into())));
445 /// ```
446 pub fn proxy_proto(&self) -> Option<ProxyProto<'_>> {
447 self.rocket()
448 .config
449 .proxy_proto_header
450 .as_ref()
451 .and_then(|header| self.headers().get_one(header.as_str()))
452 .map(ProxyProto::from)
453 }
454
455 /// Returns whether we are *likely* in a secure context.
456 ///
457 /// A request is in a "secure context" if it was initially sent over a
458 /// secure (TLS, via HTTPS) connection. If TLS is configured and enabled,
459 /// then the request is guaranteed to be in a secure context. Otherwise, if
460 /// [`Request::proxy_proto()`] evaluates to `Https`, then we are _likely_ to
461 /// be in a secure context. We say _likely_ because it is entirely possible
462 /// for the header to indicate that the connection is being proxied via
463 /// HTTPS while reality differs. As such, this value should not be trusted
464 /// when 100% confidence is a necessity.
465 ///
466 /// # Example
467 ///
468 /// ```rust
469 /// use rocket::http::{Header, ProxyProto};
470 ///
471 /// # let client = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
472 /// # let req = client.get("/");
473 /// // If TLS and proxy_proto are disabled, we are not in a secure context.
474 /// assert_eq!(req.context_is_likely_secure(), false);
475 ///
476 /// // Configuring proxy_proto and receiving a header value of `https` is
477 /// // interpreted as likely being in a secure context.
478 /// // Here we set it to `x-forwarded-proto`, considered de-facto standard.
479 /// # let figment = rocket::figment::Figment::from(rocket::Config::debug_default());
480 /// let figment = figment.merge(("proxy_proto_header", "x-forwarded-proto"));
481 /// # let c = rocket::local::blocking::Client::debug(rocket::custom(figment)).unwrap();
482 /// # let req = c.get("/");
483 /// let req = req.header(Header::new("x-forwarded-proto", "https"));
484 /// assert_eq!(req.context_is_likely_secure(), true);
485 /// ```
486 pub fn context_is_likely_secure(&self) -> bool {
487 self.cookies().state.secure
488 }
489
490 /// Attempts to return the client's IP address by first inspecting the
491 /// [`ip_header`](crate::Config::ip_header) and then using the remote
492 /// connection's IP address. Note that the built-in `IpAddr` request guard
493 /// can be used to retrieve the same information in a handler:
494 ///
495 /// ```rust
496 /// # use rocket::get;
497 /// use std::net::IpAddr;
498 ///
499 /// #[get("/")]
500 /// fn get_ip(client_ip: IpAddr) { /* ... */ }
501 ///
502 /// #[get("/")]
503 /// fn try_get_ip(client_ip: Option<IpAddr>) { /* ... */ }
504 /// ````
505 ///
506 /// If the `ip_header` exists and contains a valid IP address, that address
507 /// is returned. Otherwise, if the address of the remote connection is
508 /// known, that address is returned. Otherwise, `None` is returned.
509 ///
510 /// # Example
511 ///
512 /// ```rust
513 /// # use rocket::http::Header;
514 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
515 /// # let mut req = c.get("/");
516 /// # let request = req.inner_mut();
517 /// # use std::net::{SocketAddr, IpAddr, Ipv4Addr};
518 /// # use rocket::listener::Endpoint;
519 ///
520 /// // starting without an "X-Real-IP" header or remote address
521 /// assert!(request.client_ip().is_none());
522 ///
523 /// // add a remote address; this is done by Rocket automatically
524 /// let localhost_9190 = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9190);
525 /// request.set_remote(Endpoint::Tcp(localhost_9190));
526 /// assert_eq!(request.client_ip().unwrap(), Ipv4Addr::LOCALHOST);
527 ///
528 /// // now with an X-Real-IP header, the default value for `ip_header`.
529 /// request.add_header(Header::new("X-Real-IP", "8.8.8.8"));
530 /// assert_eq!(request.client_ip().unwrap(), Ipv4Addr::new(8, 8, 8, 8));
531 /// ```
532 #[inline]
533 pub fn client_ip(&self) -> Option<IpAddr> {
534 self.real_ip().or_else(|| self.remote()?.ip())
535 }
536
537 /// Returns a wrapped borrow to the cookies in `self`.
538 ///
539 /// [`CookieJar`] implements internal mutability, so this method allows you
540 /// to get _and_ add/remove cookies in `self`.
541 ///
542 /// # Example
543 ///
544 /// Add a new cookie to a request's cookies:
545 ///
546 /// ```rust
547 /// use rocket::http::Cookie;
548 ///
549 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
550 /// # let request = c.get("/");
551 /// # let req = request.inner();
552 /// req.cookies().add(("key", "val"));
553 /// req.cookies().add(("ans", format!("life: {}", 38 + 4)));
554 ///
555 /// assert_eq!(req.cookies().get_pending("key").unwrap().value(), "val");
556 /// assert_eq!(req.cookies().get_pending("ans").unwrap().value(), "life: 42");
557 /// ```
558 #[inline(always)]
559 pub fn cookies(&self) -> &CookieJar<'r> {
560 &self.state.cookies
561 }
562
563 /// Returns a [`HeaderMap`] of all of the headers in `self`.
564 ///
565 /// # Example
566 ///
567 /// ```rust
568 /// use rocket::http::{Accept, ContentType};
569 ///
570 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
571 /// # let get = |uri| c.get(uri);
572 /// assert!(get("/").headers().is_empty());
573 ///
574 /// let req = get("/").header(Accept::HTML).header(ContentType::HTML);
575 /// assert_eq!(req.headers().len(), 2);
576 /// ```
577 #[inline(always)]
578 pub fn headers(&self) -> &HeaderMap<'r> {
579 &self.headers
580 }
581
582 /// Add `header` to `self`'s headers. The type of `header` can be any type
583 /// that implements the `Into<Header>` trait. This includes common types
584 /// such as [`ContentType`] and [`Accept`].
585 ///
586 /// # Example
587 ///
588 /// ```rust
589 /// use rocket::http::ContentType;
590 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
591 /// # let mut req = c.get("/");
592 /// # let request = req.inner_mut();
593 ///
594 /// assert!(request.headers().is_empty());
595 ///
596 /// request.add_header(ContentType::HTML);
597 /// assert!(request.headers().contains("Content-Type"));
598 /// assert_eq!(request.headers().len(), 1);
599 /// ```
600 #[inline]
601 pub fn add_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
602 let header = header.into();
603 self.bust_header_cache(&header, false);
604 self.headers.add(header);
605 }
606
607 /// Replaces the value of the header with name `header.name` with
608 /// `header.value`. If no such header exists, `header` is added as a header
609 /// to `self`.
610 ///
611 /// # Example
612 ///
613 /// ```rust
614 /// use rocket::http::ContentType;
615 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
616 /// # let mut req = c.get("/");
617 /// # let request = req.inner_mut();
618 ///
619 /// assert!(request.headers().is_empty());
620 ///
621 /// request.add_header(ContentType::Any);
622 /// assert_eq!(request.headers().get_one("Content-Type"), Some("*/*"));
623 /// assert_eq!(request.content_type(), Some(&ContentType::Any));
624 ///
625 /// request.replace_header(ContentType::PNG);
626 /// assert_eq!(request.headers().get_one("Content-Type"), Some("image/png"));
627 /// assert_eq!(request.content_type(), Some(&ContentType::PNG));
628 /// ```
629 #[inline]
630 pub fn replace_header<'h: 'r, H: Into<Header<'h>>>(&mut self, header: H) {
631 let header = header.into();
632 self.bust_header_cache(&header, true);
633 self.headers.replace(header);
634 }
635
636 /// Returns the Content-Type header of `self`. If the header is not present,
637 /// returns `None`.
638 ///
639 /// # Example
640 ///
641 /// ```rust
642 /// use rocket::http::ContentType;
643 ///
644 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
645 /// # let get = |uri| c.get(uri);
646 /// assert_eq!(get("/").content_type(), None);
647 ///
648 /// let req = get("/").header(ContentType::JSON);
649 /// assert_eq!(req.content_type(), Some(&ContentType::JSON));
650 /// ```
651 #[inline]
652 pub fn content_type(&self) -> Option<&ContentType> {
653 self.state.content_type
654 .get_or_init(|| self.headers().get_one("Content-Type").and_then(|v| v.parse().ok()))
655 .as_ref()
656 }
657
658 /// Returns the Accept header of `self`. If the header is not present,
659 /// returns `None`.
660 ///
661 /// # Example
662 ///
663 /// ```rust
664 /// use rocket::http::Accept;
665 ///
666 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
667 /// # let get = |uri| c.get(uri);
668 /// assert_eq!(get("/").accept(), None);
669 /// assert_eq!(get("/").header(Accept::JSON).accept(), Some(&Accept::JSON));
670 /// ```
671 #[inline]
672 pub fn accept(&self) -> Option<&Accept> {
673 self.state.accept
674 .get_or_init(|| self.headers().get_one("Accept").and_then(|v| v.parse().ok()))
675 .as_ref()
676 }
677
678 /// Returns the media type "format" of the request.
679 ///
680 /// The returned `MediaType` is derived from either the `Content-Type` or
681 /// the `Accept` header of the request, based on whether the request's
682 /// method allows a body (see [`Method::allows_request_body()`]). The table
683 /// below summarized this:
684 ///
685 /// | Method Allows Body | Returned Format |
686 /// |--------------------|---------------------------------|
687 /// | Always | `Option<ContentType>` |
688 /// | Maybe or Never | `Some(Preferred Accept or Any)` |
689 ///
690 /// In short, if the request's method indicates support for a payload, the
691 /// request's `Content-Type` header value, if any, is returned. Otherwise
692 /// the [preferred](Accept::preferred()) `Accept` header value is returned,
693 /// or if none is present, [`Accept::Any`].
694 ///
695 /// The media type returned from this method is used to match against the
696 /// `format` route attribute.
697 ///
698 /// # Example
699 ///
700 /// ```rust
701 /// use rocket::http::{Accept, ContentType, MediaType};
702 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
703 /// # let get = |uri| c.get(uri);
704 /// # let post = |uri| c.post(uri);
705 ///
706 /// // Non-payload-bearing: format is accept header.
707 /// let req = get("/").header(Accept::HTML);
708 /// assert_eq!(req.format(), Some(&MediaType::HTML));
709 ///
710 /// let req = get("/").header(ContentType::JSON).header(Accept::HTML);
711 /// assert_eq!(req.format(), Some(&MediaType::HTML));
712 ///
713 /// // Payload: format is content-type header.
714 /// let req = post("/").header(ContentType::HTML);
715 /// assert_eq!(req.format(), Some(&MediaType::HTML));
716 ///
717 /// let req = post("/").header(ContentType::JSON).header(Accept::HTML);
718 /// assert_eq!(req.format(), Some(&MediaType::JSON));
719 ///
720 /// // Non-payload-bearing method and no accept header: `Any`.
721 /// assert_eq!(get("/").format(), Some(&MediaType::Any));
722 /// ```
723 pub fn format(&self) -> Option<&MediaType> {
724 static ANY: MediaType = MediaType::Any;
725 if self.method().allows_request_body().unwrap_or(false) {
726 self.content_type().map(|ct| ct.media_type())
727 } else {
728 // TODO: Should we be using `accept_first` or `preferred`? Or
729 // should we be checking neither and instead pass things through
730 // where the client accepts the thing at all?
731 self.accept()
732 .map(|accept| accept.preferred().media_type())
733 .or(Some(&ANY))
734 }
735 }
736
737 /// Returns the [`Rocket`] instance that is handling this request.
738 ///
739 /// # Example
740 ///
741 /// ```rust
742 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
743 /// # let request = c.get("/");
744 /// # type Pool = usize;
745 /// // Retrieve the application config via `Rocket::config()`.
746 /// let config = request.rocket().config();
747 ///
748 /// // Retrieve managed state via `Rocket::state()`.
749 /// let state = request.rocket().state::<Pool>();
750 ///
751 /// // Get a list of all of the registered routes and catchers.
752 /// let routes = request.rocket().routes();
753 /// let catchers = request.rocket().catchers();
754 /// ```
755 #[inline(always)]
756 pub fn rocket(&self) -> &'r Rocket<Orbit> {
757 self.state.rocket
758 }
759
760 /// Returns the configured application data limits.
761 ///
762 /// This is convenience function equivalent to:
763 ///
764 /// ```rust
765 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
766 /// # let request = c.get("/");
767 /// &request.rocket().config().limits
768 /// # ;
769 /// ```
770 ///
771 /// # Example
772 ///
773 /// ```rust
774 /// use rocket::data::ToByteUnit;
775 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
776 /// # let request = c.get("/");
777 ///
778 /// // This is the default `form` limit.
779 /// assert_eq!(request.limits().get("form"), Some(32.kibibytes()));
780 ///
781 /// // Retrieve the limit for files with extension `.pdf`; etails to 1MiB.
782 /// assert_eq!(request.limits().get("file/pdf"), Some(1.mebibytes()));
783 /// ```
784 #[inline(always)]
785 pub fn limits(&self) -> &'r Limits {
786 &self.rocket().config().limits
787 }
788
789 /// Get the presently matched route, if any.
790 ///
791 /// This method returns `Some` any time a handler or its guards are being
792 /// invoked. This method returns `None` _before_ routing has commenced; this
793 /// includes during request fairing callbacks.
794 ///
795 /// # Example
796 ///
797 /// ```rust
798 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
799 /// # let request = c.get("/");
800 /// let route = request.route();
801 /// ```
802 #[inline(always)]
803 pub fn route(&self) -> Option<&'r Route> {
804 self.state.route.load(Ordering::Acquire)
805 }
806
807 /// Invokes the request guard implementation for `T`, returning its outcome.
808 ///
809 /// # Example
810 ///
811 /// Assuming a `User` request guard exists, invoke it:
812 ///
813 /// ```rust
814 /// # type User = rocket::http::Method;
815 /// # rocket::async_test(async move {
816 /// # let c = rocket::local::asynchronous::Client::debug_with(vec![]).await.unwrap();
817 /// # let request = c.get("/");
818 /// let outcome = request.guard::<User>().await;
819 /// # })
820 /// ```
821 #[inline(always)]
822 pub fn guard<'z, 'a, T>(&'a self) -> BoxFuture<'z, Outcome<T, T::Error>>
823 where T: FromRequest<'a> + 'z, 'a: 'z, 'r: 'z
824 {
825 T::from_request(self)
826 }
827
828 /// Retrieves the cached value for type `T` from the request-local cached
829 /// state of `self`. If no such value has previously been cached for this
830 /// request, `f` is called to produce the value which is subsequently
831 /// returned.
832 ///
833 /// Different values of the same type _cannot_ be cached without using a
834 /// proxy, wrapper type. To avoid the need to write these manually, or for
835 /// libraries wishing to store values of public types, use the
836 /// [`local_cache!`](crate::request::local_cache) or
837 /// [`local_cache_once!`](crate::request::local_cache_once) macros to
838 /// generate a locally anonymous wrapper type, store, and retrieve the
839 /// wrapped value from request-local cache.
840 ///
841 /// # Example
842 ///
843 /// ```rust
844 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
845 /// # let request = c.get("/");
846 /// // The first store into local cache for a given type wins.
847 /// let value = request.local_cache(|| "hello");
848 /// assert_eq!(*request.local_cache(|| "hello"), "hello");
849 ///
850 /// // The following return the cached, previously stored value for the type.
851 /// assert_eq!(*request.local_cache(|| "goodbye"), "hello");
852 /// ```
853 #[inline]
854 pub fn local_cache<T, F>(&self, f: F) -> &T
855 where F: FnOnce() -> T,
856 T: Send + Sync + 'static
857 {
858 self.state.cache.try_get()
859 .unwrap_or_else(|| {
860 self.state.cache.set(f());
861 self.state.cache.get()
862 })
863 }
864
865 /// Retrieves the cached value for type `T` from the request-local cached
866 /// state of `self`. If no such value has previously been cached for this
867 /// request, `fut` is `await`ed to produce the value which is subsequently
868 /// returned.
869 ///
870 /// # Example
871 ///
872 /// ```rust
873 /// # use rocket::Request;
874 /// # type User = ();
875 /// async fn current_user<'r>(request: &Request<'r>) -> User {
876 /// // validate request for a given user, load from database, etc
877 /// }
878 ///
879 /// # rocket::async_test(async move {
880 /// # let c = rocket::local::asynchronous::Client::debug_with(vec![]).await.unwrap();
881 /// # let request = c.get("/");
882 /// let current_user = request.local_cache_async(async {
883 /// current_user(&request).await
884 /// }).await;
885 /// # })
886 /// ```
887 #[inline]
888 pub async fn local_cache_async<'a, T, F>(&'a self, fut: F) -> &'a T
889 where F: Future<Output = T>,
890 T: Send + Sync + 'static
891 {
892 match self.state.cache.try_get() {
893 Some(s) => s,
894 None => {
895 self.state.cache.set(fut.await);
896 self.state.cache.get()
897 }
898 }
899 }
900
901 /// Retrieves and parses into `T` the 0-indexed `n`th non-empty segment from
902 /// the _routed_ request, that is, the `n`th segment _after_ the mount
903 /// point. If the request has not been routed, then this is simply the `n`th
904 /// non-empty request URI segment.
905 ///
906 /// Returns `None` if `n` is greater than the number of non-empty segments.
907 /// Returns `Some(Err(T::Error))` if the parameter type `T` failed to be
908 /// parsed from the `n`th dynamic parameter.
909 ///
910 /// This method exists only to be used by manual routing. To retrieve
911 /// parameters from a request, use Rocket's code generation facilities.
912 ///
913 /// # Example
914 ///
915 /// ```rust
916 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
917 /// # let get = |uri| c.get(uri);
918 /// use rocket::error::Empty;
919 ///
920 /// assert_eq!(get("/a/b/c").param(0), Some(Ok("a")));
921 /// assert_eq!(get("/a/b/c").param(1), Some(Ok("b")));
922 /// assert_eq!(get("/a/b/c").param(2), Some(Ok("c")));
923 /// assert_eq!(get("/a/b/c").param::<&str>(3), None);
924 ///
925 /// assert_eq!(get("/1/b/3").param(0), Some(Ok(1)));
926 /// assert!(get("/1/b/3").param::<usize>(1).unwrap().is_err());
927 /// assert_eq!(get("/1/b/3").param(2), Some(Ok(3)));
928 ///
929 /// assert_eq!(get("/").param::<&str>(0), Some(Err(Empty)));
930 /// ```
931 #[inline]
932 pub fn param<'a, T>(&'a self, n: usize) -> Option<Result<T, T::Error>>
933 where T: FromParam<'a>
934 {
935 self.routed_segment(n).map(T::from_param)
936 }
937
938 /// Retrieves and parses into `T` all of the path segments in the request
939 /// URI beginning and including the 0-indexed `n`th non-empty segment
940 /// _after_ the mount point.,that is, the `n`th segment _after_ the mount
941 /// point. If the request has not been routed, then this is simply the `n`th
942 /// non-empty request URI segment.
943 ///
944 /// `T` must implement [`FromSegments`], which is used to parse the
945 /// segments. If there are no non-empty segments, the `Segments` iterator
946 /// will be empty.
947 ///
948 /// This method exists only to be used by manual routing. To retrieve
949 /// segments from a request, use Rocket's code generation facilities.
950 ///
951 /// # Example
952 ///
953 /// ```rust
954 /// use std::path::PathBuf;
955 ///
956 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
957 /// # let get = |uri| c.get(uri);
958 /// assert_eq!(get("/").segments(0..), Ok(PathBuf::new()));
959 /// assert_eq!(get("/").segments(2..), Ok(PathBuf::new()));
960 ///
961 /// // Empty segments are skipped.
962 /// assert_eq!(get("///").segments(2..), Ok(PathBuf::new()));
963 /// assert_eq!(get("/a/b/c").segments(0..), Ok(PathBuf::from("a/b/c")));
964 /// assert_eq!(get("/a/b/c").segments(1..), Ok(PathBuf::from("b/c")));
965 /// assert_eq!(get("/a/b/c").segments(2..), Ok(PathBuf::from("c")));
966 /// assert_eq!(get("/a/b/c").segments(3..), Ok(PathBuf::new()));
967 /// assert_eq!(get("/a/b/c").segments(4..), Ok(PathBuf::new()));
968 /// ```
969 #[inline]
970 pub fn segments<'a, T>(&'a self, n: RangeFrom<usize>) -> Result<T, T::Error>
971 where T: FromSegments<'a>
972 {
973 T::from_segments(self.routed_segments(n))
974 }
975
976 /// Retrieves and parses into `T` the query value with field name `name`.
977 /// `T` must implement [`FromForm`], which is used to parse the query's
978 /// value. Key matching is performed case-sensitively.
979 ///
980 /// # Warning
981 ///
982 /// This method exists _only_ to be used by manual routing and should
983 /// _never_ be used in a regular Rocket application. It is much more
984 /// expensive to use this method than to retrieve query parameters via
985 /// Rocket's codegen. To retrieve query values from a request, _always_
986 /// prefer to use Rocket's code generation facilities.
987 ///
988 /// # Error
989 ///
990 /// If a query segment with name `name` isn't present, returns `None`. If
991 /// parsing the value fails, returns `Some(Err(_))`.
992 ///
993 /// # Example
994 ///
995 /// ```rust
996 /// use rocket::form::FromForm;
997 ///
998 /// #[derive(Debug, PartialEq, FromForm)]
999 /// struct Dog<'r> {
1000 /// name: &'r str,
1001 /// age: usize
1002 /// }
1003 ///
1004 /// # let c = rocket::local::blocking::Client::debug_with(vec![]).unwrap();
1005 /// # let get = |uri| c.get(uri);
1006 /// let req = get("/?a=apple&z=zebra&a=aardvark");
1007 /// assert_eq!(req.query_value::<&str>("a").unwrap(), Ok("apple"));
1008 /// assert_eq!(req.query_value::<&str>("z").unwrap(), Ok("zebra"));
1009 /// assert_eq!(req.query_value::<&str>("b"), None);
1010 ///
1011 /// let a_seq = req.query_value::<Vec<&str>>("a");
1012 /// assert_eq!(a_seq.unwrap().unwrap(), ["apple", "aardvark"]);
1013 ///
1014 /// let req = get("/?dog.name=Max+Fido&dog.age=3");
1015 /// let dog = req.query_value::<Dog>("dog");
1016 /// assert_eq!(dog.unwrap().unwrap(), Dog { name: "Max Fido", age: 3 });
1017 /// ```
1018 #[inline]
1019 pub fn query_value<'a, T>(&'a self, name: &str) -> Option<form::Result<'a, T>>
1020 where T: FromForm<'a>
1021 {
1022 if !self.query_fields().any(|f| f.name == name) {
1023 return None;
1024 }
1025
1026 let mut ctxt = T::init(form::Options::Lenient);
1027
1028 self.query_fields()
1029 .filter(|f| f.name == name)
1030 .for_each(|f| T::push_value(&mut ctxt, f.shift()));
1031
1032 Some(T::finalize(ctxt))
1033 }
1034}
1035
1036// All of these methods only exist for internal, including codegen, purposes.
1037// They _are not_ part of the stable API. Please, don't use these.
1038#[doc(hidden)]
1039impl<'r> Request<'r> {
1040 /// Resets the cached value (if any) for the header with name `name`.
1041 fn bust_header_cache(&mut self, header: &Header<'_>, replace: bool) {
1042 if header.name() == "Content-Type" {
1043 if self.content_type().is_none() || replace {
1044 self.state.content_type = InitCell::new();
1045 }
1046 } else if header.name() == "Accept" {
1047 if self.accept().is_none() || replace {
1048 self.state.accept = InitCell::new();
1049 }
1050 } else if Some(header.name()) == self.rocket().config.proxy_proto_header.as_deref() {
1051 if !self.cookies().state.secure || replace {
1052 self.cookies_mut().state.secure |= ProxyProto::from(header.value()).is_https();
1053 }
1054 }
1055 }
1056
1057 /// Get the `n`th non-empty path segment, 0-indexed, after the mount point
1058 /// for the currently matched route, as a string, if it exists. Used by
1059 /// codegen.
1060 #[inline]
1061 pub fn routed_segment(&self, n: usize) -> Option<&str> {
1062 self.routed_segments(0..).get(n)
1063 }
1064
1065 /// Get the segments beginning at the `range`, 0-indexed, after the mount
1066 /// point for the currently matched route, if they exist. Used by codegen.
1067 #[inline]
1068 pub fn routed_segments(&self, range: RangeFrom<usize>) -> Segments<'_, Path> {
1069 let mount_segments = self.route()
1070 .map(|r| r.uri.metadata.base_len)
1071 .unwrap_or(0);
1072
1073 trace!(name: "segments", mount_segments, range.start);
1074 self.uri().path().segments().skip(mount_segments + range.start)
1075 }
1076
1077 // Retrieves the pre-parsed query items. Used by matching and codegen.
1078 #[inline]
1079 pub fn query_fields(&self) -> impl Iterator<Item = ValueField<'_>> {
1080 self.uri().query()
1081 .map(|q| q.segments().map(ValueField::from))
1082 .into_iter()
1083 .flatten()
1084 }
1085
1086 /// Set `self`'s parameters given that the route used to reach this request
1087 /// was `route`. Use during routing when attempting a given route.
1088 #[inline(always)]
1089 pub(crate) fn set_route(&self, route: &'r Route) {
1090 self.state.route.store(Some(route), Ordering::Release)
1091 }
1092
1093 #[inline(always)]
1094 pub(crate) fn _set_method(&self, method: Method) {
1095 self.method.store(method)
1096 }
1097
1098 pub(crate) fn cookies_mut(&mut self) -> &mut CookieJar<'r> {
1099 &mut self.state.cookies
1100 }
1101
1102 /// Convert from Hyper types into a Rocket Request.
1103 pub(crate) fn from_hyp(
1104 rocket: &'r Rocket<Orbit>,
1105 hyper: &'r hyper::http::request::Parts,
1106 connection: ConnectionMeta,
1107 ) -> Result<Request<'r>, Request<'r>> {
1108 // Keep track of parsing errors; emit a `BadRequest` if any exist.
1109 let mut errors = vec![];
1110
1111 // Ensure that the method is known.
1112 let method = match hyper.method {
1113 hyper::Method::GET => Method::Get,
1114 hyper::Method::PUT => Method::Put,
1115 hyper::Method::POST => Method::Post,
1116 hyper::Method::DELETE => Method::Delete,
1117 hyper::Method::OPTIONS => Method::Options,
1118 hyper::Method::HEAD => Method::Head,
1119 hyper::Method::TRACE => Method::Trace,
1120 hyper::Method::CONNECT => Method::Connect,
1121 hyper::Method::PATCH => Method::Patch,
1122 ref ext => Method::from_str(ext.as_str()).unwrap_or_else(|_| {
1123 errors.push(RequestError::BadMethod(hyper.method.clone()));
1124 Method::Get
1125 }),
1126 };
1127
1128 // TODO: Keep around not just the path/query, but the rest, if there?
1129 let uri = hyper.uri.path_and_query()
1130 .map(|uri| {
1131 // In debug, make sure we agree with Hyper about URI validity.
1132 // If we disagree, log a warning but continue anyway; if this is
1133 // a security issue with Hyper, there isn't much we can do.
1134 #[cfg(debug_assertions)]
1135 if Origin::parse(uri.as_str()).is_err() {
1136 warn!(
1137 name: "uri_discord",
1138 %uri,
1139 "Hyper/Rocket URI validity discord: {uri}\n\
1140 Hyper believes the URI is valid while Rocket disagrees.\n\
1141 This is likely a Hyper bug with potential security implications.\n\
1142 Please report this warning to Rocket's GitHub issue tracker."
1143 )
1144 }
1145
1146 Origin::new(uri.path(), uri.query().map(Cow::Borrowed))
1147 })
1148 .unwrap_or_else(|| {
1149 errors.push(RequestError::InvalidUri(hyper.uri.clone()));
1150 Origin::root().clone()
1151 });
1152
1153 // Construct the request object; fill in metadata and headers next.
1154 let mut request = Request::new(rocket, method, uri, match hyper.version {
1155 Version::HTTP_09 => Some(HttpVersion::Http09),
1156 Version::HTTP_10 => Some(HttpVersion::Http10),
1157 Version::HTTP_11 => Some(HttpVersion::Http11),
1158 Version::HTTP_2 => Some(HttpVersion::Http2),
1159 Version::HTTP_3 => Some(HttpVersion::Http3),
1160 _ => None,
1161 });
1162 request.errors = errors;
1163
1164 // Set the passed in connection metadata.
1165 request.connection = connection;
1166
1167 // Determine + set host. On HTTP < 2, use the `HOST` header. Otherwise,
1168 // use the `:authority` pseudo-header which hyper makes part of the URI.
1169 // TODO: Use an `InitCell` to compute this later.
1170 request.state.host = if hyper.version < hyper::Version::HTTP_2 {
1171 hyper.headers.get("host").and_then(|h| Host::parse_bytes(h.as_bytes()).ok())
1172 } else {
1173 hyper.uri.host().map(|h| Host::new(Authority::new(None, h, hyper.uri.port_u16())))
1174 };
1175
1176 // Set the request cookies, if they exist.
1177 for header in hyper.headers.get_all("Cookie") {
1178 let Ok(raw_str) = std::str::from_utf8(header.as_bytes()) else {
1179 continue
1180 };
1181
1182 for cookie_str in raw_str.split(';').map(|s| s.trim()) {
1183 if let Ok(cookie) = Cookie::parse_encoded(cookie_str) {
1184 request.state.cookies.add_original(cookie.into_owned());
1185 }
1186 }
1187 }
1188
1189 // Set the rest of the headers. This is rather unfortunate and slow.
1190 for (header, value) in hyper.headers.iter() {
1191 // FIXME: This is rather unfortunate. Header values needn't be UTF8.
1192 let Ok(value) = std::str::from_utf8(value.as_bytes()) else {
1193 warn!(%header, "dropping header with invalid UTF-8");
1194 continue;
1195 };
1196
1197 request.add_header(Header::new(header.as_str(), value));
1198 }
1199
1200 match request.errors.is_empty() {
1201 true => Ok(request),
1202 false => Err(request),
1203 }
1204 }
1205}
1206
1207#[derive(Debug, Clone)]
1208pub(crate) enum RequestError {
1209 InvalidUri(hyper::Uri),
1210 BadMethod(hyper::Method),
1211}
1212
1213impl fmt::Display for RequestError {
1214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1215 match self {
1216 RequestError::InvalidUri(u) => write!(f, "invalid origin URI: {}", u),
1217 RequestError::BadMethod(m) => write!(f, "invalid or unrecognized method: {}", m),
1218 }
1219 }
1220}
1221
1222impl fmt::Debug for Request<'_> {
1223 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1224 fmt.debug_struct("Request")
1225 .field("method", &self.method())
1226 .field("uri", &self.uri())
1227 .field("headers", &self.headers())
1228 .field("remote", &self.remote())
1229 .field("cookies", &self.cookies())
1230 .finish()
1231 }
1232}