fire_http/fs/
memory_files.rs

1use super::static_files::CachingBuilder;
2use super::{file, partial_file, Caching, Range};
3use crate::header::Method;
4use crate::into::IntoResponse;
5use crate::routes::{ParamsNames, PathParams, RoutePath};
6use crate::util::PinnedFuture;
7use crate::{Error, IntoRoute, Request, Resources, Response, Route};
8
9use std::io;
10use std::time::Duration;
11
12pub fn serve_memory_file(
13	path: &'static str,
14	bytes: &'static [u8],
15	req: &Request,
16	caching: Option<Caching>,
17) -> io::Result<Response> {
18	// check caching and if the etag matches return NOT_MODIFIED
19	if matches!(&caching, Some(c) if c.if_none_match(req.header())) {
20		return Ok(caching.unwrap().into_response());
21	}
22
23	let range = Range::parse(req.header());
24
25	let mut res = match range {
26		Some(range) => {
27			partial_file::serve_memory_partial_file(path, bytes, range)?
28		}
29		None => file::serve_memory_file(path, bytes)?,
30	};
31
32	// set etag
33	if let Some(caching) = caching {
34		caching.complete_header(&mut res.header);
35	}
36
37	Ok(res)
38}
39
40/// Static get handler which servers/returns a file.
41///
42/// ## Example
43/// ```
44/// # use fire_http as fire;
45/// use fire::fs::MemoryFile;
46/// use fire::memory_file;
47///
48/// const INDEX: MemoryFile = memory_file!(
49/// 	"/",
50/// 	"../../examples/www/hello_world.html"
51/// );
52/// ```
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct MemoryFile {
55	uri: &'static str,
56	path: &'static str,
57	bytes: &'static [u8],
58	caching: CachingBuilder,
59}
60
61impl MemoryFile {
62	/// Creates a `MemoryFile` with Default caching settings
63	pub const fn new(
64		uri: &'static str,
65		path: &'static str,
66		bytes: &'static [u8],
67	) -> Self {
68		Self {
69			uri,
70			path,
71			bytes,
72			caching: CachingBuilder::Default,
73		}
74	}
75
76	pub const fn no_cache(
77		uri: &'static str,
78		path: &'static str,
79		bytes: &'static [u8],
80	) -> Self {
81		Self {
82			uri,
83			path,
84			bytes,
85			caching: CachingBuilder::None,
86		}
87	}
88
89	pub const fn cache_with_age(
90		uri: &'static str,
91		path: &'static str,
92		bytes: &'static [u8],
93		max_age: Duration,
94	) -> Self {
95		Self {
96			uri,
97			path,
98			bytes,
99			caching: CachingBuilder::MaxAge(max_age),
100		}
101	}
102}
103
104impl IntoRoute for MemoryFile {
105	type IntoRoute = MemoryFileRoute;
106
107	fn into_route(self) -> MemoryFileRoute {
108		MemoryFileRoute {
109			uri: self.uri,
110			path: self.path,
111			bytes: self.bytes,
112			caching: self.caching.into(),
113		}
114	}
115}
116
117#[doc(hidden)]
118pub struct MemoryFileRoute {
119	uri: &'static str,
120	path: &'static str,
121	bytes: &'static [u8],
122	caching: Option<Caching>,
123}
124
125impl Route for MemoryFileRoute {
126	fn validate_requirements(&self, _params: &ParamsNames, _data: &Resources) {}
127
128	fn path(&self) -> RoutePath {
129		RoutePath {
130			method: Some(Method::GET),
131			path: self.uri.into(),
132		}
133	}
134
135	fn call<'a>(
136		&'a self,
137		req: &'a mut Request,
138		_params: &'a PathParams,
139		_data: &'a Resources,
140	) -> PinnedFuture<'a, crate::Result<Response>> {
141		let caching = self.caching.clone();
142
143		PinnedFuture::new(async move {
144			serve_memory_file(self.path, self.bytes, &req, caching)
145				.map_err(Error::from_client_io)
146		})
147	}
148}