|
|
@ -4,15 +4,12 @@ mod tpl; |
|
|
|
use hyper::{Body, Method, Request, Response}; |
|
|
|
use hyper::{Body, Method, Request, Response}; |
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
use serde::{Deserialize, Serialize}; |
|
|
|
|
|
|
|
|
|
|
|
use std::convert::Infallible; |
|
|
|
|
|
|
|
use std::str::FromStr; |
|
|
|
use std::str::FromStr; |
|
|
|
use std::{future, net, sync}; |
|
|
|
use std::{future, net, sync}; |
|
|
|
|
|
|
|
|
|
|
|
use crate::error::unexpected; |
|
|
|
use crate::error::unexpected; |
|
|
|
use crate::{domain, service, util}; |
|
|
|
use crate::{domain, service, util}; |
|
|
|
|
|
|
|
|
|
|
|
type SvcResponse = Result<Response<Body>, String>; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub struct Service { |
|
|
|
pub struct Service { |
|
|
|
domain_manager: sync::Arc<dyn domain::manager::Manager>, |
|
|
|
domain_manager: sync::Arc<dyn domain::manager::Manager>, |
|
|
|
target_a: net::Ipv4Addr, |
|
|
|
target_a: net::Ipv4Addr, |
|
|
@ -94,7 +91,7 @@ struct DomainSyncArgs { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl<'svc> Service { |
|
|
|
impl<'svc> Service { |
|
|
|
fn serve(&self, status_code: u16, path: &'_ str, body: Body) -> SvcResponse { |
|
|
|
fn serve(&self, status_code: u16, path: &'_ str, body: Body) -> Response<Body> { |
|
|
|
let content_type = mime_guess::from_path(path) |
|
|
|
let content_type = mime_guess::from_path(path) |
|
|
|
.first_or_octet_stream() |
|
|
|
.first_or_octet_stream() |
|
|
|
.to_string(); |
|
|
|
.to_string(); |
|
|
@ -104,12 +101,22 @@ impl<'svc> Service { |
|
|
|
.header("Content-Type", content_type) |
|
|
|
.header("Content-Type", content_type) |
|
|
|
.body(body) |
|
|
|
.body(body) |
|
|
|
{ |
|
|
|
{ |
|
|
|
Ok(res) => Ok(res), |
|
|
|
Ok(res) => res, |
|
|
|
Err(err) => Err(format!("failed to build {}: {}", path, err)), |
|
|
|
Err(err) => { |
|
|
|
|
|
|
|
if status_code == 500 { |
|
|
|
|
|
|
|
panic!("failed to build {}: {}", path, err); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.serve( |
|
|
|
|
|
|
|
500, |
|
|
|
|
|
|
|
"error.txt", |
|
|
|
|
|
|
|
format!("failed to build {}: {}", path, err).into(), |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn render<T>(&self, status_code: u16, name: &'_ str, value: T) -> SvcResponse |
|
|
|
fn render<T>(&self, status_code: u16, name: &'_ str, value: T) -> Response<Body> |
|
|
|
where |
|
|
|
where |
|
|
|
T: Serialize, |
|
|
|
T: Serialize, |
|
|
|
{ |
|
|
|
{ |
|
|
@ -127,7 +134,7 @@ impl<'svc> Service { |
|
|
|
self.serve(status_code, name, rendered.into()) |
|
|
|
self.serve(status_code, name, rendered.into()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn render_error_page(&'svc self, status_code: u16, e: &'_ str) -> SvcResponse { |
|
|
|
fn render_error_page(&self, status_code: u16, e: &'_ str) -> Response<Body> { |
|
|
|
#[derive(Serialize)] |
|
|
|
#[derive(Serialize)] |
|
|
|
struct Response<'a> { |
|
|
|
struct Response<'a> { |
|
|
|
error_msg: &'a str, |
|
|
|
error_msg: &'a str, |
|
|
@ -143,7 +150,7 @@ impl<'svc> Service { |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn render_page<T>(&self, name: &'_ str, data: T) -> SvcResponse |
|
|
|
fn render_page<T>(&self, name: &'_ str, data: T) -> Response<Body> |
|
|
|
where |
|
|
|
where |
|
|
|
T: Serialize, |
|
|
|
T: Serialize, |
|
|
|
{ |
|
|
|
{ |
|
|
@ -157,7 +164,7 @@ impl<'svc> Service { |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn serve_origin(&self, domain: domain::Name, path: &'_ str) -> SvcResponse { |
|
|
|
fn serve_origin(&self, domain: domain::Name, path: &'_ str) -> Response<Body> { |
|
|
|
let mut path_owned; |
|
|
|
let mut path_owned; |
|
|
|
|
|
|
|
|
|
|
|
let path = match path.ends_with('/') { |
|
|
|
let path = match path.ends_with('/') { |
|
|
@ -183,20 +190,24 @@ impl<'svc> Service { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async fn with_query_req<'a, F, In, Out>(&self, req: &'a Request<Body>, f: F) -> SvcResponse |
|
|
|
async fn with_query_req<'a, F, In, Out>(&self, req: &'a Request<Body>, f: F) -> Response<Body> |
|
|
|
where |
|
|
|
where |
|
|
|
In: Deserialize<'a>, |
|
|
|
In: Deserialize<'a>, |
|
|
|
F: FnOnce(In) -> Out, |
|
|
|
F: FnOnce(In) -> Out, |
|
|
|
Out: future::Future<Output = SvcResponse>, |
|
|
|
Out: future::Future<Output = Response<Body>>, |
|
|
|
{ |
|
|
|
{ |
|
|
|
let query = req.uri().query().unwrap_or(""); |
|
|
|
let query = req.uri().query().unwrap_or(""); |
|
|
|
match serde_urlencoded::from_str::<In>(query) { |
|
|
|
match serde_urlencoded::from_str::<In>(query) { |
|
|
|
Ok(args) => f(args).await, |
|
|
|
Ok(args) => f(args).await, |
|
|
|
Err(err) => Err(format!("failed to parse query args: {}", err)), |
|
|
|
Err(err) => self.serve( |
|
|
|
|
|
|
|
400, |
|
|
|
|
|
|
|
"error.txt", |
|
|
|
|
|
|
|
format!("failed to parse query args: {}", err).into(), |
|
|
|
|
|
|
|
), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn domain_get(&self, args: DomainGetArgs) -> SvcResponse { |
|
|
|
fn domain_get(&self, args: DomainGetArgs) -> Response<Body> { |
|
|
|
#[derive(Serialize)] |
|
|
|
#[derive(Serialize)] |
|
|
|
struct Response { |
|
|
|
struct Response { |
|
|
|
domain: domain::Name, |
|
|
|
domain: domain::Name, |
|
|
@ -225,7 +236,7 @@ impl<'svc> Service { |
|
|
|
&self, |
|
|
|
&self, |
|
|
|
args: DomainInitArgs, |
|
|
|
args: DomainInitArgs, |
|
|
|
domain_config: service::util::FlatConfig, |
|
|
|
domain_config: service::util::FlatConfig, |
|
|
|
) -> SvcResponse { |
|
|
|
) -> Response<Body> { |
|
|
|
#[derive(Serialize)] |
|
|
|
#[derive(Serialize)] |
|
|
|
struct Response { |
|
|
|
struct Response { |
|
|
|
domain: domain::Name, |
|
|
|
domain: domain::Name, |
|
|
@ -265,7 +276,7 @@ impl<'svc> Service { |
|
|
|
&self, |
|
|
|
&self, |
|
|
|
args: DomainSyncArgs, |
|
|
|
args: DomainSyncArgs, |
|
|
|
domain_config: service::util::FlatConfig, |
|
|
|
domain_config: service::util::FlatConfig, |
|
|
|
) -> SvcResponse { |
|
|
|
) -> Response<Body> { |
|
|
|
if args.passphrase != self.passphrase.as_str() { |
|
|
|
if args.passphrase != self.passphrase.as_str() { |
|
|
|
return self.render_error_page(401, "Incorrect passphrase"); |
|
|
|
return self.render_error_page(401, "Incorrect passphrase"); |
|
|
|
} |
|
|
|
} |
|
|
@ -307,7 +318,7 @@ impl<'svc> Service { |
|
|
|
self.render_page("/domain_sync.html", response) |
|
|
|
self.render_page("/domain_sync.html", response) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn domains(&self) -> SvcResponse { |
|
|
|
fn domains(&self) -> Response<Body> { |
|
|
|
#[derive(Serialize)] |
|
|
|
#[derive(Serialize)] |
|
|
|
struct Response { |
|
|
|
struct Response { |
|
|
|
domains: Vec<String>, |
|
|
|
domains: Vec<String>, |
|
|
@ -330,7 +341,7 @@ impl<'svc> Service { |
|
|
|
self.render_page("/domains.html", Response { domains }) |
|
|
|
self.render_page("/domains.html", Response { domains }) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async fn handle_request_inner(&self, req: Request<Body>) -> SvcResponse { |
|
|
|
async fn handle_request(&self, req: Request<Body>) -> Response<Body> { |
|
|
|
let maybe_host = match ( |
|
|
|
let maybe_host = match ( |
|
|
|
req.headers() |
|
|
|
req.headers() |
|
|
|
.get("Host") |
|
|
|
.get("Host") |
|
|
@ -398,13 +409,6 @@ impl<'svc> Service { |
|
|
|
_ => self.render_error_page(404, "Page not found!"), |
|
|
|
_ => self.render_error_page(404, "Page not found!"), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
pub async fn handle_request(&self, req: Request<Body>) -> Result<Response<Body>, Infallible> { |
|
|
|
|
|
|
|
match self.handle_request_inner(req).await { |
|
|
|
|
|
|
|
Ok(res) => Ok(res), |
|
|
|
|
|
|
|
Err(err) => panic!("unexpected error {err}"), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn strip_port(host: &str) -> &str { |
|
|
|
fn strip_port(host: &str) -> &str { |
|
|
|