|
|
|
@ -10,7 +10,7 @@ use http::request::Parts; |
|
|
|
|
use hyper::{Body, Method, Request, Response}; |
|
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
|
|
|
|
|
|
use std::{collections, future, net, sync}; |
|
|
|
|
use std::{future, net, sync}; |
|
|
|
|
|
|
|
|
|
use crate::error::unexpected::{self, Mappable}; |
|
|
|
|
use crate::{domain, service, task_stack}; |
|
|
|
@ -50,8 +50,6 @@ pub struct Service { |
|
|
|
|
cert_resolver: sync::Arc<dyn rustls::server::ResolvesServerCert>, |
|
|
|
|
handlebars: handlebars::Handlebars<'static>, |
|
|
|
|
config: service::Config, |
|
|
|
|
proxied_domains: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>, |
|
|
|
|
interface_domain: Option<domain::Name>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Service { |
|
|
|
@ -60,8 +58,6 @@ impl Service { |
|
|
|
|
domain_manager: sync::Arc<dyn domain::manager::Manager>, |
|
|
|
|
cert_resolver: CertResolver, |
|
|
|
|
config: service::Config, |
|
|
|
|
proxied_domains: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>, |
|
|
|
|
interface_domain: Option<domain::Name>, |
|
|
|
|
) -> sync::Arc<Service> |
|
|
|
|
where |
|
|
|
|
CertResolver: rustls::server::ResolvesServerCert + 'static, |
|
|
|
@ -71,8 +67,6 @@ impl Service { |
|
|
|
|
cert_resolver: sync::Arc::from(cert_resolver), |
|
|
|
|
handlebars: tpl::get(), |
|
|
|
|
config, |
|
|
|
|
proxied_domains, |
|
|
|
|
interface_domain, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
task_stack.push_spawn(|canceller| tasks::listen_http(service.clone(), canceller)); |
|
|
|
@ -208,18 +202,21 @@ impl Service { |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async fn serve_origin(&self, domain: domain::Name, req: Request<Body>) -> Response<Body> { |
|
|
|
|
async fn serve_origin(&self, settings: domain::Settings, req: Request<Body>) -> Response<Body> { |
|
|
|
|
let path = service::append_index_to_path(req.uri().path(), "index.html"); |
|
|
|
|
|
|
|
|
|
match self.domain_manager.get_file(&domain, &path) { |
|
|
|
|
use domain::manager::GetFileError; |
|
|
|
|
match self.domain_manager.get_file(&settings, &path) { |
|
|
|
|
Ok(f) => self.serve(200, &path, Body::wrap_stream(f)), |
|
|
|
|
Err(domain::manager::GetFileError::DomainNotFound) => { |
|
|
|
|
self.render_error_page(404, "Unknown domain name") |
|
|
|
|
} |
|
|
|
|
Err(domain::manager::GetFileError::FileNotFound) => { |
|
|
|
|
self.render_error_page(404, "File not found") |
|
|
|
|
} |
|
|
|
|
Err(domain::manager::GetFileError::Unexpected(e)) => { |
|
|
|
|
Err(GetFileError::FileNotFound) => self.render_error_page(404, "File not found"), |
|
|
|
|
Err(GetFileError::DescrNotSynced) => self.internal_error( |
|
|
|
|
format!( |
|
|
|
|
"Backend for {:?} has not yet been synced", |
|
|
|
|
settings.origin_descr |
|
|
|
|
) |
|
|
|
|
.as_str(), |
|
|
|
|
), |
|
|
|
|
Err(GetFileError::Unexpected(e)) => { |
|
|
|
|
self.internal_error(format!("failed to fetch file {path}: {e}").as_str()) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -546,41 +543,56 @@ impl Service { |
|
|
|
|
// everything else must use https if possible.
|
|
|
|
|
let https_upgradable = self.https_enabled() && !req_is_https; |
|
|
|
|
|
|
|
|
|
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 settings = { |
|
|
|
|
use domain::manager::{GetSettingsError, GetSettingsResult}; |
|
|
|
|
match self.domain_manager.get_settings(&domain) { |
|
|
|
|
Ok(GetSettingsResult::Stored(settings)) => settings, |
|
|
|
|
Ok(GetSettingsResult::Builtin(config)) => config.settings, |
|
|
|
|
Ok(GetSettingsResult::Proxied(config)) => { |
|
|
|
|
if config.http_url.is_none() { |
|
|
|
|
return self.render_error_page(404, "Domain not found"); |
|
|
|
|
} else if https_upgradable && !config.https_disabled { |
|
|
|
|
return self.https_redirect(domain, req); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let http_url = config.http_url.as_ref().unwrap(); |
|
|
|
|
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(), |
|
|
|
|
) |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
Ok(GetSettingsResult::Interface) => { |
|
|
|
|
if https_upgradable { |
|
|
|
|
return self.https_redirect(domain, req); |
|
|
|
|
} |
|
|
|
|
return self.serve_interface(req).await; |
|
|
|
|
} |
|
|
|
|
Ok(GetSettingsResult::External(_)) => { |
|
|
|
|
return self.render_error_page(404, "Unknown domain name") |
|
|
|
|
} |
|
|
|
|
Err(GetSettingsError::NotFound) => { |
|
|
|
|
return self.render_error_page(404, "Unknown domain name") |
|
|
|
|
} |
|
|
|
|
Err(GetSettingsError::Unexpected(e)) => { |
|
|
|
|
return self.internal_error( |
|
|
|
|
format!("failed to fetch settings for domain {domain}: {e}").as_str(), |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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() { |
|
|
|
|
return self.serve_interface(req).await; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self.serve_origin(domain, req).await |
|
|
|
|
self.serve_origin(settings, req).await |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|