refactor: introduce a custom service for server
Diff
Cargo.toml | 2 +-
src/handler.rs | 62 +++++++++++++++++++++++++++++++------------------
src/lib.rs | 1 +-
src/server.rs | 58 +++++++++++-----------------------------------
src/service.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 131 insertions(+), 67 deletions(-)
@@ -45,7 +45,7 @@ pin-project = "1.0"
tokio-rustls = { version = "0.22" }
humansize = "1.1"
time = "0.1"
listenfd = "0.3.3"
listenfd = "0.3"
[target.'cfg(not(windows))'.dependencies.nix]
version = "0.14"
@@ -1,32 +1,50 @@
use hyper::{Body, Request, Response};
use std::path::Path;
use std::{future::Future, path::PathBuf};
use crate::{compression, control_headers, static_files};
use crate::{error::Result, error_page};
use crate::{error_page, Error, Result};
pub async fn handle_request(
base: &Path,
compression: bool,
dir_listing: bool,
req: &Request<Body>,
) -> Result<Response<Body>> {
let headers = req.headers();
let method = req.method();
pub struct RequestHandlerOpts {
pub root_dir: PathBuf,
pub compression: bool,
pub dir_listing: bool,
}
match static_files::handle_request(method, headers, base, req.uri().path(), dir_listing).await {
Ok(mut resp) => {
if compression {
resp = compression::auto(method, headers, resp)?;
}
pub struct RequestHandler {
pub opts: RequestHandlerOpts,
}
let ext = req.uri().path().to_lowercase();
control_headers::with_cache_control(&ext, &mut resp);
impl RequestHandler {
pub fn handle<'a>(
&'a self,
req: &'a mut Request<Body>,
) -> impl Future<Output = Result<Response<Body>, Error>> + Send + 'a {
let method = req.method();
let headers = req.headers();
let root_dir = self.opts.root_dir.as_path();
let uri_path = req.uri().path();
let dir_listing = self.opts.dir_listing;
Ok(resp)
async move {
match static_files::handle_request(method, headers, root_dir, uri_path, dir_listing)
.await
{
Ok(mut resp) => {
if self.opts.compression {
resp = compression::auto(method, headers, resp)?;
}
let ext = uri_path.to_lowercase();
control_headers::with_cache_control(&ext, &mut resp);
Ok(resp)
}
Err(status) => error_page::get_error_response(method, &status),
}
}
Err(status) => error_page::get_error_response(method, &status),
}
}
@@ -11,6 +11,7 @@ pub mod handler;
pub mod helpers;
pub mod logger;
pub mod server;
pub mod service;
pub mod signals;
pub mod static_files;
pub mod tls;
@@ -1,16 +1,14 @@
use hyper::server::conn::AddrIncoming;
use hyper::server::Server as HyperServer;
use hyper::service::{make_service_fn, service_fn};
use listenfd::ListenFd;
use std::net::{IpAddr, SocketAddr, TcpListener};
use std::sync::Arc;
use structopt::StructOpt;
use crate::config::Config;
use crate::static_files::ArcPath;
use crate::handler::{RequestHandler, RequestHandlerOpts};
use crate::tls::{TlsAcceptor, TlsConfigBuilder};
use crate::Result;
use crate::{error, error_page, handler, helpers, logger};
use crate::{config::Config, service::RouterService};
use crate::{error_page, helpers, logger};
pub struct Server {
@@ -82,7 +80,6 @@ impl Server {
let root_dir = helpers::get_valid_dirpath(&opts.root)?;
let root_dir = ArcPath(Arc::new(root_dir));
error_page::PAGE_404
@@ -105,6 +102,15 @@ impl Server {
let threads = self.threads;
let router_service = RouterService::new(RequestHandler {
opts: RequestHandlerOpts {
root_dir,
compression,
dir_listing,
},
});
if opts.http2 {
@@ -112,24 +118,6 @@ impl Server {
let key_path = opts.http2_tls_key.clone();
tokio::task::spawn(async move {
let make_service = make_service_fn(move |_| {
let root_dir = root_dir.clone();
async move {
Ok::<_, error::Error>(service_fn(move |req| {
let root_dir = root_dir.clone();
async move {
handler::handle_request(
root_dir.as_ref(),
compression,
dir_listing,
&req,
)
.await
}
}))
}
});
tcplistener
.set_nonblocking(true)
.expect("Cannot set non-blocking");
@@ -145,7 +133,7 @@ impl Server {
.unwrap();
let server =
HyperServer::builder(TlsAcceptor::new(tls, incoming)).serve(make_service);
HyperServer::builder(TlsAcceptor::new(tls, incoming)).serve(router_service);
tracing::info!(
parent: tracing::info_span!("Server::start_server", ?addr_string, ?threads),
@@ -159,28 +147,10 @@ impl Server {
tokio::task::spawn(async move {
let make_service = make_service_fn(move |_| {
let root_dir = root_dir.clone();
async move {
Ok::<_, error::Error>(service_fn(move |req| {
let root_dir = root_dir.clone();
async move {
handler::handle_request(
root_dir.as_ref(),
compression,
dir_listing,
&req,
)
.await
}
}))
}
});
let server = HyperServer::from_tcp(tcplistener)
.unwrap()
.tcp_nodelay(true)
.serve(make_service);
.serve(router_service);
tracing::info!(
parent: tracing::info_span!("Server::start_server", ?addr_string, ?threads),
@@ -0,0 +1,75 @@
use hyper::{service::Service, Body, Request, Response};
use std::convert::Infallible;
use std::future::{ready, Future, Ready};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use crate::handler::RequestHandler;
use crate::Error;
pub struct RouterService {
builder: RequestServiceBuilder,
}
impl RouterService {
pub fn new(handler: RequestHandler) -> Self {
Self {
builder: RequestServiceBuilder::new(handler),
}
}
}
impl<T> Service<T> for RouterService {
type Response = RequestService;
type Error = Infallible;
type Future = Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: T) -> Self::Future {
ready(Ok(self.builder.build()))
}
}
pub struct RequestService {
handler: Arc<RequestHandler>,
}
impl Service<Request<Body>> for RequestService {
type Response = Response<Body>;
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Response<Body>, Error>> + Send + 'static>>;
fn poll_ready(&mut self, _: &mut Context) -> Poll<Result<(), Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: Request<Body>) -> Self::Future {
let handler = self.handler.clone();
Box::pin(async move { handler.handle(&mut req).await })
}
}
pub struct RequestServiceBuilder {
handler: Arc<RequestHandler>,
}
impl RequestServiceBuilder {
pub fn new(handler: RequestHandler) -> Self {
Self {
handler: Arc::new(handler),
}
}
pub fn build(&self) -> RequestService {
RequestService {
handler: self.handler.clone(),
}
}
}