diff --git a/src/service/http.rs b/src/service/http.rs
index a96f9e8..bd7a570 100644
--- a/src/service/http.rs
+++ b/src/service/http.rs
@@ -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
) -> Response {
+ 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(&self, name: &str, data: T) -> Response
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();
-
- 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();
-
- let uri: http::uri::Uri = uri_parts.try_into().unwrap();
-
- return Response::builder()
- .status(http::status::StatusCode::PERMANENT_REDIRECT)
- .header("Location", uri.to_string())
- .body(Body::empty())
- .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;
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,
- )
- .await
- .unwrap_or_else(|e| {
- self.internal_error(
- format!("serving {domain} via proxy {}: {e}", http_url.original_url)
- .as_str(),
- )
- });
- } else {
+ if config.http_url.is_none() {
return self.render_error_page(404, "Domain not found");
}
+
+ let http_url = config.http_url.as_ref().unwrap();
+
+ if https_upgradable && !config.https_disabled {
+ return self.https_redirect(domain, req);
+ }
+
+ 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(),
+ )
+ });
+ }
+
+ if https_upgradable {
+ return self.https_redirect(domain, req);
}
if Some(&domain) == self.interface_domain.as_ref() {