@@ -5,7 +5,14 @@ use std::sync::Arc;
5
5
6
6
use crate :: fs:: NamedFile ;
7
7
use crate :: { Data , Request , outcome:: IntoOutcome } ;
8
- use crate :: http:: { Method , HeaderMap , Header , uri:: { Segments , Reference } , Status } ;
8
+ use crate :: http:: {
9
+ Method ,
10
+ HeaderMap ,
11
+ Header ,
12
+ uri:: { Segments , Origin } ,
13
+ Status ,
14
+ ext:: IntoOwned ,
15
+ } ;
9
16
use crate :: route:: { Route , Handler , Outcome } ;
10
17
use crate :: response:: { Redirect , Responder } ;
11
18
@@ -117,9 +124,9 @@ pub trait Rewriter: Send + Sync + 'static {
117
124
pub enum FileResponse < ' p , ' h > {
118
125
/// Return the contents of the specified file.
119
126
File ( File < ' p , ' h > ) ,
120
- /// Returns a Redirect to the specified path. This will be appended to the
121
- /// [`FileServer`]'s base uri , so the redirect uri is relative to wherever
122
- /// the [`FileServer`] is mounted .
127
+ /// Returns a Redirect to the specified path. This needs to be an absolute
128
+ /// URI , so you should start with [`File.full_uri`](File) when constructing
129
+ /// a redirect .
123
130
Redirect ( Redirect ) ,
124
131
}
125
132
@@ -150,8 +157,8 @@ impl<'p, 'h> From<Redirect> for Option<FileResponse<'p, 'h>> {
150
157
pub struct File < ' p , ' h > {
151
158
/// The path to the file that [`FileServer`] will respond with.
152
159
pub path : Cow < ' p , Path > ,
153
- /// The original path this File started out with.
154
- pub starting_path : & ' p Path ,
160
+ /// The original uri from the [`Request`]
161
+ pub full_uri : & ' p Origin < ' p > ,
155
162
/// A list of headers to be added to the generated response.
156
163
pub headers : HeaderMap < ' h > ,
157
164
}
@@ -167,7 +174,7 @@ impl<'p, 'h> File<'p, 'h> {
167
174
pub fn with_path ( self , path : impl Into < Cow < ' p , Path > > ) -> Self {
168
175
Self {
169
176
path : path. into ( ) ,
170
- starting_path : self . starting_path ,
177
+ full_uri : self . full_uri ,
171
178
headers : self . headers ,
172
179
}
173
180
}
@@ -176,10 +183,17 @@ impl<'p, 'h> File<'p, 'h> {
176
183
pub fn map_path < R : Into < Cow < ' p , Path > > > ( self , f : impl FnOnce ( Cow < ' p , Path > ) -> R ) -> Self {
177
184
Self {
178
185
path : f ( self . path ) . into ( ) ,
179
- starting_path : self . starting_path ,
186
+ full_uri : self . full_uri ,
180
187
headers : self . headers ,
181
188
}
182
189
}
190
+
191
+ /// Convert this `File` into a Redirect, transforming the URI.
192
+ pub fn into_redirect ( self , f : impl FnOnce ( Origin < ' static > ) -> Origin < ' static > )
193
+ -> FileResponse < ' p , ' h >
194
+ {
195
+ FileResponse :: Redirect ( Redirect :: permanent ( f ( self . full_uri . clone ( ) . into_owned ( ) ) ) )
196
+ }
183
197
}
184
198
185
199
impl < F : Send + Sync + ' static > Rewriter for F
@@ -339,8 +353,9 @@ pub fn filter_dotfiles(file: &File<'_, '_>) -> bool {
339
353
/// # }
340
354
/// ```
341
355
pub fn normalize_dirs < ' p , ' h > ( file : File < ' p , ' h > ) -> FileResponse < ' p , ' h > {
342
- if file. path . is_dir ( ) && !file. path . as_os_str ( ) . as_encoded_bytes ( ) . ends_with ( b"/" ) {
343
- FileResponse :: Redirect ( Redirect :: permanent ( format ! ( "{}/" , file. starting_path. display( ) ) ) )
356
+ if !file. full_uri . has_trailing_slash ( ) && file. path . is_dir ( ) {
357
+ // Known good path + '/' is a good path
358
+ file. into_redirect ( |o| o. map_path ( |p| format ! ( "{p}/" ) ) . unwrap ( ) )
344
359
} else {
345
360
FileResponse :: File ( file)
346
361
}
@@ -521,7 +536,7 @@ impl Handler for FileServer {
521
536
. and_then ( |segments| segments. to_path_buf ( true ) . ok ( ) ) ;
522
537
let mut response = path. as_ref ( ) . map ( |p| FileResponse :: File ( File {
523
538
path : Cow :: Borrowed ( p) ,
524
- starting_path : p ,
539
+ full_uri : req . uri ( ) ,
525
540
headers : HeaderMap :: new ( ) ,
526
541
} ) ) ;
527
542
@@ -538,9 +553,7 @@ impl Handler for FileServer {
538
553
} ) . or_forward ( ( data, Status :: NotFound ) )
539
554
} ,
540
555
Some ( FileResponse :: Redirect ( r) ) => {
541
- let base = req. route ( ) . unwrap ( ) . uri . base ( ) ;
542
- r. map_uri ( |u| Reference :: parse_owned ( format ! ( "{}/{}" , base, u) ) . unwrap ( ) )
543
- . respond_to ( req)
556
+ r. respond_to ( req)
544
557
. or_forward ( ( data, Status :: InternalServerError ) )
545
558
} ,
546
559
_ => Outcome :: forward ( data, Status :: NotFound ) ,
0 commit comments