ferron/modules/
redirects.rs

1use 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          // Even more code rewritten from SVR.JS...
118          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}