Move handle_request onto service as a method

This commit is contained in:
Brian Picciano 2023-06-19 20:09:25 +02:00
parent 506037dcd0
commit 43f4b98b38
2 changed files with 104 additions and 103 deletions

View File

@ -328,7 +328,7 @@ impl<'svc> Service {
self.render_page("/domain_sync.html", response) self.render_page("/domain_sync.html", response)
} }
pub fn domains(&self) -> SvcResponse { fn domains(&self) -> SvcResponse {
#[derive(Serialize)] #[derive(Serialize)]
struct Response { struct Response {
domains: Vec<String>, domains: Vec<String>,
@ -350,15 +350,88 @@ impl<'svc> Service {
self.render_page("/domains.html", Response { domains }) self.render_page("/domains.html", Response { domains })
} }
}
pub async fn handle_request( async fn handle_request_inner(&self, req: Request<Body>) -> SvcResponse {
svc: sync::Arc<Service>, let maybe_host = match (
req: Request<Body>, req.headers()
) -> Result<Response<Body>, Infallible> { .get("Host")
match handle_request_inner(svc, req).await { .and_then(|v| v.to_str().ok())
Ok(res) => Ok(res), .map(strip_port),
Err(err) => panic!("unexpected error {err}"), req.uri().host().map(strip_port),
) {
(Some(h), _) if h != self.http_domain.as_str() => Some(h),
(_, Some(h)) if h != self.http_domain.as_str() => Some(h),
_ => None,
}
.and_then(|h| domain::Name::from_str(h).ok());
let method = req.method();
let path = req.uri().path();
// Serving acme challenges always takes priority. We serve them from the same store no matter
// the domain, presumably they are cryptographically random enough that it doesn't matter.
if method == Method::GET && path.starts_with("/.well-known/acme-challenge/") {
let token = path.trim_start_matches("/.well-known/acme-challenge/");
if let Ok(key) = self.domain_manager.get_acme_http01_challenge_key(token) {
let body: hyper::Body = key.into();
return match Response::builder().status(200).body(body) {
Ok(res) => Ok(res),
Err(err) => Err(format!(
"failed to write acme http-01 challenge key: {}",
err
)),
};
}
}
// If a managed domain was given then serve that from its origin
if let Some(domain) = maybe_host {
return self.serve_origin(domain, req.uri().path());
}
// Serve main domiply site
if method == Method::GET && path.starts_with("/static/") {
return self.render(200, path, ());
}
match (method, path) {
(&Method::GET, "/") | (&Method::GET, "/index.html") => {
self.render_page("/index.html", ())
}
(&Method::GET, "/domain.html") => {
self.with_query_req(&req, |args: DomainGetArgs| async { self.domain_get(args) })
.await
}
(&Method::GET, "/domain_init.html") => {
self.with_query_req(&req, |args: DomainInitArgs| async {
self.with_query_req(&req, |config: service::util::FlatConfig| async {
self.domain_init(args, config)
})
.await
})
.await
}
(&Method::GET, "/domain_sync.html") => {
self.with_query_req(&req, |args: DomainSyncArgs| async {
self.with_query_req(&req, |config: service::util::FlatConfig| async {
self.domain_sync(args, config).await
})
.await
})
.await
}
(&Method::GET, "/domains.html") => self.domains(),
_ => 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}"),
}
} }
} }
@ -368,77 +441,3 @@ fn strip_port(host: &str) -> &str {
Some(i) => &host[..i], Some(i) => &host[..i],
} }
} }
pub async fn handle_request_inner(svc: sync::Arc<Service>, 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.as_str() => Some(h),
(_, Some(h)) if h != svc.http_domain.as_str() => Some(h),
_ => None,
}
.and_then(|h| domain::Name::from_str(h).ok());
let method = req.method();
let path = req.uri().path();
// Serving acme challenges always takes priority. We serve them from the same store no matter
// the domain, presumably they are cryptographically random enough that it doesn't matter.
if method == Method::GET && path.starts_with("/.well-known/acme-challenge/") {
let token = path.trim_start_matches("/.well-known/acme-challenge/");
if let Ok(key) = svc.domain_manager.get_acme_http01_challenge_key(token) {
let body: hyper::Body = key.into();
return match Response::builder().status(200).body(body) {
Ok(res) => Ok(res),
Err(err) => Err(format!(
"failed to write acme http-01 challenge key: {}",
err
)),
};
}
}
// If a managed domain was given then serve that from its origin
if let Some(domain) = maybe_host {
return svc.serve_origin(domain, req.uri().path());
}
// Serve main domiply site
if method == Method::GET && path.starts_with("/static/") {
return svc.render(200, path, ());
}
match (method, path) {
(&Method::GET, "/") | (&Method::GET, "/index.html") => svc.render_page("/index.html", ()),
(&Method::GET, "/domain.html") => {
svc.with_query_req(&req, |args: DomainGetArgs| async { svc.domain_get(args) })
.await
}
(&Method::GET, "/domain_init.html") => {
svc.with_query_req(&req, |args: DomainInitArgs| async {
svc.with_query_req(&req, |config: service::util::FlatConfig| async {
svc.domain_init(args, config)
})
.await
})
.await
}
(&Method::GET, "/domain_sync.html") => {
svc.with_query_req(&req, |args: DomainSyncArgs| async {
svc.with_query_req(&req, |config: service::util::FlatConfig| async {
svc.domain_sync(args, config).await
})
.await
})
.await
}
(&Method::GET, "/domains.html") => svc.domains(),
_ => svc.render_error_page(404, "Page not found!"),
}
}

View File

@ -11,19 +11,20 @@ pub fn listen_http(
addr: net::SocketAddr, addr: net::SocketAddr,
domain: domain::Name, domain: domain::Name,
) -> tokio::task::JoinHandle<()> { ) -> tokio::task::JoinHandle<()> {
let make_service = hyper::service::make_service_fn(move |_| { tokio::spawn(async move {
let service = service.clone(); let make_service = hyper::service::make_service_fn(move |_| {
let service = service.clone();
// 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| {
service::http::handle_request(service.clone(), req) let service = service.clone();
async move { service.handle_request(req).await }
});
// Return the service to hyper.
async move { Ok::<_, convert::Infallible>(hyper_service) }
}); });
// Return the service to hyper.
async move { Ok::<_, convert::Infallible>(hyper_service) }
});
tokio::spawn(async move {
log::info!("Listening on http://{}:{}", domain.as_str(), addr.port()); log::info!("Listening on http://{}:{}", domain.as_str(), addr.port());
let server = hyper::Server::bind(&addr).serve(make_service); let server = hyper::Server::bind(&addr).serve(make_service);
@ -44,19 +45,20 @@ pub fn listen_https(
addr: net::SocketAddr, addr: net::SocketAddr,
domain: domain::Name, domain: domain::Name,
) -> tokio::task::JoinHandle<()> { ) -> tokio::task::JoinHandle<()> {
let make_service = hyper::service::make_service_fn(move |_| { tokio::spawn(async move {
let service = service.clone(); let make_service = hyper::service::make_service_fn(move |_| {
let service = service.clone();
// 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| {
service::http::handle_request(service.clone(), req) let service = service.clone();
async move { service.handle_request(req).await }
});
// Return the service to hyper.
async move { Ok::<_, convert::Infallible>(hyper_service) }
}); });
// Return the service to hyper.
async move { Ok::<_, convert::Infallible>(hyper_service) }
});
tokio::spawn(async move {
let server_config: tokio_rustls::TlsAcceptor = sync::Arc::new( let server_config: tokio_rustls::TlsAcceptor = sync::Arc::new(
rustls::server::ServerConfig::builder() rustls::server::ServerConfig::builder()
.with_safe_defaults() .with_safe_defaults()