Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redirect_path_prefix option #486

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions tower-http/src/services/fs/serve_dir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const DEFAULT_CAPACITY: usize = 65536;
#[derive(Clone, Debug)]
pub struct ServeDir<F = DefaultServeDirFallback> {
base: PathBuf,
redirect_path_prefix: String,
buf_chunk_size: usize,
precompressed_variants: Option<PrecompressedVariants>,
// This is used to specialise implementation for
Expand All @@ -72,6 +73,7 @@ impl ServeDir<DefaultServeDirFallback> {

Self {
base,
redirect_path_prefix: "".to_string(),
buf_chunk_size: DEFAULT_CAPACITY,
precompressed_variants: None,
variant: ServeVariant::Directory {
Expand All @@ -88,6 +90,7 @@ impl ServeDir<DefaultServeDirFallback> {
{
Self {
base: path.as_ref().to_owned(),
redirect_path_prefix: "".to_string(),
buf_chunk_size: DEFAULT_CAPACITY,
precompressed_variants: None,
variant: ServeVariant::SingleFile { mime },
Expand Down Expand Up @@ -115,6 +118,19 @@ impl<F> ServeDir<F> {
}
}

/// Sets a path to be prepended when performing a trailing slash redirect.
///
/// This is useful when you want to serve the files at another location than "/", for example
/// when you are using multiple services and want this instance to handle `/static/<path>`.
/// In that example, you should pass in "/static" so that a trailing slash redirect does not
/// redirect to `/<path>/` but instead to `/static/<path>/`
///
/// The default is the empty string.
pub fn redirect_path_prefix(mut self, path: String) -> Self {
self.redirect_path_prefix = path;
self
}

/// Set a specific read buffer chunk size.
///
/// The default capacity is 64kb.
Expand Down Expand Up @@ -211,6 +227,7 @@ impl<F> ServeDir<F> {
/// ```
pub fn fallback<F2>(self, new_fallback: F2) -> ServeDir<F2> {
ServeDir {
redirect_path_prefix: "".to_string(),
base: self.base,
buf_chunk_size: self.buf_chunk_size,
precompressed_variants: self.precompressed_variants,
Expand Down Expand Up @@ -358,6 +375,8 @@ impl<F> ServeDir<F> {
}
};

let prepend_path = self.redirect_path_prefix.clone();

let buf_chunk_size = self.buf_chunk_size;
let range_header = req
.headers()
Expand All @@ -375,6 +394,7 @@ impl<F> ServeDir<F> {

let open_file_future = Box::pin(open_file::open_file(
variant,
prepend_path,
path_to_file,
req,
negotiated_encodings,
Expand Down
9 changes: 7 additions & 2 deletions tower-http/src/services/fs/serve_dir/open_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub(super) enum FileRequestExtent {

pub(super) async fn open_file(
variant: ServeVariant,
prepend_path: String,
mut path_to_file: PathBuf,
req: Request<Empty<Bytes>>,
negotiated_encodings: Vec<(Encoding, QValue)>,
Expand All @@ -64,6 +65,7 @@ pub(super) async fn open_file(
// returned which corresponds to a Some(output). Otherwise the path might be
// modified and proceed to the open file/metadata future.
if let Some(output) = maybe_redirect_or_append_path(
&prepend_path,
&mut path_to_file,
req.uri(),
append_index_html_on_directories,
Expand Down Expand Up @@ -251,6 +253,7 @@ async fn file_metadata_with_fallback(
}

async fn maybe_redirect_or_append_path(
prepend_path: &str,
path_to_file: &mut PathBuf,
uri: &Uri,
append_index_html_on_directories: bool,
Expand All @@ -267,8 +270,10 @@ async fn maybe_redirect_or_append_path(
path_to_file.push("index.html");
None
} else {
let location =
HeaderValue::from_str(&append_slash_on_path(uri.clone()).to_string()).unwrap();
let location_string = format!("{prepend_path}{}", append_slash_on_path(uri.clone()));

let location = HeaderValue::from_str(&location_string).unwrap();

Some(OpenFileOutput::Redirect { location })
}
}
Expand Down
13 changes: 13 additions & 0 deletions tower-http/src/services/fs/serve_dir/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,19 @@ async fn redirect_to_trailing_slash_on_dir() {
assert_eq!(location, "/src/");
}

#[tokio::test]
async fn redirect_to_trailing_slash_with_redirect_path_prefix() {
let svc = ServeDir::new(".").redirect_path_prefix("/foo".to_string());

let req = Request::builder().uri("/src").body(Body::empty()).unwrap();
let res = svc.oneshot(req).await.unwrap();

assert_eq!(res.status(), StatusCode::TEMPORARY_REDIRECT);

let location = &res.headers()[http::header::LOCATION];
assert_eq!(location, "/foo/src/");
}

#[tokio::test]
async fn empty_directory_without_index() {
let svc = ServeDir::new(".").append_index_html_on_directories(false);
Expand Down
Loading