@@ -20,7 +20,7 @@ use mime_guess::{get_mime_type, guess_mime_type};
20
20
use error:: Error ;
21
21
use handler:: { AsyncResult , Handler , Responder , RouteHandler , WrapHandler } ;
22
22
use header;
23
- use http:: { Method , StatusCode } ;
23
+ use http:: { HttpRange , Method , StatusCode } ;
24
24
use httpmessage:: HttpMessage ;
25
25
use httprequest:: HttpRequest ;
26
26
use httpresponse:: HttpResponse ;
@@ -209,7 +209,7 @@ impl Responder for NamedFile {
209
209
} ) . if_some ( self . path ( ) . file_name ( ) , |file_name, resp| {
210
210
let mime_type = guess_mime_type ( self . path ( ) ) ;
211
211
let inline_or_attachment = match mime_type. type_ ( ) {
212
- mime:: IMAGE | mime:: TEXT => "inline" ,
212
+ mime:: IMAGE | mime:: TEXT | mime :: VIDEO => "inline" ,
213
213
_ => "attachment" ,
214
214
} ;
215
215
resp. header (
@@ -228,6 +228,7 @@ impl Responder for NamedFile {
228
228
. unwrap_or_else ( || req. cpu_pool ( ) . clone ( ) ) ,
229
229
file : Some ( self . file ) ,
230
230
fut : None ,
231
+ counter : 0 ,
231
232
} ;
232
233
return Ok ( resp. streaming ( reader) ) ;
233
234
}
@@ -274,7 +275,7 @@ impl Responder for NamedFile {
274
275
} ) . if_some ( self . path ( ) . file_name ( ) , |file_name, resp| {
275
276
let mime_type = guess_mime_type ( self . path ( ) ) ;
276
277
let inline_or_attachment = match mime_type. type_ ( ) {
277
- mime:: IMAGE | mime:: TEXT => "inline" ,
278
+ mime:: IMAGE | mime:: TEXT | mime :: VIDEO => "inline" ,
278
279
_ => "attachment" ,
279
280
} ;
280
281
resp. header (
@@ -292,6 +293,31 @@ impl Responder for NamedFile {
292
293
. if_some ( etag, |etag, resp| {
293
294
resp. set ( header:: ETag ( etag) ) ;
294
295
} ) ;
296
+
297
+ // TODO: Debug, enabling "accept-ranges: bytes" causes problems with
298
+ // certain clients when not using the ranges header.
299
+ //resp.header(header::ACCEPT_RANGES, format!("bytes"));
300
+
301
+ let mut length = self . md . len ( ) ;
302
+ let mut offset = 0 ;
303
+
304
+ // check for ranges header
305
+ if let Some ( ranges) = req. headers ( ) . get ( header:: RANGE ) {
306
+ if let Ok ( rangesheader) = ranges. to_str ( ) {
307
+ if let Ok ( rangesvec) = HttpRange :: parse ( rangesheader, length) {
308
+ length = rangesvec[ 0 ] . length - 1 ;
309
+ offset = rangesvec[ 0 ] . start ;
310
+ resp. header ( header:: RANGE , format ! ( "bytes={}-{}/{}" , offset, offset+length, self . md. len( ) ) ) ;
311
+ } else {
312
+ resp. header ( header:: RANGE , format ! ( "*/{}" , length) ) ;
313
+ return Ok ( resp. status ( StatusCode :: RANGE_NOT_SATISFIABLE ) . finish ( ) ) ;
314
+ } ;
315
+ } else {
316
+ return Ok ( resp. status ( StatusCode :: BAD_REQUEST ) . finish ( ) ) ;
317
+ } ;
318
+ } ;
319
+
320
+ resp. header ( header:: CONTENT_LENGTH , format ! ( "{}" , length) ) ;
295
321
296
322
if precondition_failed {
297
323
return Ok ( resp. status ( StatusCode :: PRECONDITION_FAILED ) . finish ( ) ) ;
@@ -303,12 +329,16 @@ impl Responder for NamedFile {
303
329
Ok ( resp. finish ( ) )
304
330
} else {
305
331
let reader = ChunkedReadFile {
306
- size : self . md . len ( ) ,
307
- offset : 0 ,
332
+ size : length ,
333
+ offset : offset ,
308
334
cpu_pool : self . cpu_pool
309
335
. unwrap_or_else ( || req. cpu_pool ( ) . clone ( ) ) ,
310
336
file : Some ( self . file ) ,
311
337
fut : None ,
338
+ counter : 0 ,
339
+ } ;
340
+ if offset != 0 || length != self . md . len ( ) {
341
+ return Ok ( resp. status ( StatusCode :: PARTIAL_CONTENT ) . streaming ( reader) ) ;
312
342
} ;
313
343
Ok ( resp. streaming ( reader) )
314
344
}
@@ -323,6 +353,7 @@ pub struct ChunkedReadFile {
323
353
cpu_pool : CpuPool ,
324
354
file : Option < File > ,
325
355
fut : Option < CpuFuture < ( File , Bytes ) , io:: Error > > ,
356
+ counter : u64 ,
326
357
}
327
358
328
359
impl Stream for ChunkedReadFile {
@@ -336,6 +367,7 @@ impl Stream for ChunkedReadFile {
336
367
self . fut . take ( ) ;
337
368
self . file = Some ( file) ;
338
369
self . offset += bytes. len ( ) as u64 ;
370
+ self . counter += bytes. len ( ) as u64 ;
339
371
Ok ( Async :: Ready ( Some ( bytes) ) )
340
372
}
341
373
Async :: NotReady => Ok ( Async :: NotReady ) ,
@@ -344,14 +376,16 @@ impl Stream for ChunkedReadFile {
344
376
345
377
let size = self . size ;
346
378
let offset = self . offset ;
379
+ let counter = self . counter ;
347
380
348
- if size == offset {
381
+ if size == counter {
349
382
Ok ( Async :: Ready ( None ) )
350
383
} else {
351
384
let mut file = self . file . take ( ) . expect ( "Use after completion" ) ;
352
385
self . fut = Some ( self . cpu_pool . spawn_fn ( move || {
353
- let max_bytes = cmp:: min ( size. saturating_sub ( offset) , 65_536 ) as usize ;
354
- let mut buf = BytesMut :: with_capacity ( max_bytes) ;
386
+ let max_bytes: usize ;
387
+ max_bytes = cmp:: min ( size. saturating_sub ( counter) , 65_536 ) as usize ;
388
+ let mut buf = BytesMut :: from ( Vec :: with_capacity ( max_bytes) ) ;
355
389
file. seek ( io:: SeekFrom :: Start ( offset) ) ?;
356
390
let nbytes = file. read ( unsafe { buf. bytes_mut ( ) } ) ?;
357
391
if nbytes == 0 {
@@ -742,6 +776,49 @@ mod tests {
742
776
) ;
743
777
assert_eq ! ( resp. status( ) , StatusCode :: NOT_FOUND ) ;
744
778
}
779
+
780
+ #[ test]
781
+ fn test_named_file_ranges_status_code ( ) {
782
+ let mut srv = test:: TestServer :: with_factory ( || {
783
+ App :: new ( ) . handler ( "test" , StaticFiles :: new ( "." ) . index_file ( "Cargo.toml" ) )
784
+ } ) ;
785
+
786
+ let request = srv. get ( )
787
+ . uri ( srv. url ( "/t%65st/Cargo.toml" ) )
788
+ . header ( header:: RANGE , "bytes=10-20" )
789
+ . finish ( )
790
+ . unwrap ( ) ;
791
+ let response = srv. execute ( request. send ( ) ) . unwrap ( ) ;
792
+
793
+ assert_eq ! ( response. status( ) , StatusCode :: PARTIAL_CONTENT ) ;
794
+ }
795
+
796
+ #[ test]
797
+ fn test_named_file_ranges_headers ( ) {
798
+ let mut srv = test:: TestServer :: with_factory ( || {
799
+ App :: new ( ) . handler ( "test" , StaticFiles :: new ( "." ) . index_file ( "tests/test.binary" ) )
800
+ } ) ;
801
+
802
+ let request = srv. get ( )
803
+ . uri ( srv. url ( "/t%65st/tests/test.binary" ) )
804
+ . header ( header:: RANGE , "bytes=10-20" )
805
+ . finish ( )
806
+ . unwrap ( ) ;
807
+ let response = srv. execute ( request. send ( ) ) . unwrap ( ) ;
808
+ let contentlength = response. headers ( ) . get ( header:: CONTENT_LENGTH ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
809
+
810
+ assert_eq ! ( contentlength, "10" ) ;
811
+
812
+ let request = srv. get ( )
813
+ . uri ( srv. url ( "/t%65st/tests/test.binary" ) )
814
+ . header ( header:: RANGE , "bytes=10-20" )
815
+ . finish ( )
816
+ . unwrap ( ) ;
817
+ let response = srv. execute ( request. send ( ) ) . unwrap ( ) ;
818
+ let range = response. headers ( ) . get ( header:: RANGE ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
819
+
820
+ assert_eq ! ( range, "bytes=10-20/100" ) ;
821
+ }
745
822
746
823
#[ test]
747
824
fn test_named_file_not_allowed ( ) {
0 commit comments