Skip to content

Commit fb8df12

Browse files
committed
Clean up docs. Rename 'FileServer::empty()'.
1 parent 482a047 commit fb8df12

File tree

4 files changed

+139
-107
lines changed

4 files changed

+139
-107
lines changed

core/http/src/uri/segments.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,9 @@ impl<'a> Segments<'a, Path> {
178178
}
179179

180180
/// Creates a `PathBuf` from `self`. The returned `PathBuf` is
181-
/// percent-decoded. If a segment is equal to `..`, the previous segment (if
182-
/// any) is skipped.
181+
/// percent-decoded and guaranteed to be relative. If a segment is equal to
182+
/// `.`, it is skipped. If a segment is equal to `..`, the previous segment
183+
/// (if any) is skipped.
183184
///
184185
/// For security purposes, if a segment meets any of the following
185186
/// conditions, an `Err` is returned indicating the condition met:
@@ -193,7 +194,7 @@ impl<'a> Segments<'a, Path> {
193194
/// Additionally, if `allow_dotfiles` is `false`, an `Err` is returned if
194195
/// the following condition is met:
195196
///
196-
/// * Decoded segment starts with any of: `.` (except `..`)
197+
/// * Decoded segment starts with any of: `.` (except `..` and `.`)
197198
///
198199
/// As a result of these conditions, a `PathBuf` derived via `FromSegments`
199200
/// is safe to interpolate within, or use as a suffix of, a path without
@@ -216,10 +217,10 @@ impl<'a> Segments<'a, Path> {
216217
pub fn to_path_buf(&self, allow_dotfiles: bool) -> Result<PathBuf, PathError> {
217218
let mut buf = PathBuf::new();
218219
for segment in self.clone() {
219-
if segment == ".." {
220-
buf.pop();
221-
} else if segment == "." {
220+
if segment == "." {
222221
continue;
222+
} else if segment == ".." {
223+
buf.pop();
223224
} else if !allow_dotfiles && segment.starts_with('.') {
224225
return Err(PathError::BadStart('.'))
225226
} else if segment.starts_with('*') {

core/lib/src/fs/rewrite.rs

+13-30
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,20 @@ use crate::Request;
55
use crate::http::{ext::IntoOwned, HeaderMap};
66
use crate::response::Redirect;
77

8-
/// Trait used to implement [`FileServer`] customization.
8+
/// A file server [`Rewrite`] rewriter.
99
///
10-
/// A [`FileServer`] is a sequence of `Rewriter`s which transform the incoming
11-
/// request path into a [`Rewrite`] or `None`.
12-
///
13-
/// If the final rewrite is `None` or a nonexistent path or a directory,
14-
/// [`FileServer`] responds with [`Status::NotFound`]. Otherwise it responds
15-
/// with the file contents, if [`Rewrite::File`] is specified, or a redirect, if
10+
/// A [`FileServer`] is a sequence of [`Rewriter`]s which transform the incoming
11+
/// request path into a [`Rewrite`] or `None`. The first rewriter is called with
12+
/// the request path as a [`Rewrite::File`]. Each `Rewriter` thereafter is
13+
/// called in-turn with the previously returned [`Rewrite`], and the value
14+
/// returned from the last `Rewriter` is used to respond to the request. If the
15+
/// final rewrite is `None` or a nonexistent path or a directory, [`FileServer`]
16+
/// responds with [`Status::NotFound`]. Otherwise it responds with the file
17+
/// contents, if [`Rewrite::File`] is specified, or a redirect, if
1618
/// [`Rewrite::Redirect`] is specified.
1719
///
18-
/// # Creating a [`FileServer`]
19-
///
20-
/// The primary way to create a [`FileServer`] is via [`FileServer::new()`] which
21-
/// creates a new [`FileServer`] with a default set of `Rewriter`s: a filter for
22-
/// dotfiles, a root path to apply as a prefix, an index file rewriter, and a
23-
/// rewriter to normalize directories to always include a trailing slash.
24-
///
2520
/// [`FileServer`]: super::FileServer
26-
/// [`FileServer::new()`]: super::FileServer::new
21+
/// [`Status::NotFound`]: crate::http::Status::NotFound
2722
pub trait Rewriter: Send + Sync + 'static {
2823
/// Alter the [`Rewrite`] as needed.
2924
fn rewrite<'r>(&self, opt: Option<Rewrite<'r>>, req: &'r Request<'_>) -> Option<Rewrite<'r>>;
@@ -109,7 +104,7 @@ impl<'r> File<'r> {
109104
/// use rocket::fs::FileServer;
110105
/// use rocket::fs::rewrite::Prefix;
111106
///
112-
/// FileServer::empty()
107+
/// FileServer::identity()
113108
/// .filter(|f, _| f.is_visible())
114109
/// .rewrite(Prefix::checked("static"));
115110
/// ```
@@ -160,7 +155,7 @@ impl Rewriter for PathBuf {
160155
/// use rocket::fs::FileServer;
161156
/// use rocket::fs::rewrite::{Prefix, TrailingDirs};
162157
///
163-
/// FileServer::empty()
158+
/// FileServer::identity()
164159
/// .filter(|f, _| f.is_visible())
165160
/// .rewrite(TrailingDirs);
166161
/// ```
@@ -190,7 +185,7 @@ impl Rewriter for TrailingDirs {
190185
/// use rocket::fs::FileServer;
191186
/// use rocket::fs::rewrite::DirIndex;
192187
///
193-
/// FileServer::new_without_index("static")
188+
/// FileServer::without_index("static")
194189
/// .rewrite(DirIndex::if_exists("index.htm"))
195190
/// .rewrite(DirIndex::unconditional("index.html"));
196191
/// ```
@@ -233,24 +228,12 @@ impl<'r> From<File<'r>> for Rewrite<'r> {
233228
}
234229
}
235230

236-
impl<'r> From<File<'r>> for Option<Rewrite<'r>> {
237-
fn from(value: File<'r>) -> Self {
238-
Some(Rewrite::File(value))
239-
}
240-
}
241-
242231
impl<'r> From<Redirect> for Rewrite<'r> {
243232
fn from(value: Redirect) -> Self {
244233
Self::Redirect(value)
245234
}
246235
}
247236

248-
impl<'r> From<Redirect> for Option<Rewrite<'r>> {
249-
fn from(value: Redirect) -> Self {
250-
Some(Rewrite::Redirect(value))
251-
}
252-
}
253-
254237
impl<F: Send + Sync + 'static> Rewriter for F
255238
where F: for<'r> Fn(Option<Rewrite<'r>>, &Request<'_>) -> Option<Rewrite<'r>>
256239
{

core/lib/src/fs/server.rs

+110-62
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,38 @@ use crate::fs::rewrite::*;
1515
///
1616
/// This handler makes is simple to serve static files from a directory on the
1717
/// local file system. To use it, construct a `FileServer` using
18-
/// [`FileServer::new()`], then simply `mount` the handler. When mounted, the
19-
/// handler serves files from the specified directory. If the file is not found,
20-
/// the handler _forwards_ the request. By default, `FileServer` has a rank of
21-
/// `10`. Use [`FileServer::new()`] to create a handler with a custom rank.
18+
/// [`FileServer::new()`], then `mount` the handler.
2219
///
23-
/// # Customization
20+
/// ```rust,no_run
21+
/// # #[macro_use] extern crate rocket;
22+
/// use rocket::fs::FileServer;
2423
///
25-
/// How `FileServer` responds to specific requests can be customized through
26-
/// the use of [`Rewriter`]s. See [`Rewriter`] for more detailed documentation
27-
/// on how to take full advantage of `FileServer`'s extensibility.
24+
/// #[launch]
25+
/// fn rocket() -> _ {
26+
/// rocket::build()
27+
/// .mount("/", FileServer::new("/www/static"))
28+
/// }
29+
/// ```
30+
///
31+
/// When mounted, the handler serves files from the specified path. If a
32+
/// requested file does not exist, the handler _forwards_ the request with a
33+
/// `404` status.
34+
///
35+
/// By default, the route has a rank of `10` which can be changed with
36+
/// [`FileServer::rank()`].
2837
///
29-
/// [`FileServer::new()`] construct a `FileServer`
30-
/// with common rewrites: they filter out dotfiles, redirect requests to
31-
/// directories to include a trailing slash, and use `index.html` to respond to
32-
/// requests for a directory. If you want to customize or replace these default
33-
/// rewrites, see [`FileServer::empty()`].
38+
/// # Customization
39+
///
40+
/// `FileServer` works through a pipeline of _rewrites_ in which a requested
41+
/// path is transformed into a `PathBuf` via [`Segments::to_path_buf()`] and
42+
/// piped through a series of [`Rewriter`]s to obtain a final [`Rewrite`] which
43+
/// is then used to generate a final response. See [`Rewriter`] for complete
44+
/// details on implementing your own `Rewriter`s.
3445
///
3546
/// # Example
3647
///
3748
/// Serve files from the `/static` directory on the local file system at the
38-
/// `/public` path, with the default rewrites.
49+
/// `/public` path:
3950
///
4051
/// ```rust,no_run
4152
/// # #[macro_use] extern crate rocket;
@@ -68,6 +79,8 @@ use crate::fs::rewrite::*;
6879
/// rocket::build().mount("/", FileServer::new(relative!("static")))
6980
/// }
7081
/// ```
82+
///
83+
/// [`relative!`]: crate::fs::relative!
7184
#[derive(Clone)]
7285
pub struct FileServer {
7386
rewrites: Vec<Arc<dyn Rewriter>>,
@@ -79,50 +92,53 @@ impl FileServer {
7992
const DEFAULT_RANK: isize = 10;
8093

8194
/// Constructs a new `FileServer` that serves files from the file system
82-
/// `path` with a default rank.
95+
/// `path` with the following rewrites:
8396
///
84-
/// Adds a set of default rewrites:
85-
/// - `|f, _| f.is_visible()`: Hides all dotfiles.
86-
/// - [`Prefix::checked(path)`]: Applies the root path.
87-
/// - [`TrailingDirs`]: Normalizes directories to have a trailing slash.
88-
/// - [`DirIndex::unconditional("index.html")`](DirIndex::unconditional):
89-
/// Appends `index.html` to directory requests.
97+
/// - `|f, _| f.is_visible()`: Serve only visible files (hide dotfiles).
98+
/// - [`Prefix::checked(path)`]: Prefix requests with `path`.
99+
/// - [`TrailingDirs`]: Ensure directory have a trailing slash.
100+
/// - [`DirIndex::unconditional("index.html")`]: Serve `$dir/index.html` for
101+
/// requests to directory `$dir`.
90102
///
91-
/// If you don't want to serve requests for directories, or want to
92-
/// customize what files are served when a directory is requested, see
93-
/// [`Self::new_without_index`].
94-
///
95-
/// If you need to allow requests for dotfiles, or make any other changes
96-
/// to the default rewrites, see [`Self::empty`].
103+
/// If you don't want to serve index files or want a different index file,
104+
/// use [`Self::without_index`]. To customize the entire request to file
105+
/// path rewrite pipeline, use [`Self::identity`].
97106
///
98107
/// [`Prefix::checked(path)`]: crate::fs::rewrite::Prefix::checked
99108
/// [`TrailingDirs`]: crate::fs::rewrite::TrailingDirs
100-
/// [`DirIndex::unconditional`]: crate::fs::DirIndex::unconditional
109+
/// [`DirIndex::unconditional("index.html")`]: DirIndex::unconditional()
110+
///
111+
/// # Example
112+
///
113+
/// ```rust,no_run
114+
/// # #[macro_use] extern crate rocket;
115+
/// use rocket::fs::FileServer;
116+
///
117+
/// #[launch]
118+
/// fn rocket() -> _ {
119+
/// rocket::build()
120+
/// .mount("/", FileServer::new("/www/static"))
121+
/// }
122+
/// ```
101123
pub fn new<P: AsRef<Path>>(path: P) -> Self {
102-
Self::empty()
124+
Self::identity()
103125
.filter(|f, _| f.is_visible())
104126
.rewrite(Prefix::checked(path))
105127
.rewrite(TrailingDirs)
106128
.rewrite(DirIndex::unconditional("index.html"))
107129
}
108130

109-
/// Constructs a new `FileServer` that serves files from the file system
110-
/// `path` with a default rank. This variant does not add a default
111-
/// directory index option.
131+
/// Exactly like [`FileServer::new()`] except it _does not_ serve directory
132+
/// index files via [`DirIndex`]. It rewrites with the following:
112133
///
113-
/// Adds a set of default rewrites:
114-
/// - `|f, _| f.is_visible()`: Hides all dotfiles.
115-
/// - [`Prefix::checked(path)`]: Applies the root path.
116-
/// - [`TrailingDirs`]: Normalizes directories to have a trailing slash.
117-
///
118-
/// In most cases, [`Self::new`] is good enough. However, if you do not want
119-
/// to automatically respond to requests for directories with `index.html`,
120-
/// this method is provided.
134+
/// - `|f, _| f.is_visible()`: Serve only visible files (hide dotfiles).
135+
/// - [`Prefix::checked(path)`]: Prefix requests with `path`.
136+
/// - [`TrailingDirs`]: Ensure directory have a trailing slash.
121137
///
122138
/// # Example
123139
///
124-
/// Constructs a default file server to server files from `./static`, but
125-
/// uses `index.txt` if `index.html` doesn't exist.
140+
/// Constructs a default file server to serve files from `./static` using
141+
/// `index.txt` as the index file if `index.html` doesn't exist.
126142
///
127143
/// ```rust,no_run
128144
/// # #[macro_use] extern crate rocket;
@@ -141,15 +157,43 @@ impl FileServer {
141157
///
142158
/// [`Prefix::checked(path)`]: crate::fs::rewrite::Prefix::checked
143159
/// [`TrailingDirs`]: crate::fs::rewrite::TrailingDirs
144-
pub fn new_without_index<P: AsRef<Path>>(path: P) -> Self {
145-
Self::empty()
160+
pub fn without_index<P: AsRef<Path>>(path: P) -> Self {
161+
Self::identity()
146162
.filter(|f, _| f.is_visible())
147163
.rewrite(Prefix::checked(path))
148164
.rewrite(TrailingDirs)
149165
}
150166

151-
/// Constructs a new `FileServer`, with default rank, and no rewrites.
152-
pub fn empty() -> Self {
167+
/// Constructs a new `FileServer` with no rewrites.
168+
///
169+
/// Without any rewrites, a `FileServer` will try to serve the requested
170+
/// file from the current working directory. In other words, it represents
171+
/// the identity rewrite. For example, a request `GET /foo/bar` will be
172+
/// passed through unmodified and thus `./foo/bar` will be served. This is
173+
/// very unlikely to be what you want.
174+
///
175+
/// Prefer to use [`FileServer::new()`] or [`FileServer::without_index()`]
176+
/// whenever possible and otherwise use one or more of the rewrites in
177+
/// [`rocket::fs::rewrite`] or your own custom rewrites.
178+
///
179+
/// # Example
180+
///
181+
/// ```rust,no_run
182+
/// # #[macro_use] extern crate rocket;
183+
/// use rocket::fs::{FileServer, rewrite};
184+
///
185+
/// #[launch]
186+
/// fn rocket() -> _ {
187+
/// // A file server that serves exactly one file: /www/foo.html. The
188+
/// // file is served irrespective of what's requested.
189+
/// let server = FileServer::identity()
190+
/// .rewrite(rewrite::File::checked("/www/foo.html"));
191+
///
192+
/// rocket::build()
193+
/// .mount("/", server)
194+
/// }
195+
/// ```
196+
pub fn identity() -> Self {
153197
Self {
154198
rewrites: vec![],
155199
rank: Self::DEFAULT_RANK
@@ -163,34 +207,34 @@ impl FileServer {
163207
/// ```rust,no_run
164208
/// # use rocket::fs::FileServer;
165209
/// # fn make_server() -> FileServer {
166-
/// FileServer::empty()
210+
/// FileServer::identity()
167211
/// .rank(5)
168212
/// # }
169213
pub fn rank(mut self, rank: isize) -> Self {
170214
self.rank = rank;
171215
self
172216
}
173217

174-
/// Generic rewrite to transform one Rewrite to another.
218+
/// Add `rewriter` to the rewrite pipeline.
175219
///
176220
/// # Example
177221
///
178-
/// Redirects all requests that have been filtered to the root of the `FileServer`.
222+
/// Redirect filtered requests (`None`) to `/`.
179223
///
180224
/// ```rust,no_run
181-
/// # use rocket::fs::{FileServer, rewrite::Rewrite};
182-
/// # use rocket::{response::Redirect, uri, Build, Rocket, Request};
225+
/// # #[macro_use] extern crate rocket;
226+
/// use rocket::fs::{FileServer, rewrite::Rewrite};
227+
/// use rocket::{request::Request, response::Redirect};
228+
///
183229
/// fn redir_missing<'r>(p: Option<Rewrite<'r>>, _req: &Request<'_>) -> Option<Rewrite<'r>> {
184-
/// match p {
185-
/// None => Redirect::temporary(uri!("/")).into(),
186-
/// p => p,
187-
/// }
230+
/// Some(p.unwrap_or_else(|| Redirect::temporary(uri!("/")).into()))
188231
/// }
189232
///
190-
/// # fn launch() -> Rocket<Build> {
191-
/// rocket::build()
192-
/// .mount("/", FileServer::new("static").rewrite(redir_missing))
193-
/// # }
233+
/// #[launch]
234+
/// fn rocket() -> _ {
235+
/// rocket::build()
236+
/// .mount("/", FileServer::new("static").rewrite(redir_missing))
237+
/// }
194238
/// ```
195239
///
196240
/// Note that `redir_missing` is not a closure in this example. Making it a closure
@@ -200,11 +244,13 @@ impl FileServer {
200244
self
201245
}
202246

203-
/// Filter what files this `FileServer` will respond with
247+
/// Adds a rewriter to the pipeline that returns `Some` only when the
248+
/// function `f` returns `true`, filtering out all other files.
204249
///
205250
/// # Example
206251
///
207-
/// Filter out all paths with a filename of `hidden`.
252+
/// Allow all files that don't have a file name or have a file name other
253+
/// than "hidden".
208254
///
209255
/// ```rust,no_run
210256
/// # #[macro_use] extern crate rocket;
@@ -238,7 +284,9 @@ impl FileServer {
238284
self.rewrite(Filter(f))
239285
}
240286

241-
/// Change what files this `FileServer` will respond with
287+
/// Adds a rewriter to the pipeline that maps the current `File` to another
288+
/// `Rewrite` using `f`. If the current `Rewrite` is a `Redirect`, it is
289+
/// passed through without calling `f`.
242290
///
243291
/// # Example
244292
///

0 commit comments

Comments
 (0)