Implement serving of origins
This commit is contained in:
parent
c1b6d982ef
commit
8f1c0cce22
1
.env.dev
1
.env.dev
@ -1,3 +1,4 @@
|
||||
export DOMIPLY_HTTP_DOMAIN=localhost
|
||||
export DOMIPLY_PASSPHRASE=foobar
|
||||
export DOMIPLY_ORIGIN_STORE_GIT_DIR_PATH=/tmp/domiply_dev_env/origin/git
|
||||
export DOMIPLY_DOMAIN_CHECKER_TARGET_AAAA=::1
|
||||
|
@ -17,6 +17,9 @@ struct Cli {
|
||||
#[arg(long, default_value_t = SocketAddr::from_str("[::]:3030").unwrap(), env = "DOMIPLY_HTTP_LISTEN_ADDR")]
|
||||
http_listen_addr: SocketAddr,
|
||||
|
||||
#[arg(long, required = true, env = "DOMIPLY_HTTP_DOMAIN")]
|
||||
http_domain: String,
|
||||
|
||||
#[arg(long, required = true, env = "DOMIPLY_PASSPHRASE")]
|
||||
passphrase: String,
|
||||
|
||||
@ -82,6 +85,7 @@ fn main() {
|
||||
manager,
|
||||
config.domain_checker_target_aaaa,
|
||||
config.passphrase,
|
||||
config.http_domain,
|
||||
);
|
||||
|
||||
let service = sync::Arc::new(service);
|
||||
|
103
src/service.rs
103
src/service.rs
@ -1,24 +1,25 @@
|
||||
use http::status::StatusCode;
|
||||
use hyper::{Body, Method, Request, Response};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::convert::Infallible;
|
||||
use std::future::Future;
|
||||
use std::net;
|
||||
use std::str::FromStr;
|
||||
use std::sync;
|
||||
|
||||
use crate::domain;
|
||||
use crate::{domain, origin};
|
||||
|
||||
pub mod http_tpl;
|
||||
mod util;
|
||||
|
||||
type SvcResponse = Result<Response<String>, String>;
|
||||
type SvcResponse = Result<Response<hyper::body::Body>, String>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Service<'svc> {
|
||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||
target_aaaa: net::Ipv6Addr,
|
||||
passphrase: String,
|
||||
http_domain: String,
|
||||
handlebars: handlebars::Handlebars<'svc>,
|
||||
}
|
||||
|
||||
@ -26,11 +27,13 @@ pub fn new<'svc, 'mgr>(
|
||||
domain_manager: sync::Arc<dyn domain::manager::Manager>,
|
||||
target_aaaa: net::Ipv6Addr,
|
||||
passphrase: String,
|
||||
http_domain: String,
|
||||
) -> Service<'svc> {
|
||||
Service {
|
||||
domain_manager,
|
||||
target_aaaa,
|
||||
passphrase,
|
||||
http_domain,
|
||||
handlebars: self::http_tpl::get().expect("Retrieved Handlebars templates"),
|
||||
}
|
||||
}
|
||||
@ -58,6 +61,21 @@ struct DomainSyncArgs {
|
||||
}
|
||||
|
||||
impl<'svc> Service<'svc> {
|
||||
fn serve_string(&self, status_code: u16, path: &'_ str, body: Vec<u8>) -> SvcResponse {
|
||||
let content_type = mime_guess::from_path(path)
|
||||
.first_or_octet_stream()
|
||||
.to_string();
|
||||
|
||||
match Response::builder()
|
||||
.status(status_code)
|
||||
.header("Content-Type", content_type)
|
||||
.body(body.into())
|
||||
{
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => Err(format!("failed to build {}: {}", path, err)),
|
||||
}
|
||||
}
|
||||
|
||||
//// TODO make this use an io::Write, rather than SvcResponse
|
||||
fn render<T>(&self, status_code: u16, name: &'_ str, value: T) -> SvcResponse
|
||||
where
|
||||
@ -74,18 +92,7 @@ impl<'svc> Service<'svc> {
|
||||
}
|
||||
};
|
||||
|
||||
let content_type = mime_guess::from_path(name)
|
||||
.first_or_octet_stream()
|
||||
.to_string();
|
||||
|
||||
match Response::builder()
|
||||
.status(status_code)
|
||||
.header("Content-Type", content_type)
|
||||
.body(rendered)
|
||||
{
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => Err(format!("failed to build {}: {}", name, err)),
|
||||
}
|
||||
self.serve_string(status_code, name, rendered.into())
|
||||
}
|
||||
|
||||
fn render_error_page(&'svc self, status_code: u16, e: &'_ str) -> SvcResponse {
|
||||
@ -118,6 +125,40 @@ impl<'svc> Service<'svc> {
|
||||
)
|
||||
}
|
||||
|
||||
fn serve_origin(&self, domain: domain::Name, path: &'_ str) -> SvcResponse {
|
||||
let mut path_owned;
|
||||
|
||||
let path = match path.ends_with("/") {
|
||||
true => {
|
||||
path_owned = String::from(path);
|
||||
path_owned.push_str("index.html");
|
||||
path_owned.as_str()
|
||||
}
|
||||
false => path,
|
||||
};
|
||||
|
||||
let origin = match self.domain_manager.get_origin(&domain) {
|
||||
Ok(o) => o,
|
||||
Err(domain::manager::GetOriginError::NotFound) => {
|
||||
return self.render_error_page(404, "Domain not found")
|
||||
}
|
||||
Err(domain::manager::GetOriginError::Unexpected(e)) => {
|
||||
return self.render_error_page(500, format!("failed to fetch origin: {e}").as_str())
|
||||
}
|
||||
};
|
||||
|
||||
let mut buf = Vec::<u8>::new();
|
||||
match origin.read_file_into(&path, &mut buf) {
|
||||
Ok(_) => self.serve_string(200, &path, buf),
|
||||
Err(origin::ReadFileIntoError::FileNotFound) => {
|
||||
self.render_error_page(404, "File not found")
|
||||
}
|
||||
Err(origin::ReadFileIntoError::Unexpected(e)) => {
|
||||
self.render_error_page(500, format!("failed to fetch file {path}: {e}").as_str())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn with_query_req<'a, F, In, Out>(&self, req: &'a Request<Body>, f: F) -> SvcResponse
|
||||
where
|
||||
In: Deserialize<'a>,
|
||||
@ -239,14 +280,17 @@ impl<'svc> Service<'svc> {
|
||||
pub async fn handle_request<'svc>(
|
||||
svc: sync::Arc<Service<'svc>>,
|
||||
req: Request<Body>,
|
||||
) -> Result<Response<String>, Infallible> {
|
||||
) -> Result<Response<Body>, Infallible> {
|
||||
match handle_request_inner(svc, req).await {
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => {
|
||||
let mut res = Response::new(format!("failed to serve request: {}", err));
|
||||
*res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
Ok(res)
|
||||
}
|
||||
Err(err) => panic!("unexpected error {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn strip_port(host: &str) -> &str {
|
||||
match host.rfind(":") {
|
||||
None => host,
|
||||
Some(i) => &host[..i],
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,6 +298,23 @@ pub async fn handle_request_inner<'svc>(
|
||||
svc: sync::Arc<Service<'svc>>,
|
||||
req: Request<Body>,
|
||||
) -> SvcResponse {
|
||||
let maybe_host = match (
|
||||
req.headers()
|
||||
.get("Host")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(strip_port),
|
||||
req.uri().host().map(strip_port),
|
||||
) {
|
||||
(Some(h), _) if h != svc.http_domain => Some(h),
|
||||
(_, Some(h)) if h != svc.http_domain => Some(h),
|
||||
_ => None,
|
||||
}
|
||||
.and_then(|h| domain::Name::from_str(h).ok());
|
||||
|
||||
if let Some(domain) = maybe_host {
|
||||
return svc.serve_origin(domain, req.uri().path());
|
||||
}
|
||||
|
||||
let method = req.method();
|
||||
let path = req.uri().path();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user