15
15
//!
16
16
//! let client = reqwest::Client::new();
17
17
//! let request = client.get("http://httpbin.org/base64/aGVsbG8gd29ybGQ=");
18
- //! let mut file = RequestFile::new(request);
18
+ //! let mut file: RequestFile = RequestFile::new(request);
19
19
//!
20
20
//! let mut buffer = [0; 5];
21
21
//! assert_eq!(file.read(&mut buffer).await.unwrap(), 5);
@@ -126,16 +126,16 @@ fn send_request(request: &RequestBuilder, offset: u64) -> RequestStreamFuture {
126
126
///
127
127
/// Return `Ok` if the fastforward is complete,
128
128
/// or if EOF is reached (at the first empty read).
129
- fn fastforward < R : AsyncRead > (
129
+ fn fastforward < R : AsyncRead , const BUFFER : usize > (
130
130
mut reader : Pin < & mut R > ,
131
131
delta : u64 ,
132
132
context : & mut Context < ' _ > ,
133
133
) -> ( u64 , u64 , Poll < Result < ( ) , IoError > > ) {
134
- let mut array = [ std:: mem:: MaybeUninit :: uninit ( ) ; FF_BUFFER ] ;
134
+ let mut array = [ std:: mem:: MaybeUninit :: uninit ( ) ; BUFFER ] ;
135
135
let mut remaining = delta;
136
136
let poll = loop {
137
137
assert ! ( remaining > 0 ) ;
138
- let buffer_size = ( remaining as usize ) . min ( FF_BUFFER ) ;
138
+ let buffer_size = ( remaining as usize ) . min ( BUFFER ) ;
139
139
let mut buffer = ReadBuf :: uninit ( & mut array[ 0 ..buffer_size] ) ;
140
140
match reader. as_mut ( ) . poll_read ( context, & mut buffer) {
141
141
Poll :: Ready ( Ok ( ( ) ) ) => {
@@ -160,10 +160,10 @@ fn fastforward<R: AsyncRead>(
160
160
}
161
161
162
162
/// The maximum fast-forward read length.
163
- const FF_WINDOW : u64 = 128 * 1024 ;
163
+ const DEFAULT_FF_WINDOW : u64 = 128 * 1024 ;
164
164
165
165
/// The size of the fast-forward read buffer.
166
- const FF_BUFFER : usize = 4096 ;
166
+ const DEFAULT_FF_BUFFER : usize = 4096 ;
167
167
168
168
/// State of the request file.
169
169
enum State < P , R > {
@@ -236,19 +236,35 @@ enum State<P, R> {
236
236
/// ## Fast-Forward
237
237
///
238
238
/// As an optimization, seeking forward by a small amount
239
- /// (currently less than 128KiB) will not perform
239
+ /// (by default up to 128KiB) will not perform
240
240
/// a new request, but rather fast-forward through
241
241
/// the response body.
242
242
///
243
243
/// This type of seek is always allowed,
244
- /// even if the webserver does not support `Range` requests
244
+ /// even if the webserver does not support `Range` requests.
245
+ ///
246
+ /// ###### Customization
247
+ ///
248
+ /// The settings for fast-forwards can be changed through
249
+ /// two constant parameters.
250
+ ///
251
+ /// * `FF_WINDOW` limits how much can be fast-forwarded;
252
+ /// only seeking up to this number of bytes forwards
253
+ /// will read through the request (discarding the data)
254
+ /// to avoid sending out a new request.
255
+ ///
256
+ /// * `FF_BUFFER` defines the internal buffer size
257
+ /// used to read into during a fast-foward.
245
258
///
246
259
/// [`NotSeekable`]: std::io::ErrorKind::NotSeekable
247
260
/// [`Unsupported`]: std::io::ErrorKind::Unsupported
248
261
/// [`InvalidInput`]: std::io::ErrorKind::InvalidInput
249
262
/// [`Other`]: std::io::ErrorKind::Other
250
263
#[ pin_project( project = RequestFileProjection ) ]
251
- pub struct RequestFile {
264
+ pub struct RequestFile <
265
+ const FF_WINDOW : u64 = DEFAULT_FF_WINDOW ,
266
+ const FF_BUFFER : usize = DEFAULT_FF_BUFFER ,
267
+ > {
252
268
/// The request template.
253
269
request : RequestBuilder ,
254
270
/// The state of the HTTP request.
@@ -259,7 +275,11 @@ pub struct RequestFile {
259
275
position : u64 ,
260
276
}
261
277
262
- impl RequestFile {
278
+ impl <
279
+ const FF_WINDOW : u64 ,
280
+ const FF_BUFFER : usize ,
281
+ >
282
+ RequestFile < FF_WINDOW , FF_BUFFER > {
263
283
/// Create a new file-like object for a web resource.
264
284
///
265
285
/// # Panics
@@ -328,7 +348,11 @@ impl RequestFile {
328
348
}
329
349
}
330
350
331
- impl RequestFileProjection < ' _ > {
351
+ impl <
352
+ const FF_WINDOW : u64 ,
353
+ const FF_BUFFER : usize ,
354
+ >
355
+ RequestFileProjection < ' _ , FF_WINDOW , FF_BUFFER > {
332
356
/// Compute the absolute seek position.
333
357
fn resolve_seek_position (
334
358
& self ,
@@ -396,7 +420,7 @@ impl RequestFileProjection<'_> {
396
420
) -> Poll < Result < ( ) , IoError > > {
397
421
match std:: mem:: replace ( self . state , State :: Transient ) {
398
422
State :: Seeking ( mut reader, delta) => {
399
- let ( read, remaining, poll) = fastforward (
423
+ let ( read, remaining, poll) = fastforward :: < _ , { FF_BUFFER } > (
400
424
reader. as_mut ( ) , delta, context) ;
401
425
* self . position = self . position . saturating_add ( read) ;
402
426
match poll {
@@ -443,7 +467,8 @@ impl RequestFileProjection<'_> {
443
467
}
444
468
}
445
469
446
- impl AsyncRead for RequestFile {
470
+ impl < const FF_WINDOW : u64 , const FF_BUFFER : usize > AsyncRead
471
+ for RequestFile < FF_WINDOW , FF_BUFFER > {
447
472
fn poll_read (
448
473
self : Pin < & mut Self > ,
449
474
context : & mut Context < ' _ > ,
@@ -467,7 +492,8 @@ impl AsyncRead for RequestFile {
467
492
}
468
493
}
469
494
470
- impl AsyncSeek for RequestFile {
495
+ impl < const FF_WINDOW : u64 , const FF_BUFFER : usize > AsyncSeek
496
+ for RequestFile < FF_WINDOW , FF_BUFFER > {
471
497
fn start_seek (
472
498
self : Pin < & mut Self > ,
473
499
position : SeekFrom ,
@@ -590,13 +616,41 @@ mod tests {
590
616
macro_rules! test {
591
617
(
592
618
$name: ident
593
- [ $( SeekFrom :: $seek_from: ident( $offset: literal) => ( $( $tell: tt) * ) ) ;* $( ; ) ? ]
619
+ $( $token: tt ) *
620
+ ) => {
621
+ // Make versions of the test for various const param combos.
622
+ test!(
623
+ @concrete
624
+ $name
625
+ $( $token ) *
626
+ ) ;
627
+ test!(
628
+ @concrete
629
+ @ff_window 2
630
+ @ff_buffer 1
631
+ $name
632
+ $( $token ) *
633
+ ) ;
634
+ } ;
635
+ (
636
+ @concrete
637
+ $( @ff_window $ff_window: literal ) ?
638
+ $( @ff_buffer $ff_buffer: literal ) ?
639
+ $name: ident
640
+ [
641
+ $( SeekFrom :: $seek_from: ident( $offset: literal)
642
+ => ( $( $tell: tt) * ) ) ;* $( ; ) ?
643
+ ]
594
644
$( Content -Length = $content_length: literal ) ?
595
645
$( Content -Range = $content_range: literal ) ?
596
646
$data: literal => $result: tt
597
- ) => {
647
+ ) => { paste :: paste! {
598
648
#[ tokio:: test]
599
- async fn $name( ) {
649
+ async fn [ <
650
+ $name
651
+ $( _ff_window_ $ff_window ) ?
652
+ $( _ff_buffer_ $ff_buffer ) ?
653
+ >] ( ) {
600
654
let url = start_server( ) ;
601
655
let data = $data;
602
656
let client = reqwest:: Client :: new( ) ;
@@ -615,7 +669,12 @@ mod tests {
615
669
) ?
616
670
617
671
let request = client. get( format!( "{url}/?data={data}{params}" ) ) ;
618
- let mut file = RequestFile :: new( request) ;
672
+ const FF_WINDOW : u64 = super :: DEFAULT_FF_WINDOW
673
+ $( + $ff_window - super :: DEFAULT_FF_WINDOW ) ?;
674
+ const FF_BUFFER : usize = super :: DEFAULT_FF_BUFFER
675
+ $( + $ff_buffer - super :: DEFAULT_FF_BUFFER ) ?;
676
+ let mut file: RequestFile :: <FF_WINDOW , FF_BUFFER >
677
+ = RequestFile :: new( request) ;
619
678
620
679
$(
621
680
let seek_from = SeekFrom :: $seek_from( $offset) ;
@@ -627,7 +686,7 @@ mod tests {
627
686
let read_result = file. read_to_string( & mut response_data) . await ;
628
687
test!( @check_read read_result response_data $result) ;
629
688
}
630
- } ;
689
+ } } ;
631
690
(
632
691
@check_seek $seek: ident $result: literal
633
692
) => {
@@ -829,7 +888,7 @@ mod tests {
829
888
let client = reqwest:: Client :: new ( ) ;
830
889
let data = "abc" ;
831
890
let request = client. get ( format ! ( "{url}/?data={data}" ) ) ;
832
- let mut file = RequestFile :: new ( request) ;
891
+ let mut file: RequestFile = RequestFile :: new ( request) ;
833
892
834
893
let mut response_data = String :: new ( ) ;
835
894
file. read_to_string ( & mut response_data) . await . expect ( "read error" ) ;
@@ -846,7 +905,7 @@ mod tests {
846
905
let client = reqwest:: Client :: new ( ) ;
847
906
let data = "abcd" ;
848
907
let request = client. get ( format ! ( "{url}/?data={data}" ) ) ;
849
- let mut file = RequestFile :: new ( request) ;
908
+ let mut file: RequestFile = RequestFile :: new ( request) ;
850
909
851
910
assert_eq ! ( file. size( ) , None ) ;
852
911
file. prepare ( ) . await . unwrap ( ) ;
@@ -859,7 +918,7 @@ mod tests {
859
918
let client = reqwest:: Client :: new ( ) ;
860
919
let data = "abcd" ;
861
920
let request = client. get ( format ! ( "{url}/?data={data}&content_length=true" ) ) ;
862
- let mut file = RequestFile :: new ( request) ;
921
+ let mut file: RequestFile = RequestFile :: new ( request) ;
863
922
864
923
assert_eq ! ( file. size( ) , None ) ;
865
924
file. seek ( SeekFrom :: Start ( 2 ) ) . await . unwrap ( ) ;
@@ -872,7 +931,7 @@ mod tests {
872
931
let client = reqwest:: Client :: new ( ) ;
873
932
let data = "abcd" ;
874
933
let request = client. get ( format ! ( "{url}/?data={data}&content_range=true" ) ) ;
875
- let mut file = RequestFile :: new ( request) ;
934
+ let mut file: RequestFile = RequestFile :: new ( request) ;
876
935
877
936
assert_eq ! ( file. size( ) , None ) ;
878
937
file. seek ( SeekFrom :: Start ( 2 ) ) . await . unwrap ( ) ;
@@ -885,7 +944,7 @@ mod tests {
885
944
let url = start_server ( ) ;
886
945
let client = reqwest:: Client :: new ( ) ;
887
946
let request = client. get ( format ! ( "{url}/404" ) ) ;
888
- let mut file = RequestFile :: new ( request) ;
947
+ let mut file: RequestFile = RequestFile :: new ( request) ;
889
948
890
949
match file. prepare ( ) . await {
891
950
Ok ( ( ) ) => unreachable ! ( ) ,
@@ -906,7 +965,7 @@ mod tests {
906
965
let url = start_server ( ) ;
907
966
let client = reqwest:: Client :: new ( ) ;
908
967
let request = client. get ( format ! ( "{url}/?data=abc&status=204" ) ) ;
909
- let mut file = RequestFile :: new ( request) ;
968
+ let mut file: RequestFile = RequestFile :: new ( request) ;
910
969
911
970
match file. prepare ( ) . await {
912
971
Ok ( ( ) ) => unreachable ! ( ) ,
@@ -926,7 +985,7 @@ mod tests {
926
985
let url = start_server ( ) ;
927
986
let client = reqwest:: Client :: new ( ) ;
928
987
let request = client. get ( format ! ( "{url}/no-range-support" ) ) ;
929
- let mut file = RequestFile :: new ( request) ;
988
+ let mut file: RequestFile = RequestFile :: new ( request) ;
930
989
931
990
file. seek ( SeekFrom :: Start ( 0 ) ) . await . expect ( "seek error" ) ;
932
991
let data = read ( & mut file) . await . expect ( "read error" ) ;
@@ -939,7 +998,7 @@ mod tests {
939
998
let url = start_server ( ) ;
940
999
let client = reqwest:: Client :: new ( ) ;
941
1000
let request = client. get ( format ! ( "{url}/no-range-support" ) ) ;
942
- let mut file = RequestFile :: new ( request) ;
1001
+ let mut file: RequestFile = RequestFile :: new ( request) ;
943
1002
944
1003
// seek beyond the fastforward window
945
1004
file. seek ( SeekFrom :: Start ( 1_000_000_000 ) ) . await . expect ( "seek error" ) ;
@@ -950,7 +1009,7 @@ mod tests {
950
1009
let url = start_server ( ) ;
951
1010
let client = reqwest:: Client :: new ( ) ;
952
1011
let request = client. get ( format ! ( "{url}/?data=abc" ) ) ;
953
- let mut file = RequestFile :: new ( request) ;
1012
+ let mut file: RequestFile = RequestFile :: new ( request) ;
954
1013
955
1014
let seek_from = SeekFrom :: Start ( u64:: MAX - 10 ) ;
956
1015
let pos = file. seek ( seek_from) . await . expect ( "seek error" ) ;
@@ -965,7 +1024,7 @@ mod tests {
965
1024
let url = start_server ( ) ;
966
1025
let client = reqwest:: Client :: new ( ) ;
967
1026
let request = client. get ( format ! ( "{url}/?data=abc" ) ) ;
968
- let mut file = RequestFile :: with_size ( request, u64:: MAX - 10 ) ;
1027
+ let mut file: RequestFile = RequestFile :: with_size ( request, u64:: MAX - 10 ) ;
969
1028
970
1029
let pos = file. seek ( SeekFrom :: End ( 20 ) ) . await . expect ( "seek error" ) ;
971
1030
assert_eq ! ( pos, u64 :: MAX ) ;
@@ -975,7 +1034,7 @@ mod tests {
975
1034
async fn test_reset ( ) {
976
1035
let client = reqwest:: Client :: new ( ) ;
977
1036
let request = client. get ( "http://example.com/" ) ;
978
- let mut file = RequestFile :: new ( request) ;
1037
+ let mut file: RequestFile = RequestFile :: new ( request) ;
979
1038
file. reset ( ) ;
980
1039
}
981
1040
}
0 commit comments