|
|
@ -10,7 +10,6 @@ use http::request::Parts; |
|
|
|
use hyper::{Body, Method, Request, Response}; |
|
|
|
use hyper::{Body, Method, Request, Response}; |
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
|
|
|
|
|
|
|
|
use std::str::FromStr; |
|
|
|
|
|
|
|
use std::{future, net, sync}; |
|
|
|
use std::{future, net, sync}; |
|
|
|
|
|
|
|
|
|
|
|
use crate::error::unexpected; |
|
|
|
use crate::error::unexpected; |
|
|
@ -164,7 +163,7 @@ impl Service { |
|
|
|
match self.domain_manager.get_file(&domain, &path) { |
|
|
|
match self.domain_manager.get_file(&domain, &path) { |
|
|
|
Ok(f) => self.serve(200, &path, Body::wrap_stream(f)), |
|
|
|
Ok(f) => self.serve(200, &path, Body::wrap_stream(f)), |
|
|
|
Err(domain::manager::GetFileError::DomainNotFound) => { |
|
|
|
Err(domain::manager::GetFileError::DomainNotFound) => { |
|
|
|
return self.render_error_page(404, "Domain not found") |
|
|
|
return self.render_error_page(404, "Unknown domain name") |
|
|
|
} |
|
|
|
} |
|
|
|
Err(domain::manager::GetFileError::FileNotFound) => { |
|
|
|
Err(domain::manager::GetFileError::FileNotFound) => { |
|
|
|
self.render_error_page(404, "File not found") |
|
|
|
self.render_error_page(404, "File not found") |
|
|
@ -396,32 +395,75 @@ impl Service { |
|
|
|
self.render_page("/domains.html", Response { domains }) |
|
|
|
self.render_page("/domains.html", Response { domains }) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async fn serve_interface(&self, req: Request<Body>) -> Response<Body> { |
|
|
|
|
|
|
|
let (req, body) = req.into_parts(); |
|
|
|
|
|
|
|
let path = req.uri.path(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if req.method == Method::GET && path.starts_with("/static/") { |
|
|
|
|
|
|
|
return self.render(200, path, ()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let config_form_method = self.config.http.form_method.as_ref(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match (&req.method, path) { |
|
|
|
|
|
|
|
(&Method::GET, "/") | (&Method::GET, "/index.html") => { |
|
|
|
|
|
|
|
self.render_page("/index.html", ()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(form_method, "/domain.html") if form_method == config_form_method => { |
|
|
|
|
|
|
|
self.with_query_req(&req, body, |args: DomainArgs| async { self.domain(args) }) |
|
|
|
|
|
|
|
.await |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(form_method, "/domain_init.html") if form_method == config_form_method => { |
|
|
|
|
|
|
|
self.with_query_req(&req, body, |args: DomainInitArgs| async { |
|
|
|
|
|
|
|
self.domain_init(args) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.await |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(form_method, "/domain_sync.html") if form_method == config_form_method => { |
|
|
|
|
|
|
|
self.with_query_req(&req, body, |args: DomainSyncArgs| async { |
|
|
|
|
|
|
|
self.domain_sync(args).await |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.await |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(&Method::GET, "/domains.html") => self.domains(), |
|
|
|
|
|
|
|
_ => self.render_error_page( |
|
|
|
|
|
|
|
404, |
|
|
|
|
|
|
|
"This is not the page you're looking for. This page doesn't even exist!", |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn domain_from_req(req: &Request<Body>) -> Option<domain::Name> { |
|
|
|
|
|
|
|
let host_header = req |
|
|
|
|
|
|
|
.headers() |
|
|
|
|
|
|
|
.get("Host") |
|
|
|
|
|
|
|
.and_then(|v| v.to_str().ok()) |
|
|
|
|
|
|
|
.map(strip_port); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
host_header |
|
|
|
|
|
|
|
// if host_header isn't present, try the host from the URI
|
|
|
|
|
|
|
|
.or_else(|| req.uri().host().map(strip_port)) |
|
|
|
|
|
|
|
.and_then(|h| h.parse().ok()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async fn handle_request( |
|
|
|
async fn handle_request( |
|
|
|
&self, |
|
|
|
&self, |
|
|
|
client_ip: net::IpAddr, |
|
|
|
client_ip: net::IpAddr, |
|
|
|
req: Request<Body>, |
|
|
|
req: Request<Body>, |
|
|
|
req_is_https: bool, |
|
|
|
req_is_https: bool, |
|
|
|
) -> Response<Body> { |
|
|
|
) -> Response<Body> { |
|
|
|
let maybe_host = match ( |
|
|
|
let domain = match Self::domain_from_req(&req) { |
|
|
|
req.headers() |
|
|
|
Some(domain) => domain, |
|
|
|
.get("Host") |
|
|
|
None => return self.render_error_page(400, "Cannot serve page without domain"), |
|
|
|
.and_then(|v| v.to_str().ok()) |
|
|
|
}; |
|
|
|
.map(strip_port), |
|
|
|
|
|
|
|
req.uri().host().map(strip_port), |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
(Some(h), _) if h != self.config.primary_domain.as_str() => Some(h), |
|
|
|
|
|
|
|
(_, Some(h)) if h != self.config.primary_domain.as_str() => Some(h), |
|
|
|
|
|
|
|
_ => None, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
.and_then(|h| domain::Name::from_str(h).ok()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
let method = req.method(); |
|
|
|
let path = req.uri().path(); |
|
|
|
let path = req.uri().path(); |
|
|
|
|
|
|
|
|
|
|
|
// Serving acme challenges always takes priority. We serve them from the same store no
|
|
|
|
// Serving acme challenges always takes priority. We serve them from the same store no
|
|
|
|
// matter the domain, presumably they are cryptographically random enough that it doesn't
|
|
|
|
// matter the domain, presumably they are cryptographically random enough that it doesn't
|
|
|
|
// matter.
|
|
|
|
// matter.
|
|
|
|
if req.method() == Method::GET && path.starts_with("/.well-known/acme-challenge/") { |
|
|
|
if method == Method::GET && path.starts_with("/.well-known/acme-challenge/") { |
|
|
|
let token = path.trim_start_matches("/.well-known/acme-challenge/"); |
|
|
|
let token = path.trim_start_matches("/.well-known/acme-challenge/"); |
|
|
|
|
|
|
|
|
|
|
|
if let Ok(key) = self.domain_manager.get_acme_http01_challenge_key(token) { |
|
|
|
if let Ok(key) = self.domain_manager.get_acme_http01_challenge_key(token) { |
|
|
@ -430,11 +472,10 @@ impl Service { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Serving domani challenges similarly takes priority.
|
|
|
|
// Serving domani challenges similarly takes priority.
|
|
|
|
if req.method() == Method::GET && path == "/.well-known/domani-challenge" { |
|
|
|
if method == Method::GET && path == "/.well-known/domani-challenge" { |
|
|
|
if let Some(ref domain) = maybe_host { |
|
|
|
|
|
|
|
match self |
|
|
|
match self |
|
|
|
.domain_manager |
|
|
|
.domain_manager |
|
|
|
.get_domain_checker_challenge_token(domain) |
|
|
|
.get_domain_checker_challenge_token(&domain) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Ok(Some(token)) => return self.serve(200, "token.txt", token.into()), |
|
|
|
Ok(Some(token)) => return self.serve(200, "token.txt", token.into()), |
|
|
|
Ok(None) => return self.render_error_page(404, "Token not found"), |
|
|
|
Ok(None) => return self.render_error_page(404, "Token not found"), |
|
|
@ -445,11 +486,7 @@ impl Service { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If a managed domain was given then serve that from its origin or a proxy.
|
|
|
|
|
|
|
|
if let Some(domain) = maybe_host { |
|
|
|
|
|
|
|
if let Some(proxied_domain_config) = self.config.http.proxied_domains.get(&domain) { |
|
|
|
if let Some(proxied_domain_config) = self.config.http.proxied_domains.get(&domain) { |
|
|
|
return service::http::proxy::serve_http_request( |
|
|
|
return service::http::proxy::serve_http_request( |
|
|
|
proxied_domain_config, |
|
|
|
proxied_domain_config, |
|
|
@ -469,45 +506,11 @@ impl Service { |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return self.serve_origin(domain, req).await; |
|
|
|
if Some(&domain) == self.config.interface_domain.as_ref() { |
|
|
|
|
|
|
|
return self.serve_interface(req).await; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Serve main domani site
|
|
|
|
self.serve_origin(domain, req).await |
|
|
|
let (req, body) = req.into_parts(); |
|
|
|
|
|
|
|
let path = req.uri.path(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if req.method == Method::GET && path.starts_with("/static/") { |
|
|
|
|
|
|
|
return self.render(200, path, ()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let config_form_method = self.config.http.form_method.as_ref(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match (&req.method, path) { |
|
|
|
|
|
|
|
(&Method::GET, "/") | (&Method::GET, "/index.html") => { |
|
|
|
|
|
|
|
self.render_page("/index.html", ()) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(form_method, "/domain.html") if form_method == config_form_method => { |
|
|
|
|
|
|
|
self.with_query_req(&req, body, |args: DomainArgs| async { self.domain(args) }) |
|
|
|
|
|
|
|
.await |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(form_method, "/domain_init.html") if form_method == config_form_method => { |
|
|
|
|
|
|
|
self.with_query_req(&req, body, |args: DomainInitArgs| async { |
|
|
|
|
|
|
|
self.domain_init(args) |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.await |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(form_method, "/domain_sync.html") if form_method == config_form_method => { |
|
|
|
|
|
|
|
self.with_query_req(&req, body, |args: DomainSyncArgs| async { |
|
|
|
|
|
|
|
self.domain_sync(args).await |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
.await |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(&Method::GET, "/domains.html") => self.domains(), |
|
|
|
|
|
|
|
_ => self.render_error_page( |
|
|
|
|
|
|
|
404, |
|
|
|
|
|
|
|
"This is not the page you're looking for. This page doesn't even exist!", |
|
|
|
|
|
|
|
), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|