hyper_staticfile_jsutf8/
service.rs

1use crate::{resolve, ResponseBuilder};
2use http::{Request, Response};
3use hyper::{service::Service, Body};
4use std::future::Future;
5use std::io::Error as IoError;
6use std::path::PathBuf;
7use std::pin::Pin;
8use std::task::{Context, Poll};
9
10/// High-level interface for serving static files.
11///
12/// This struct serves files from a single root path, which may be absolute or relative. The
13/// request is mapped onto the filesystem by appending their URL path to the root path. If the
14/// filesystem path corresponds to a regular file, the service will attempt to serve it. Otherwise,
15/// if the path corresponds to a directory containing an `index.html`, the service will attempt to
16/// serve that instead.
17///
18/// This struct allows direct access to its fields, but these fields are typically initialized by
19/// the accessors, using the builder pattern. The fields are basically a bunch of settings that
20/// determine the response details.
21///
22/// This struct also implements the `hyper::Service` trait, which simply wraps `Static::serve`.
23/// Note that using the trait currently involves an extra `Box`.
24#[derive(Clone)]
25pub struct Static {
26    /// The root directory path to serve files from.
27    pub root: PathBuf,
28    /// Whether to send cache headers, and what lifespan to indicate.
29    pub cache_headers: Option<u32>,
30}
31
32impl Static {
33    /// Create a new instance of `Static` with a given root path.
34    ///
35    /// If `Path::new("")` is given, files will be served from the current directory.
36    pub fn new(root: impl Into<PathBuf>) -> Self {
37        let root = root.into();
38        Static {
39            root,
40            cache_headers: None,
41        }
42    }
43
44    /// Add cache headers to responses for the given lifespan.
45    pub fn cache_headers(&mut self, value: Option<u32>) -> &mut Self {
46        self.cache_headers = value;
47        self
48    }
49
50    /// Serve a request.
51    pub async fn serve<B>(self, request: Request<B>) -> Result<Response<Body>, IoError> {
52        let Self {
53            root,
54            cache_headers,
55        } = self;
56        resolve(root, &request).await.map(|result| {
57            ResponseBuilder::new()
58                .request(&request)
59                .cache_headers(cache_headers)
60                .build(result)
61                .expect("unable to build response")
62        })
63    }
64}
65
66impl<B: Send + Sync + 'static> Service<Request<B>> for Static {
67    type Response = Response<Body>;
68    type Error = IoError;
69    type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
70
71    fn poll_ready(&mut self, _cx: &mut Context) -> Poll<Result<(), Self::Error>> {
72        Poll::Ready(Ok(()))
73    }
74
75    fn call(&mut self, request: Request<B>) -> Self::Future {
76        Box::pin(self.clone().serve(request))
77    }
78}