Simplify the http service a bunch, better error handling
This commit is contained in:
parent
82290d8b0b
commit
a3c823c7b2
@ -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 {
|
||||||
|
@ -18,7 +18,7 @@ pub async fn listen_http(
|
|||||||
// Create a `Service` for responding to the request.
|
// Create a `Service` for responding to the request.
|
||||||
let hyper_service = hyper::service::service_fn(move |req| {
|
let hyper_service = hyper::service::service_fn(move |req| {
|
||||||
let service = service.clone();
|
let service = service.clone();
|
||||||
async move { service.handle_request(req).await }
|
async move { Ok::<_, convert::Infallible>(service.handle_request(req).await) }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return the service to hyper.
|
// Return the service to hyper.
|
||||||
@ -48,7 +48,7 @@ pub async fn listen_https(
|
|||||||
// Create a `Service` for responding to the request.
|
// Create a `Service` for responding to the request.
|
||||||
let hyper_service = hyper::service::service_fn(move |req| {
|
let hyper_service = hyper::service::service_fn(move |req| {
|
||||||
let service = service.clone();
|
let service = service.clone();
|
||||||
async move { service.handle_request(req).await }
|
async move { Ok::<_, convert::Infallible>(service.handle_request(req).await) }
|
||||||
});
|
});
|
||||||
|
|
||||||
// Return the service to hyper.
|
// Return the service to hyper.
|
||||||
|
Loading…
Reference in New Issue
Block a user