1
- use async_std:: io:: { self , BufRead , Read } ;
2
- use async_std:: sync:: Arc ;
3
- use byte_pool:: { Block , BytePool } ;
4
1
use std:: fmt;
2
+ use std:: future:: Future ;
5
3
use std:: pin:: Pin ;
4
+ use std:: str:: FromStr ;
6
5
use std:: task:: { Context , Poll } ;
7
6
7
+ use async_std:: io:: { self , BufRead , Read } ;
8
+ use async_std:: sync:: { channel, Arc , Receiver , Sender } ;
9
+ use byte_pool:: { Block , BytePool } ;
10
+ use http_types:: headers:: { HeaderName , HeaderValue } ;
11
+
8
12
const INITIAL_CAPACITY : usize = 1024 * 4 ;
9
13
const MAX_CAPACITY : usize = 512 * 1024 * 1024 ; // 512 MiB
10
14
@@ -27,20 +31,33 @@ pub struct ChunkedDecoder<R: BufRead> {
27
31
/// Whether we should attempt to decode whatever is currently inside the buffer.
28
32
/// False indicates that we know for certain that the buffer is incomplete.
29
33
initial_decode : bool ,
34
+ /// Current state.
30
35
state : State ,
36
+ /// Trailer channel sender.
37
+ trailer_sender : Sender < ( HeaderName , HeaderValue ) > ,
38
+ /// Trailer channel receiver.
39
+ trailer_receiver : Receiver < ( HeaderName , HeaderValue ) > ,
31
40
}
32
41
33
42
impl < R : BufRead > ChunkedDecoder < R > {
34
43
pub fn new ( inner : R ) -> Self {
44
+ let ( sender, receiver) = channel ( 1 ) ;
45
+
35
46
ChunkedDecoder {
36
47
inner,
37
48
buffer : POOL . alloc ( INITIAL_CAPACITY ) ,
38
49
current : Position :: default ( ) ,
39
50
decode_needs : 0 ,
40
51
initial_decode : false , // buffer is empty initially, nothing to decode}
41
52
state : State :: Init ,
53
+ trailer_sender : sender,
54
+ trailer_receiver : receiver,
42
55
}
43
56
}
57
+
58
+ pub fn trailer ( & self ) -> Receiver < ( HeaderName , HeaderValue ) > {
59
+ self . trailer_receiver . clone ( )
60
+ }
44
61
}
45
62
46
63
fn decode_init ( buffer : Block < ' static > , pos : & Position , buf : & mut [ u8 ] ) -> io:: Result < DecodeResult > {
@@ -78,39 +95,79 @@ fn decode_chunk(
78
95
79
96
buf[ ..to_read] . copy_from_slice ( & buffer[ pos. start ..pos. start + to_read] ) ;
80
97
98
+ dbg ! ( to_read) ;
99
+ dbg ! ( std:: str :: from_utf8( & buf[ ..to_read] ) ) ;
100
+
101
+ let new_pos = Position {
102
+ start : pos. start + to_read,
103
+ end : pos. end ,
104
+ } ;
81
105
let new_state = if left_to_read - to_read > 0 {
82
106
State :: Chunk ( current + to_read as u64 , len)
83
107
} else {
84
- State :: Init
108
+ // read one \r\n
109
+ State :: ChunkEnd
85
110
} ;
86
111
87
112
Ok ( DecodeResult :: Some {
88
113
read : to_read,
89
114
buffer,
90
115
new_state,
91
- new_pos : Position {
92
- start : pos. start + to_read,
93
- end : pos. end ,
94
- } ,
116
+ new_pos,
95
117
} )
96
118
}
97
119
120
+ fn decode_chunk_end (
121
+ buffer : Block < ' static > ,
122
+ pos : & Position ,
123
+ buf : & mut [ u8 ] ,
124
+ ) -> io:: Result < DecodeResult > {
125
+ if pos. len ( ) < 2 {
126
+ return Ok ( DecodeResult :: None ( buffer) ) ;
127
+ }
128
+
129
+ if & buffer[ pos. start ..pos. start + 2 ] == b"\r \n " {
130
+ // valid chunk end move on to a new header
131
+ return decode_init (
132
+ buffer,
133
+ & Position {
134
+ start : pos. start + 2 ,
135
+ end : pos. end ,
136
+ } ,
137
+ buf,
138
+ ) ;
139
+ }
140
+
141
+ dbg ! ( std:: str :: from_utf8( & buffer[ pos. start..pos. end] ) ) ;
142
+
143
+ Err ( io:: Error :: from ( io:: ErrorKind :: InvalidData ) )
144
+ }
145
+
98
146
fn decode_trailer ( buffer : Block < ' static > , pos : & Position ) -> io:: Result < DecodeResult > {
99
147
use httparse:: Status ;
100
148
101
- // TODO: find a way to emit the actual read headers
102
-
103
149
// read headers
104
150
let mut headers = [ httparse:: EMPTY_HEADER ; 16 ] ;
105
151
dbg ! ( std:: str :: from_utf8( & buffer[ pos. start..pos. end] ) ) ;
106
152
match httparse:: parse_headers ( & buffer[ pos. start ..pos. end ] , & mut headers) {
107
153
Ok ( Status :: Complete ( ( used, headers) ) ) => {
108
154
dbg ! ( headers) ;
109
-
155
+ dbg ! ( used) ;
156
+ let headers = headers
157
+ . iter ( )
158
+ . map ( |header| {
159
+ // TODO: error propagation
160
+ let name = HeaderName :: from_str ( header. name ) . unwrap ( ) ;
161
+ let value =
162
+ HeaderValue :: from_str ( std:: str:: from_utf8 ( header. value ) . unwrap ( ) ) . unwrap ( ) ;
163
+ ( name, value)
164
+ } )
165
+ . collect ( ) ;
166
+ dbg ! ( & headers) ;
110
167
Ok ( DecodeResult :: Some {
111
- read : used ,
168
+ read : 0 ,
112
169
buffer,
113
- new_state : State :: Done ,
170
+ new_state : State :: TrailerDone ( headers ) ,
114
171
new_pos : Position {
115
172
start : pos. start + used,
116
173
end : pos. end ,
@@ -143,6 +200,14 @@ impl<R: BufRead + Unpin + Send + 'static> ChunkedDecoder<R> {
143
200
} )
144
201
}
145
202
203
+ fn poll_read_chunk_end (
204
+ self : Pin < & mut Self > ,
205
+ cx : & mut Context < ' _ > ,
206
+ buf : & mut [ u8 ] ,
207
+ ) -> Poll < io:: Result < usize > > {
208
+ self . poll_read_inner ( cx, buf, decode_chunk_end)
209
+ }
210
+
146
211
fn poll_read_trailer (
147
212
self : Pin < & mut Self > ,
148
213
cx : & mut Context < ' _ > ,
@@ -178,7 +243,11 @@ impl<R: BufRead + Unpin + Send + 'static> ChunkedDecoder<R> {
178
243
std:: mem:: replace ( & mut this. buffer , buffer) ;
179
244
std:: mem:: replace ( & mut this. state , new_state) ;
180
245
this. current = new_pos;
181
- return Poll :: Ready ( Ok ( read) ) ;
246
+
247
+ if State :: Done == this. state || read > 0 {
248
+ return Poll :: Ready ( Ok ( read) ) ;
249
+ }
250
+ return Poll :: Pending ;
182
251
}
183
252
DecodeResult :: None ( buffer) => buffer,
184
253
}
@@ -217,7 +286,7 @@ impl<R: BufRead + Unpin + Send + 'static> ChunkedDecoder<R> {
217
286
match f ( buffer, & n, buf) ? {
218
287
DecodeResult :: Some {
219
288
read,
220
- buffer,
289
+ buffer : new_buffer ,
221
290
new_pos,
222
291
new_state,
223
292
} => {
@@ -228,9 +297,14 @@ impl<R: BufRead + Unpin + Send + 'static> ChunkedDecoder<R> {
228
297
// to decode it next time
229
298
this. initial_decode = true ;
230
299
std:: mem:: replace ( & mut this. state , new_state) ;
231
- std:: mem:: replace ( & mut this. buffer , buffer) ;
232
300
this. current = new_pos;
233
- return Poll :: Ready ( Ok ( read) ) ;
301
+ if State :: Done == this. state || read > 0 {
302
+ std:: mem:: replace ( & mut this. buffer , new_buffer) ;
303
+ return Poll :: Ready ( Ok ( read) ) ;
304
+ }
305
+
306
+ buffer = new_buffer;
307
+ continue ;
234
308
}
235
309
DecodeResult :: None ( buf) => {
236
310
buffer = buf;
@@ -262,7 +336,7 @@ impl<R: BufRead + Unpin + Send + 'static> ChunkedDecoder<R> {
262
336
impl < R : BufRead + Unpin + Send + ' static > Read for ChunkedDecoder < R > {
263
337
#[ allow( missing_doc_code_examples) ]
264
338
fn poll_read (
265
- self : Pin < & mut Self > ,
339
+ mut self : Pin < & mut Self > ,
266
340
cx : & mut Context < ' _ > ,
267
341
buf : & mut [ u8 ] ,
268
342
) -> Poll < io:: Result < usize > > {
@@ -275,10 +349,22 @@ impl<R: BufRead + Unpin + Send + 'static> Read for ChunkedDecoder<R> {
275
349
// reading a chunk
276
350
self . poll_read_chunk ( cx, buf, current, len)
277
351
}
352
+ State :: ChunkEnd => self . poll_read_chunk_end ( cx, buf) ,
278
353
State :: Trailer => {
279
354
// reading the trailer headers
280
355
self . poll_read_trailer ( cx, buf)
281
356
}
357
+ State :: TrailerDone ( ref mut headers) => {
358
+ dbg ! ( "Done" ) ;
359
+ let headers = std:: mem:: replace ( headers, Vec :: new ( ) ) ;
360
+ for ( name, value) in headers. into_iter ( ) {
361
+ dbg ! ( & name) ;
362
+ let mut fut = self . trailer_sender . send ( ( name, value) ) ;
363
+ async_std:: task:: ready!( unsafe { Pin :: new_unchecked( & mut fut) } . poll( cx) ) ;
364
+ }
365
+ self . state = State :: Done ;
366
+ Poll :: Ready ( Ok ( 0 ) )
367
+ }
282
368
State :: Done => Poll :: Ready ( Ok ( 0 ) ) ,
283
369
}
284
370
}
@@ -317,11 +403,13 @@ enum DecodeResult {
317
403
None ( Block < ' static > ) ,
318
404
}
319
405
320
- #[ derive( Debug ) ]
406
+ #[ derive( Debug , PartialEq ) ]
321
407
enum State {
322
408
Init ,
323
409
Chunk ( u64 , u64 ) ,
410
+ ChunkEnd ,
324
411
Trailer ,
412
+ TrailerDone ( Vec < ( HeaderName , HeaderValue ) > ) ,
325
413
Done ,
326
414
}
327
415
@@ -344,3 +432,69 @@ impl fmt::Debug for DecodeResult {
344
432
}
345
433
}
346
434
}
435
+
436
+ #[ cfg( test) ]
437
+ mod tests {
438
+ use super :: * ;
439
+ use async_std:: prelude:: * ;
440
+
441
+ #[ test]
442
+ fn test_chunked_wiki ( ) {
443
+ async_std:: task:: block_on ( async move {
444
+ let input = async_std:: io:: Cursor :: new (
445
+ "4\r \n \
446
+ Wiki\r \n \
447
+ 5\r \n \
448
+ pedia\r \n \
449
+ E\r \n in\r \n \
450
+ \r \n \
451
+ chunks.\r \n \
452
+ 0\r \n \
453
+ \r \n "
454
+ . as_bytes ( ) ,
455
+ ) ;
456
+ let mut decoder = ChunkedDecoder :: new ( input) ;
457
+
458
+ let mut output = String :: new ( ) ;
459
+ decoder. read_to_string ( & mut output) . await . unwrap ( ) ;
460
+ assert_eq ! (
461
+ output,
462
+ "Wikipedia in\r \n \
463
+ \r \n \
464
+ chunks."
465
+ ) ;
466
+ } ) ;
467
+ }
468
+
469
+ #[ test]
470
+ fn test_chunked_mdn ( ) {
471
+ async_std:: task:: block_on ( async move {
472
+ let input = async_std:: io:: Cursor :: new (
473
+ "7\r \n \
474
+ Mozilla\r \n \
475
+ 9\r \n \
476
+ Developer\r \n \
477
+ 7\r \n \
478
+ Network\r \n \
479
+ 0\r \n \
480
+ Expires: Wed, 21 Oct 2015 07:28:00 GMT\r \n \
481
+ \r \n "
482
+ . as_bytes ( ) ,
483
+ ) ;
484
+ let mut decoder = ChunkedDecoder :: new ( input) ;
485
+
486
+ let mut output = String :: new ( ) ;
487
+ decoder. read_to_string ( & mut output) . await . unwrap ( ) ;
488
+ assert_eq ! ( output, "MozillaDeveloperNetwork" ) ;
489
+
490
+ let trailer = decoder. trailer ( ) . recv ( ) . await ;
491
+ assert_eq ! (
492
+ trailer,
493
+ Some ( (
494
+ "Expires" . parse( ) . unwrap( ) ,
495
+ "Wed, 21 Oct 2015 07:28:00 GMT" . parse( ) . unwrap( ) ,
496
+ ) )
497
+ ) ;
498
+ } ) ;
499
+ }
500
+ }
0 commit comments