ferron/modules/
redirects.rs1use std::error::Error;
2
3use crate::ferron_common::{
4 ErrorLogger, HyperResponse, RequestData, ResponseData, ServerConfig, ServerModule,
5 ServerModuleHandlers, SocketData,
6};
7use crate::ferron_common::{HyperUpgraded, WithRuntime};
8use async_trait::async_trait;
9use http_body_util::{BodyExt, Empty};
10use hyper::{header, Response, StatusCode, Uri};
11use hyper_tungstenite::HyperWebsocket;
12use tokio::runtime::Handle;
13
14struct RedirectsModule;
15
16pub fn server_module_init(
17) -> Result<Box<dyn ServerModule + Send + Sync>, Box<dyn Error + Send + Sync>> {
18 Ok(Box::new(RedirectsModule::new()))
19}
20
21impl RedirectsModule {
22 fn new() -> Self {
23 Self
24 }
25}
26
27impl ServerModule for RedirectsModule {
28 fn get_handlers(&self, handle: Handle) -> Box<dyn ServerModuleHandlers + Send> {
29 Box::new(RedirectsModuleHandlers { handle })
30 }
31}
32struct RedirectsModuleHandlers {
33 handle: Handle,
34}
35
36#[async_trait]
37impl ServerModuleHandlers for RedirectsModuleHandlers {
38 async fn request_handler(
39 &mut self,
40 request: RequestData,
41 config: &ServerConfig,
42 socket_data: &SocketData,
43 _error_logger: &ErrorLogger,
44 ) -> Result<ResponseData, Box<dyn Error + Send + Sync>> {
45 WithRuntime::new(self.handle.clone(), async move {
46 let hyper_request = request.get_hyper_request();
47
48 if config["secure"].as_bool() == Some(true)
49 && !socket_data.encrypted
50 && config["disableNonEncryptedServer"].as_bool() != Some(true)
51 && config["disableToHTTPSRedirect"].as_bool() != Some(true)
52 {
53 let host_header_option = hyper_request.headers().get(header::HOST);
54 let host_header = match host_header_option {
55 Some(header_data) => header_data.to_str()?,
56 None => {
57 return Ok(
58 ResponseData::builder(request)
59 .status(StatusCode::BAD_REQUEST)
60 .build(),
61 )
62 }
63 };
64
65 let path_and_query_option = hyper_request.uri().path_and_query();
66 let path_and_query = match path_and_query_option {
67 Some(path_and_query) => path_and_query.to_string(),
68 None => {
69 return Ok(
70 ResponseData::builder(request)
71 .status(StatusCode::BAD_REQUEST)
72 .build(),
73 )
74 }
75 };
76
77 let mut parts: Vec<&str> = host_header.split(':').collect();
78
79 if parts.len() > 1
80 && !(parts[0].starts_with('[')
81 && parts
82 .last()
83 .map(|part| part.ends_with(']'))
84 .unwrap_or(false))
85 {
86 parts.pop();
87 }
88
89 let host_name = parts.join(":");
90
91 let new_uri = Uri::builder()
92 .scheme("https")
93 .authority(match config["sport"].as_i64() {
94 None | Some(443) => host_name,
95 Some(port) => format!("{}:{}", host_name, port),
96 })
97 .path_and_query(path_and_query)
98 .build()?;
99
100 return Ok(
101 ResponseData::builder(request)
102 .response(
103 Response::builder()
104 .status(StatusCode::MOVED_PERMANENTLY)
105 .header(header::LOCATION, new_uri.to_string())
106 .body(Empty::new().map_err(|e| match e {}).boxed())?,
107 )
108 .build(),
109 );
110 }
111
112 let domain_yaml = &config["domain"];
113 let domain = domain_yaml.as_str();
114
115 if let Some(domain) = domain {
116 if config["wwwredirect"].as_bool() == Some(true) {
117 if let Some(host_header_value) = hyper_request.headers().get(header::HOST) {
119 let host_header = host_header_value.to_str()?;
120
121 let path_and_query_option = hyper_request.uri().path_and_query();
122 let path_and_query = match path_and_query_option {
123 Some(path_and_query) => path_and_query.to_string(),
124 None => {
125 return Ok(
126 ResponseData::builder(request)
127 .status(StatusCode::BAD_REQUEST)
128 .build(),
129 )
130 }
131 };
132
133 let mut parts: Vec<&str> = host_header.split(':').collect();
134 let mut host_port: Option<&str> = None;
135
136 if parts.len() > 1
137 && !(parts[0].starts_with('[')
138 && parts
139 .last()
140 .map(|part| part.ends_with(']'))
141 .unwrap_or(false))
142 {
143 host_port = parts.pop();
144 }
145
146 let host_name = parts.join(":");
147
148 if host_name == domain && !host_name.starts_with("www.") {
149 let new_uri = Uri::builder()
150 .scheme(match socket_data.encrypted {
151 true => "https",
152 false => "http",
153 })
154 .authority(match host_port {
155 Some(port) => format!("www.{}:{}", host_name, port),
156 None => host_name,
157 })
158 .path_and_query(path_and_query)
159 .build()?;
160
161 return Ok(
162 ResponseData::builder(request)
163 .response(
164 Response::builder()
165 .status(StatusCode::MOVED_PERMANENTLY)
166 .header(header::LOCATION, new_uri.to_string())
167 .body(Empty::new().map_err(|e| match e {}).boxed())?,
168 )
169 .build(),
170 );
171 }
172 }
173 }
174 }
175
176 Ok(ResponseData::builder(request).build())
177 })
178 .await
179 }
180
181 async fn proxy_request_handler(
182 &mut self,
183 request: RequestData,
184 config: &ServerConfig,
185 socket_data: &SocketData,
186 _error_logger: &ErrorLogger,
187 ) -> Result<ResponseData, Box<dyn Error + Send + Sync>> {
188 if config["secure"].as_bool() == Some(true)
189 && !socket_data.encrypted
190 && config["disableNonEncryptedServer"].as_bool() != Some(true)
191 && config["disableToHTTPSRedirect"].as_bool() != Some(true)
192 {
193 return Ok(
194 ResponseData::builder(request)
195 .status(StatusCode::NOT_IMPLEMENTED)
196 .build(),
197 );
198 }
199 Ok(ResponseData::builder(request).build())
200 }
201
202 async fn response_modifying_handler(
203 &mut self,
204 response: HyperResponse,
205 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>> {
206 Ok(response)
207 }
208
209 async fn proxy_response_modifying_handler(
210 &mut self,
211 response: HyperResponse,
212 ) -> Result<HyperResponse, Box<dyn Error + Send + Sync>> {
213 Ok(response)
214 }
215
216 async fn connect_proxy_request_handler(
217 &mut self,
218 _upgraded_request: HyperUpgraded,
219 _connect_address: &str,
220 _config: &ServerConfig,
221 _socket_data: &SocketData,
222 _error_logger: &ErrorLogger,
223 ) -> Result<(), Box<dyn Error + Send + Sync>> {
224 Ok(())
225 }
226
227 fn does_connect_proxy_requests(&mut self) -> bool {
228 false
229 }
230
231 async fn websocket_request_handler(
232 &mut self,
233 _websocket: HyperWebsocket,
234 _uri: &hyper::Uri,
235 _config: &ServerConfig,
236 _socket_data: &SocketData,
237 _error_logger: &ErrorLogger,
238 ) -> Result<(), Box<dyn Error + Send + Sync>> {
239 Ok(())
240 }
241
242 fn does_websocket_requests(&mut self, _config: &ServerConfig, _socket_data: &SocketData) -> bool {
243 false
244 }
245}