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