axum_embed_files/
service.rs1use tracing::debug;
2
3#[derive(Debug, Clone)]
33pub struct EmbeddedFileService {
34 pub directory: &'static str, pub filename: &'static str, pub bytes: &'static [u8],
40 pub etag: &'static str,
44 pub last_modified: &'static str,
48 pub content_type: Option<&'static str>,
52}
53
54#[cfg(not(feature = "serve-from-fs"))]
55impl<B> tower_service::Service<http::Request<B>> for EmbeddedFileService {
56 type Response = http::Response<axum::body::Body>;
57 type Error = std::convert::Infallible;
58 type Future = std::future::Ready<Result<Self::Response, Self::Error>>;
59
60 fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
61 std::task::Poll::Ready(Ok(()))
62 }
63
64 fn call(&mut self, req: http::Request<B>) -> Self::Future {
65 if let Some(value) = req.headers().get(http::header::IF_NONE_MATCH) {
66 if let Ok(etag) = value.to_str() {
67 if etag == self.etag {
68 debug!("cache hit");
69
70 let mut res = http::Response::new([][..].into());
71 *res.status_mut() = http::StatusCode::NOT_MODIFIED;
72 res.headers_mut().insert(
73 http::header::ETAG,
74 http::header::HeaderValue::from_static(self.etag),
75 );
76 res.headers_mut().insert(
77 http::header::LAST_MODIFIED,
78 http::header::HeaderValue::from_static(self.last_modified),
79 );
80 return std::future::ready(Ok(res));
81 }
82 }
83 }
84
85 debug!("serving from embedded file {}", self.filename);
88
89 let mut res = http::Response::new(self.bytes.into());
90 res.headers_mut().insert(
91 http::header::CACHE_CONTROL,
92 http::header::HeaderValue::from_static("max-age=31536000"),
93 );
94 res.headers_mut().insert(
95 http::header::ETAG,
96 http::header::HeaderValue::from_static(self.etag),
97 );
98 res.headers_mut().insert(
99 http::header::LAST_MODIFIED,
100 http::header::HeaderValue::from_static(self.last_modified),
101 );
102 if let Some(content_type) = &self.content_type {
103 res.headers_mut().insert(
104 http::header::CONTENT_TYPE,
105 http::header::HeaderValue::from_static(content_type),
106 );
107 }
108 std::future::ready(Ok(res))
109 }
110}
111
112#[cfg(feature = "serve-from-fs")]
113impl<B> tower_service::Service<http::Request<B>> for EmbeddedFileService {
114 type Response = http::Response<axum::body::Body>;
115 type Error = std::convert::Infallible;
116 type Future = std::future::Ready<Result<Self::Response, Self::Error>>;
117
118 fn poll_ready(&mut self, _: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
119 std::task::Poll::Ready(Ok(()))
120 }
121
122 fn call(&mut self, _req: http::Request<B>) -> Self::Future {
123 use axum_embed_files_core::content_type;
124
125 debug!("serving from local file {}", self.filename);
126
127 let path = std::path::PathBuf::from(&self.directory)
128 .join(&self.filename);
129
130 let bytes = std::fs::read(path).unwrap();
131
132 let mut res = http::Response::new(bytes.into());
133 if let Some(content_type) = content_type::guess_from_path(&self.filename) {
134 res.headers_mut().insert(
135 http::header::CONTENT_TYPE,
136 http::header::HeaderValue::from_str(&content_type).unwrap(),
137 );
138 }
139 std::future::ready(Ok(res))
140 }
141}