Skip to content

Commit e5249a2

Browse files
committed
Add prepend_path option
1 parent bb0d1b2 commit e5249a2

File tree

3 files changed

+40
-2
lines changed

3 files changed

+40
-2
lines changed

tower-http/src/services/fs/serve_dir/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const DEFAULT_CAPACITY: usize = 65536;
5252
#[derive(Clone, Debug)]
5353
pub struct ServeDir<F = DefaultServeDirFallback> {
5454
base: PathBuf,
55+
prepend_path: String,
5556
buf_chunk_size: usize,
5657
precompressed_variants: Option<PrecompressedVariants>,
5758
// This is used to specialise implementation for
@@ -72,6 +73,7 @@ impl ServeDir<DefaultServeDirFallback> {
7273

7374
Self {
7475
base,
76+
prepend_path: "".to_string(),
7577
buf_chunk_size: DEFAULT_CAPACITY,
7678
precompressed_variants: None,
7779
variant: ServeVariant::Directory {
@@ -88,6 +90,7 @@ impl ServeDir<DefaultServeDirFallback> {
8890
{
8991
Self {
9092
base: path.as_ref().to_owned(),
93+
prepend_path: "".to_string(),
9194
buf_chunk_size: DEFAULT_CAPACITY,
9295
precompressed_variants: None,
9396
variant: ServeVariant::SingleFile { mime },
@@ -115,6 +118,19 @@ impl<F> ServeDir<F> {
115118
}
116119
}
117120

121+
/// Sets a path to be prepended when performing a trailing slash redirect.
122+
///
123+
/// This is useful when you want to serve the files at another location than "/", for example
124+
/// when you are using multiple services and want this instance to handle `/static/<path>`.
125+
/// In that example, you should pass in "/static" so that a trailing slash redirect does not
126+
/// redirect to `/<path>/` but instead to `/static/<path>/`
127+
///
128+
/// The default is the empty string.
129+
pub fn prepend_path(mut self, path: String) -> Self {
130+
self.prepend_path = path;
131+
self
132+
}
133+
118134
/// Set a specific read buffer chunk size.
119135
///
120136
/// The default capacity is 64kb.
@@ -211,6 +227,7 @@ impl<F> ServeDir<F> {
211227
/// ```
212228
pub fn fallback<F2>(self, new_fallback: F2) -> ServeDir<F2> {
213229
ServeDir {
230+
prepend_path: "".to_string(),
214231
base: self.base,
215232
buf_chunk_size: self.buf_chunk_size,
216233
precompressed_variants: self.precompressed_variants,
@@ -358,6 +375,8 @@ impl<F> ServeDir<F> {
358375
}
359376
};
360377

378+
let prepend_path = self.prepend_path.clone();
379+
361380
let buf_chunk_size = self.buf_chunk_size;
362381
let range_header = req
363382
.headers()
@@ -375,6 +394,7 @@ impl<F> ServeDir<F> {
375394

376395
let open_file_future = Box::pin(open_file::open_file(
377396
variant,
397+
prepend_path,
378398
path_to_file,
379399
req,
380400
negotiated_encodings,

tower-http/src/services/fs/serve_dir/open_file.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub(super) enum FileRequestExtent {
4040

4141
pub(super) async fn open_file(
4242
variant: ServeVariant,
43+
prepend_path: String,
4344
mut path_to_file: PathBuf,
4445
req: Request<Empty<Bytes>>,
4546
negotiated_encodings: Vec<(Encoding, QValue)>,
@@ -64,6 +65,7 @@ pub(super) async fn open_file(
6465
// returned which corresponds to a Some(output). Otherwise the path might be
6566
// modified and proceed to the open file/metadata future.
6667
if let Some(output) = maybe_redirect_or_append_path(
68+
&prepend_path,
6769
&mut path_to_file,
6870
req.uri(),
6971
append_index_html_on_directories,
@@ -251,6 +253,7 @@ async fn file_metadata_with_fallback(
251253
}
252254

253255
async fn maybe_redirect_or_append_path(
256+
prepend_path: &str,
254257
path_to_file: &mut PathBuf,
255258
uri: &Uri,
256259
append_index_html_on_directories: bool,
@@ -267,8 +270,10 @@ async fn maybe_redirect_or_append_path(
267270
path_to_file.push("index.html");
268271
None
269272
} else {
270-
let location =
271-
HeaderValue::from_str(&append_slash_on_path(uri.clone()).to_string()).unwrap();
273+
let location_string = format!("{prepend_path}{}", append_slash_on_path(uri.clone()));
274+
275+
let location = HeaderValue::from_str(&location_string).unwrap();
276+
272277
Some(OpenFileOutput::Redirect { location })
273278
}
274279
}

tower-http/src/services/fs/serve_dir/tests.rs

+13
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,19 @@ async fn redirect_to_trailing_slash_on_dir() {
384384
assert_eq!(location, "/src/");
385385
}
386386

387+
#[tokio::test]
388+
async fn redirect_to_trailing_slash_with_prepend_path() {
389+
let svc = ServeDir::new(".").prepend_path("/foo".to_string());
390+
391+
let req = Request::builder().uri("/src").body(Body::empty()).unwrap();
392+
let res = svc.oneshot(req).await.unwrap();
393+
394+
assert_eq!(res.status(), StatusCode::TEMPORARY_REDIRECT);
395+
396+
let location = &res.headers()[http::header::LOCATION];
397+
assert_eq!(location, "/foo/src/");
398+
}
399+
387400
#[tokio::test]
388401
async fn empty_directory_without_index() {
389402
let svc = ServeDir::new(".").append_index_html_on_directories(false);

0 commit comments

Comments
 (0)