hyper_staticfile/
service.rs1use std::{future::Future, io::Error as IoError, path::PathBuf, pin::Pin};
2
3use http::{Request, Response};
4use hyper::service::Service;
5
6use crate::{
7 vfs::{FileOpener, IntoFileAccess, TokioFileOpener},
8 AcceptEncoding, Body, Resolver, ResponseBuilder,
9};
10
11pub struct Static<O = TokioFileOpener> {
26 pub resolver: Resolver<O>,
28 pub cache_headers: Option<u32>,
30}
31
32impl Static<TokioFileOpener> {
33 pub fn new(root: impl Into<PathBuf>) -> Self {
38 Self {
39 resolver: Resolver::new(root),
40 cache_headers: None,
41 }
42 }
43}
44
45impl<O: FileOpener> Static<O> {
46 pub fn with_opener(opener: O) -> Self {
48 Self {
49 resolver: Resolver::with_opener(opener),
50 cache_headers: None,
51 }
52 }
53
54 pub fn cache_headers(&mut self, value: Option<u32>) -> &mut Self {
56 self.cache_headers = value;
57 self
58 }
59
60 pub fn allowed_encodings(&mut self, allowed_encodings: AcceptEncoding) -> &mut Self {
62 self.resolver.allowed_encodings = allowed_encodings;
63 self
64 }
65
66 pub async fn serve<B>(
68 self,
69 request: Request<B>,
70 ) -> Result<Response<Body<<O::File as IntoFileAccess>::Output>>, IoError> {
71 let Self {
72 resolver,
73 cache_headers,
74 } = self;
75 resolver.resolve_request(&request).await.map(|result| {
76 ResponseBuilder::new()
77 .request(&request)
78 .cache_headers(cache_headers)
79 .build(result)
80 .expect("unable to build response")
81 })
82 }
83}
84
85impl<O> Clone for Static<O> {
86 fn clone(&self) -> Self {
87 Self {
88 resolver: self.resolver.clone(),
89 cache_headers: self.cache_headers,
90 }
91 }
92}
93
94impl<O, B> Service<Request<B>> for Static<O>
95where
96 O: FileOpener,
97 B: Send + Sync + 'static,
98{
99 type Response = Response<Body<<O::File as IntoFileAccess>::Output>>;
100 type Error = IoError;
101 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
102
103 fn call(&self, request: Request<B>) -> Self::Future {
104 Box::pin(self.clone().serve(request))
105 }
106}