Simplify the http service a bunch, better error handling

This commit is contained in:
Brian Picciano 2023-07-08 16:04:33 +02:00
parent 82290d8b0b
commit a3c823c7b2
2 changed files with 31 additions and 27 deletions

View File

@ -4,15 +4,12 @@ mod tpl;
use hyper::{Body, Method, Request, Response};
use serde::{Deserialize, Serialize};
use std::convert::Infallible;
use std::str::FromStr;
use std::{future, net, sync};
use crate::error::unexpected;
use crate::{domain, service, util};
type SvcResponse = Result<Response<Body>, String>;
pub struct Service {
domain_manager: sync::Arc<dyn domain::manager::Manager>,
target_a: net::Ipv4Addr,
@ -94,7 +91,7 @@ struct DomainSyncArgs {
}
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)
.first_or_octet_stream()
.to_string();
@ -104,12 +101,22 @@ impl<'svc> Service {
.header("Content-Type", content_type)
.body(body)
{
Ok(res) => Ok(res),
Err(err) => Err(format!("failed to build {}: {}", path, err)),
Ok(res) => res,
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
T: Serialize,
{
@ -127,7 +134,7 @@ impl<'svc> Service {
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)]
struct Response<'a> {
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
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 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
In: Deserialize<'a>,
F: FnOnce(In) -> Out,
Out: future::Future<Output = SvcResponse>,
Out: future::Future<Output = Response<Body>>,
{
let query = req.uri().query().unwrap_or("");
match serde_urlencoded::from_str::<In>(query) {
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)]
struct Response {
domain: domain::Name,
@ -225,7 +236,7 @@ impl<'svc> Service {
&self,
args: DomainInitArgs,
domain_config: service::util::FlatConfig,
) -> SvcResponse {
) -> Response<Body> {
#[derive(Serialize)]
struct Response {
domain: domain::Name,
@ -265,7 +276,7 @@ impl<'svc> Service {
&self,
args: DomainSyncArgs,
domain_config: service::util::FlatConfig,
) -> SvcResponse {
) -> Response<Body> {
if args.passphrase != self.passphrase.as_str() {
return self.render_error_page(401, "Incorrect passphrase");
}
@ -307,7 +318,7 @@ impl<'svc> Service {
self.render_page("/domain_sync.html", response)
}
fn domains(&self) -> SvcResponse {
fn domains(&self) -> Response<Body> {
#[derive(Serialize)]
struct Response {
domains: Vec<String>,
@ -330,7 +341,7 @@ impl<'svc> Service {
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 (
req.headers()
.get("Host")
@ -398,13 +409,6 @@ impl<'svc> Service {
_ => 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 {

View File

@ -18,7 +18,7 @@ pub async fn listen_http(
// Create a `Service` for responding to the request.
let hyper_service = hyper::service::service_fn(move |req| {
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.
@ -48,7 +48,7 @@ pub async fn listen_https(
// Create a `Service` for responding to the request.
let hyper_service = hyper::service::service_fn(move |req| {
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.