|
|
|
@ -12,7 +12,7 @@ use serde::{Deserialize, Serialize}; |
|
|
|
|
|
|
|
|
|
use std::{collections, future, net, sync}; |
|
|
|
|
|
|
|
|
|
use crate::error::unexpected; |
|
|
|
|
use crate::error::unexpected::{self, Mappable}; |
|
|
|
|
use crate::{domain, service, task_stack}; |
|
|
|
|
|
|
|
|
|
#[derive(Serialize)] |
|
|
|
@ -157,6 +157,39 @@ impl Service { |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn https_redirect(&self, domain: domain::Name, req: Request<Body>) -> Response<Body> { |
|
|
|
|
let https_addr = self.config.http.https_addr.unwrap(); |
|
|
|
|
|
|
|
|
|
(|| { |
|
|
|
|
let mut uri_parts = http::uri::Parts::default(); |
|
|
|
|
uri_parts.scheme = Some(http::uri::Scheme::HTTPS); |
|
|
|
|
uri_parts.authority = Some( |
|
|
|
|
http::uri::Authority::from_maybe_shared(format!( |
|
|
|
|
"{}:{}", |
|
|
|
|
&domain, |
|
|
|
|
https_addr.port() |
|
|
|
|
)) |
|
|
|
|
.or_unexpected_while("constructing authority")?, |
|
|
|
|
); |
|
|
|
|
uri_parts.path_and_query = req.uri().path_and_query().cloned(); |
|
|
|
|
|
|
|
|
|
let uri: http::uri::Uri = uri_parts |
|
|
|
|
.try_into() |
|
|
|
|
.or_unexpected_while("constructing new URI")?; |
|
|
|
|
|
|
|
|
|
Response::builder() |
|
|
|
|
.status(http::status::StatusCode::PERMANENT_REDIRECT) |
|
|
|
|
.header("Location", uri.to_string()) |
|
|
|
|
.body(Body::empty()) |
|
|
|
|
.or_unexpected_while("building redirect") |
|
|
|
|
})() |
|
|
|
|
.unwrap_or_else(|err| { |
|
|
|
|
self.internal_error( |
|
|
|
|
format!("failed to redirect from {} to https: {}", req.uri(), err).as_str(), |
|
|
|
|
) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn render_page<T>(&self, name: &str, data: T) -> Response<Body> |
|
|
|
|
where |
|
|
|
|
T: Serialize, |
|
|
|
@ -504,51 +537,40 @@ impl Service { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If HTTPS is enabled then only .well-known endpoints are allowed over HTTP (because they
|
|
|
|
|
// require it). Otherwise we redirect all HTTP requests to HTTPS.
|
|
|
|
|
if self.config.http.https_addr.is_some() && !req_is_https { |
|
|
|
|
let https_addr = self.config.http.https_addr.unwrap(); |
|
|
|
|
// We only allow HTTP requests when HTTPS is enabled in specific cases:
|
|
|
|
|
// - /.well-known urls
|
|
|
|
|
// - proxied domains with https_disabled set on them
|
|
|
|
|
// everything else must use https if possible.
|
|
|
|
|
let https_upgradable = self.config.http.https_addr.is_some() && !req_is_https; |
|
|
|
|
|
|
|
|
|
let mut uri_parts = http::uri::Parts::default(); |
|
|
|
|
uri_parts.scheme = Some(http::uri::Scheme::HTTPS); |
|
|
|
|
uri_parts.authority = Some( |
|
|
|
|
http::uri::Authority::from_maybe_shared(format!( |
|
|
|
|
"{}:{}", |
|
|
|
|
&domain, |
|
|
|
|
https_addr.port() |
|
|
|
|
)) |
|
|
|
|
.unwrap(), |
|
|
|
|
); |
|
|
|
|
uri_parts.path_and_query = req.uri().path_and_query().cloned(); |
|
|
|
|
if let Some(config) = self.proxied_domains.get(&domain) { |
|
|
|
|
if config.http_url.is_none() { |
|
|
|
|
return self.render_error_page(404, "Domain not found"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let uri: http::uri::Uri = uri_parts.try_into().unwrap(); |
|
|
|
|
let http_url = config.http_url.as_ref().unwrap(); |
|
|
|
|
|
|
|
|
|
return Response::builder() |
|
|
|
|
.status(http::status::StatusCode::PERMANENT_REDIRECT) |
|
|
|
|
.header("Location", uri.to_string()) |
|
|
|
|
.body(Body::empty()) |
|
|
|
|
.unwrap(); |
|
|
|
|
} |
|
|
|
|
if https_upgradable && !config.https_disabled { |
|
|
|
|
return self.https_redirect(domain, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if let Some(config) = self.proxied_domains.get(&domain) { |
|
|
|
|
if let Some(ref http_url) = config.http_url { |
|
|
|
|
return service::http::proxy::serve_http_request( |
|
|
|
|
http_url.original_url.as_str(), |
|
|
|
|
&config.http_request_headers.0, |
|
|
|
|
client_ip, |
|
|
|
|
req, |
|
|
|
|
req_is_https, |
|
|
|
|
return service::http::proxy::serve_http_request( |
|
|
|
|
http_url.original_url.as_str(), |
|
|
|
|
&config.http_request_headers.0, |
|
|
|
|
client_ip, |
|
|
|
|
req, |
|
|
|
|
req_is_https, |
|
|
|
|
) |
|
|
|
|
.await |
|
|
|
|
.unwrap_or_else(|e| { |
|
|
|
|
self.internal_error( |
|
|
|
|
format!("serving {domain} via proxy {}: {e}", http_url.original_url).as_str(), |
|
|
|
|
) |
|
|
|
|
.await |
|
|
|
|
.unwrap_or_else(|e| { |
|
|
|
|
self.internal_error( |
|
|
|
|
format!("serving {domain} via proxy {}: {e}", http_url.original_url) |
|
|
|
|
.as_str(), |
|
|
|
|
) |
|
|
|
|
}); |
|
|
|
|
} else { |
|
|
|
|
return self.render_error_page(404, "Domain not found"); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if https_upgradable { |
|
|
|
|
return self.https_redirect(domain, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if Some(&domain) == self.interface_domain.as_ref() { |
|
|
|
|