reinhardt_http/request/methods.rs
1use super::Request;
2
3impl Request {
4 /// Returns true if the request was made over HTTPS
5 ///
6 /// This can be determined either by:
7 /// 1. The actual connection being TLS (is_secure flag)
8 /// 2. X-Forwarded-Proto header indicating HTTPS (only from trusted proxies)
9 ///
10 /// # Examples
11 ///
12 /// ```
13 /// use reinhardt_http::{Request, TrustedProxies};
14 /// use hyper::Method;
15 /// use std::net::{SocketAddr, IpAddr, Ipv4Addr};
16 ///
17 /// // Direct HTTPS connection
18 /// let request = Request::builder()
19 /// .method(Method::GET)
20 /// .uri("/")
21 /// .secure(true)
22 /// .build()
23 /// .unwrap();
24 /// assert!(request.is_secure());
25 ///
26 /// // Behind trusted reverse proxy
27 /// let proxy_ip = IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1));
28 /// let mut headers = hyper::HeaderMap::new();
29 /// headers.insert("x-forwarded-proto", "https".parse().unwrap());
30 /// let request = Request::builder()
31 /// .method(Method::GET)
32 /// .uri("/")
33 /// .headers(headers)
34 /// .remote_addr(SocketAddr::new(proxy_ip, 8080))
35 /// .build()
36 /// .unwrap();
37 /// request.set_trusted_proxies(TrustedProxies::new(vec![proxy_ip]));
38 /// assert!(request.is_secure());
39 /// ```
40 pub fn is_secure(&self) -> bool {
41 if self.is_secure {
42 return true;
43 }
44
45 // Only trust X-Forwarded-Proto header from configured trusted proxies
46 if self.is_from_trusted_proxy()
47 && let Some(proto) = self
48 .headers
49 .get("x-forwarded-proto")
50 .and_then(|h| h.to_str().ok())
51 {
52 return proto.eq_ignore_ascii_case("https");
53 }
54
55 false
56 }
57
58 /// Returns the scheme of the request (http or https)
59 ///
60 /// # Examples
61 ///
62 /// ```
63 /// use reinhardt_http::Request;
64 /// use hyper::{Method, Uri, Version, HeaderMap};
65 /// use bytes::Bytes;
66 ///
67 /// let request = Request::builder()
68 /// .method(Method::GET)
69 /// .uri("/")
70 /// .secure(true)
71 /// .build()
72 /// .unwrap();
73 /// assert_eq!(request.scheme(), "https");
74 ///
75 /// let request = Request::builder()
76 /// .method(Method::GET)
77 /// .uri("/")
78 /// .build()
79 /// .unwrap();
80 /// assert_eq!(request.scheme(), "http");
81 /// ```
82 pub fn scheme(&self) -> &str {
83 if self.is_secure() { "https" } else { "http" }
84 }
85
86 /// Build an absolute URI for the request
87 ///
88 /// Example: "<https://example.com:8000/path?query=value>"
89 ///
90 /// # Examples
91 ///
92 /// ```
93 /// use reinhardt_http::Request;
94 /// use hyper::{Method, Uri, Version, HeaderMap};
95 /// use bytes::Bytes;
96 ///
97 /// let mut headers = hyper::HeaderMap::new();
98 /// headers.insert("host", "example.com".parse().unwrap());
99 ///
100 /// let request = Request::builder()
101 /// .method(Method::GET)
102 /// .uri("/api/users")
103 /// .headers(headers)
104 /// .secure(true)
105 /// .build()
106 /// .unwrap();
107 ///
108 /// let uri = request.build_absolute_uri(None);
109 /// assert_eq!(uri, "https://example.com/api/users");
110 ///
111 /// let uri = request.build_absolute_uri(Some("/other/path"));
112 /// assert_eq!(uri, "https://example.com/other/path");
113 /// ```
114 pub fn build_absolute_uri(&self, path: Option<&str>) -> String {
115 let scheme = self.scheme();
116 let host = self.get_host().unwrap_or_else(|| "localhost".to_string());
117 let path = path.unwrap_or_else(|| self.path());
118
119 format!("{}://{}{}", scheme, host, path)
120 }
121
122 /// Get the host from the request headers
123 fn get_host(&self) -> Option<String> {
124 self.headers
125 .get(hyper::header::HOST)
126 .and_then(|h| h.to_str().ok())
127 .map(|s| s.to_string())
128 }
129}