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}