Compare commits
2 Commits
4009f0da48
...
6a611c371c
Author | SHA1 | Date | |
---|---|---|---|
6a611c371c | |||
43502f82f9 |
21
flake.nix
21
flake.nix
@ -57,7 +57,7 @@
|
||||
{}
|
||||
supportedSystems;
|
||||
|
||||
# eachCrossSystem [system] (buildSystem: targetSystem: ...)
|
||||
# eachCrossSystem [system] ({buildSystem, targetSystem, isDefault }: ...)
|
||||
#
|
||||
# Returns an attrset with a key "$buildSystem.cross-$targetSystem" for
|
||||
# every combination of the elements of the array of system strings. The
|
||||
@ -72,13 +72,20 @@
|
||||
pkgs = mkPkgs buildSystem null;
|
||||
crosses = builtins.foldl'
|
||||
(inner: targetSystem: inner // {
|
||||
"cross-${targetSystem}" = callback buildSystem targetSystem;
|
||||
"cross-${targetSystem}" = callback {
|
||||
inherit buildSystem targetSystem;
|
||||
isDefault = false;
|
||||
};
|
||||
})
|
||||
{ default = callback buildSystem buildSystem; }
|
||||
{}
|
||||
supportedSystems;
|
||||
in
|
||||
crosses // (rec {
|
||||
default = callback buildSystem buildSystem;
|
||||
default = callback {
|
||||
inherit buildSystem;
|
||||
targetSystem = buildSystem;
|
||||
isDefault = true;
|
||||
};
|
||||
release = let
|
||||
bins = pkgs.symlinkJoin {
|
||||
name = "${default.name}-all-bins";
|
||||
@ -164,7 +171,7 @@
|
||||
|
||||
packages = eachCrossSystem
|
||||
(builtins.attrNames buildTargets)
|
||||
(buildSystem: targetSystem: let
|
||||
({ buildSystem, targetSystem, isDefault }: let
|
||||
pkgs = mkPkgs buildSystem null;
|
||||
toolchain = mkToolchain buildSystem targetSystem;
|
||||
naersk-lib = pkgs.callPackage naersk {
|
||||
@ -177,7 +184,7 @@
|
||||
strictDeps = true;
|
||||
doCheck = false;
|
||||
release = true;
|
||||
postInstall = ''
|
||||
postInstall = if isDefault then "" else ''
|
||||
cd "$out"/bin
|
||||
for f in "$(ls)"; do
|
||||
if ext="$(echo "$f" | grep -oP '\.[a-z]+$')"; then
|
||||
@ -193,7 +200,7 @@
|
||||
|
||||
devShells = eachCrossSystem
|
||||
(builtins.attrNames buildTargets)
|
||||
(buildSystem: targetSystem: let
|
||||
({ buildSystem, targetSystem, isDefault }: let
|
||||
pkgs = mkPkgs buildSystem null;
|
||||
toolchain = mkToolchain buildSystem targetSystem;
|
||||
in
|
||||
|
@ -30,38 +30,7 @@ impl From<store::GetError> for GetSettingsError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum GetFileError {
|
||||
#[error("domain not found")]
|
||||
DomainNotFound,
|
||||
|
||||
#[error("file not found")]
|
||||
FileNotFound,
|
||||
|
||||
#[error(transparent)]
|
||||
Unexpected(#[from] unexpected::Error),
|
||||
}
|
||||
|
||||
impl From<GetSettingsError> for GetFileError {
|
||||
fn from(e: GetSettingsError) -> Self {
|
||||
match e {
|
||||
GetSettingsError::NotFound => Self::DomainNotFound,
|
||||
GetSettingsError::Unexpected(e) => Self::Unexpected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<origin::GetFileError> for GetFileError {
|
||||
fn from(e: origin::GetFileError) -> Self {
|
||||
match e {
|
||||
origin::GetFileError::DescrNotSynced => {
|
||||
Self::Unexpected(unexpected::Error::from("origin descr not synced"))
|
||||
}
|
||||
origin::GetFileError::FileNotFound => Self::FileNotFound,
|
||||
origin::GetFileError::Unexpected(e) => Self::Unexpected(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub type GetFileError = origin::GetFileError;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum SyncWithSettingsError {
|
||||
@ -125,7 +94,7 @@ pub trait Manager: Sync + Send {
|
||||
|
||||
fn get_file(
|
||||
&self,
|
||||
domain: &domain::Name,
|
||||
settings: &domain::Settings,
|
||||
path: &str,
|
||||
) -> Result<util::BoxByteStream, GetFileError>;
|
||||
|
||||
@ -425,34 +394,12 @@ impl Manager for ManagerImpl {
|
||||
|
||||
fn get_file(
|
||||
&self,
|
||||
domain: &domain::Name,
|
||||
settings: &domain::Settings,
|
||||
path: &str,
|
||||
) -> Result<util::BoxByteStream, GetFileError> {
|
||||
let settings = match self.get_settings(domain)? {
|
||||
GetSettingsResult::Stored(settings) => settings,
|
||||
GetSettingsResult::Builtin(config) => config.settings,
|
||||
GetSettingsResult::Proxied(_) => {
|
||||
return Err(
|
||||
unexpected::Error::from("can't call get_file on proxied domain").into(),
|
||||
);
|
||||
}
|
||||
GetSettingsResult::Interface => {
|
||||
return Err(
|
||||
unexpected::Error::from("can't call get_file on interface domain").into(),
|
||||
);
|
||||
}
|
||||
GetSettingsResult::External(_) => {
|
||||
return Err(GetFileError::DomainNotFound);
|
||||
}
|
||||
};
|
||||
|
||||
let path = settings.process_path(path);
|
||||
|
||||
let f = self
|
||||
.origin_store
|
||||
.get_file(&settings.origin_descr, path.as_ref())?;
|
||||
|
||||
Ok(f)
|
||||
self.origin_store
|
||||
.get_file(&settings.origin_descr, path.as_ref())
|
||||
}
|
||||
|
||||
fn sync_with_settings(
|
||||
|
@ -176,8 +176,6 @@ async fn main() {
|
||||
domain_manager.clone(),
|
||||
domani::domain::manager::HttpsCertResolver::from(domain_manager.clone()),
|
||||
config.service.clone(),
|
||||
config.domain.proxied_domains.clone(),
|
||||
config.domain.interface_domain.clone(),
|
||||
);
|
||||
|
||||
if gemini_enabled {
|
||||
@ -186,7 +184,6 @@ async fn main() {
|
||||
domain_manager.clone(),
|
||||
domani::domain::manager::GeminiCertResolver::from(domain_manager.clone()),
|
||||
config.service.gemini.clone(),
|
||||
config.domain.proxied_domains.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6,14 +6,13 @@ pub use config::*;
|
||||
use crate::error::unexpected::{self, Mappable};
|
||||
use crate::{domain, service, task_stack, util};
|
||||
|
||||
use std::{collections, sync};
|
||||
use std::sync;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
pub struct Service {
|
||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||
cert_resolver: sync::Arc<dyn rustls::server::ResolvesServerCert>,
|
||||
config: Config,
|
||||
proxied: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>,
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
@ -31,7 +30,6 @@ impl Service {
|
||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||
cert_resolver: CertResolver,
|
||||
config: Config,
|
||||
proxied: collections::HashMap<domain::Name, domain::ConfigProxiedDomain>,
|
||||
) -> sync::Arc<Service>
|
||||
where
|
||||
CertResolver: rustls::server::ResolvesServerCert + 'static,
|
||||
@ -40,7 +38,6 @@ impl Service {
|
||||
domain_manager,
|
||||
cert_resolver: sync::Arc::from(cert_resolver),
|
||||
config,
|
||||
proxied,
|
||||
});
|
||||
task_stack.push_spawn(|canceller| listen(service.clone(), canceller));
|
||||
service
|
||||
@ -74,7 +71,11 @@ impl Service {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn serve_conn<IO>(&self, domain: &domain::Name, conn: IO) -> Result<(), HandleConnError>
|
||||
async fn serve_conn<IO>(
|
||||
&self,
|
||||
settings: &domain::Settings,
|
||||
conn: IO,
|
||||
) -> Result<(), HandleConnError>
|
||||
where
|
||||
IO: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
|
||||
{
|
||||
@ -96,15 +97,23 @@ impl Service {
|
||||
|
||||
let path = service::append_index_to_path(req.path(), "index.gmi");
|
||||
|
||||
let f = match self.domain_manager.get_file(domain, &path) {
|
||||
use domain::manager::GetFileError;
|
||||
let f = match self.domain_manager.get_file(settings, &path) {
|
||||
Ok(f) => f,
|
||||
Err(domain::manager::GetFileError::DomainNotFound) => {
|
||||
return Err(unexpected::Error::from("domain not found when serving file").into())
|
||||
}
|
||||
Err(domain::manager::GetFileError::FileNotFound) => {
|
||||
Err(GetFileError::FileNotFound) => {
|
||||
return Ok(self.respond_conn(w, "51", "File not found", None).await?)
|
||||
}
|
||||
Err(domain::manager::GetFileError::Unexpected(e)) => return Err(e.into()),
|
||||
Err(GetFileError::DescrNotSynced) => {
|
||||
return Err(unexpected::Error::from(
|
||||
format!(
|
||||
"Backend for {:?} has not yet been synced",
|
||||
settings.origin_descr
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
Err(GetFileError::Unexpected(e)) => return Err(e.into()),
|
||||
};
|
||||
|
||||
let content_type = service::guess_mime(&path);
|
||||
@ -156,8 +165,14 @@ impl Service {
|
||||
))
|
||||
})?;
|
||||
|
||||
// If the domain should be proxied, then proxy it
|
||||
if let Some(config) = self.proxied.get(&domain) {
|
||||
use domain::manager::{GetSettingsError, GetSettingsResult};
|
||||
|
||||
let get_settings_res = self.domain_manager.get_settings(&domain);
|
||||
|
||||
// If the domain should be proxied, then proxy it. This case gets checked before
|
||||
// any of the others because the others require terminating the TLS stream to be
|
||||
// handled.
|
||||
if let Ok(GetSettingsResult::Proxied(ref config)) = get_settings_res {
|
||||
if let Some(ref gemini_url) = config.gemini_url {
|
||||
let prefixed_conn = proxy::teed_io_to_prefixed(start.into_inner());
|
||||
self.proxy_conn(gemini_url.addr.as_str(), prefixed_conn)
|
||||
@ -166,8 +181,26 @@ impl Service {
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate TLS stream
|
||||
let conn = start.into_stream(tls_config).await.or_unexpected()?;
|
||||
self.serve_conn(&domain, conn).await
|
||||
|
||||
let settings = match get_settings_res {
|
||||
Ok(GetSettingsResult::Stored(settings)) => settings,
|
||||
Ok(GetSettingsResult::Builtin(config)) => config.settings,
|
||||
Ok(GetSettingsResult::Proxied(_)) => {
|
||||
panic!("proxied case already handled")
|
||||
}
|
||||
Ok(GetSettingsResult::Interface)
|
||||
| Ok(GetSettingsResult::External(_))
|
||||
| Err(GetSettingsError::NotFound) => {
|
||||
return Ok(self
|
||||
.respond_conn(conn, "51", "File not found", None)
|
||||
.await?)
|
||||
}
|
||||
Err(GetSettingsError::Unexpected(e)) => return Err(e.into()),
|
||||
};
|
||||
|
||||
self.serve_conn(&settings, conn).await
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(unexpected::Error::from(
|
||||
|
@ -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,17 +543,20 @@ 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) {
|
||||
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();
|
||||
|
||||
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,
|
||||
@ -567,20 +567,32 @@ impl Service {
|
||||
.await
|
||||
.unwrap_or_else(|e| {
|
||||
self.internal_error(
|
||||
format!("serving {domain} via proxy {}: {e}", http_url.original_url).as_str(),
|
||||
format!("serving {domain} via proxy {}: {e}", http_url.original_url)
|
||||
.as_str(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
Ok(GetSettingsResult::Interface) => {
|
||||
if https_upgradable {
|
||||
return self.https_redirect(domain, req);
|
||||
}
|
||||
|
||||
if Some(&domain) == self.interface_domain.as_ref() {
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.serve_origin(domain, req).await
|
||||
self.serve_origin(settings, req).await
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,12 +14,6 @@ pub async fn listen_http(
|
||||
) -> Result<(), unexpected::Error> {
|
||||
let addr = service.config.http.http_addr;
|
||||
|
||||
// only used for logging
|
||||
let listen_host = service
|
||||
.interface_domain
|
||||
.clone()
|
||||
.map_or(addr.ip().to_string(), |ref d| d.as_str().to_string());
|
||||
|
||||
let make_service = hyper::service::make_service_fn(move |conn: &AddrStream| {
|
||||
let service = service.clone();
|
||||
let client_ip = conn.remote_addr().ip();
|
||||
@ -36,7 +30,7 @@ pub async fn listen_http(
|
||||
async move { Ok::<_, convert::Infallible>(hyper_service) }
|
||||
});
|
||||
|
||||
log::info!("Listening on http://{}:{}", listen_host, addr.port(),);
|
||||
log::info!("Listening on http://{}", &addr);
|
||||
let server = hyper::Server::bind(&addr).serve(make_service);
|
||||
|
||||
let graceful = server.with_graceful_shutdown(async {
|
||||
@ -53,12 +47,6 @@ pub async fn listen_https(
|
||||
let cert_resolver = service.cert_resolver.clone();
|
||||
let addr = service.config.http.https_addr.unwrap();
|
||||
|
||||
// only used for logging
|
||||
let listen_host = service
|
||||
.interface_domain
|
||||
.clone()
|
||||
.map_or(addr.ip().to_string(), |ref d| d.as_str().to_string());
|
||||
|
||||
let make_service = hyper::service::make_service_fn(move |conn: &TlsStream<AddrStream>| {
|
||||
let service = service.clone();
|
||||
let client_ip = conn.get_ref().0.remote_addr().ip();
|
||||
@ -97,7 +85,7 @@ pub async fn listen_https(
|
||||
|
||||
let incoming = hyper::server::accept::from_stream(incoming);
|
||||
|
||||
log::info!("Listening on https://{}:{}", listen_host, addr.port());
|
||||
log::info!("Listening on https://{}", addr);
|
||||
|
||||
let server = hyper::Server::builder(incoming).serve(make_service);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user