From 43f4b98b38a531357f28c56bf39439aaa5f1fd26 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Mon, 19 Jun 2023 20:09:25 +0200 Subject: [PATCH] Move handle_request onto service as a method --- src/service/http.rs | 165 +++++++++++++++++++------------------- src/service/http/tasks.rs | 42 +++++----- 2 files changed, 104 insertions(+), 103 deletions(-) diff --git a/src/service/http.rs b/src/service/http.rs index 8ea2692..a0a44c3 100644 --- a/src/service/http.rs +++ b/src/service/http.rs @@ -328,7 +328,7 @@ impl<'svc> Service { self.render_page("/domain_sync.html", response) } - pub fn domains(&self) -> SvcResponse { + fn domains(&self) -> SvcResponse { #[derive(Serialize)] struct Response { domains: Vec, @@ -350,15 +350,88 @@ impl<'svc> Service { self.render_page("/domains.html", Response { domains }) } -} -pub async fn handle_request( - svc: sync::Arc, - req: Request, -) -> Result, Infallible> { - match handle_request_inner(svc, req).await { - Ok(res) => Ok(res), - Err(err) => panic!("unexpected error {err}"), + async fn handle_request_inner(&self, req: Request) -> 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 != 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) -> Result, 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], } } - -pub async fn handle_request_inner(svc: sync::Arc, req: Request) -> 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!"), - } -} diff --git a/src/service/http/tasks.rs b/src/service/http/tasks.rs index 29750ce..9a0f136 100644 --- a/src/service/http/tasks.rs +++ b/src/service/http/tasks.rs @@ -11,19 +11,20 @@ pub fn listen_http( addr: net::SocketAddr, domain: domain::Name, ) -> tokio::task::JoinHandle<()> { - let make_service = hyper::service::make_service_fn(move |_| { - let service = service.clone(); + tokio::spawn(async move { + let make_service = hyper::service::make_service_fn(move |_| { + let service = service.clone(); - // Create a `Service` for responding to the request. - let hyper_service = hyper::service::service_fn(move |req| { - service::http::handle_request(service.clone(), req) + // 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 } + }); + + // 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()); let server = hyper::Server::bind(&addr).serve(make_service); @@ -44,19 +45,20 @@ pub fn listen_https( addr: net::SocketAddr, domain: domain::Name, ) -> tokio::task::JoinHandle<()> { - let make_service = hyper::service::make_service_fn(move |_| { - let service = service.clone(); + tokio::spawn(async move { + let make_service = hyper::service::make_service_fn(move |_| { + let service = service.clone(); - // Create a `Service` for responding to the request. - let hyper_service = hyper::service::service_fn(move |req| { - service::http::handle_request(service.clone(), req) + // 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 } + }); + + // 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( rustls::server::ServerConfig::builder() .with_safe_defaults()