1
+ use std:: collections:: HashMap ;
1
2
use std:: fmt;
2
3
use std:: io:: IoSlice ;
3
4
4
5
use bytes:: buf:: { Chain , Take } ;
5
- use bytes:: Buf ;
6
+ use bytes:: { Buf , Bytes } ;
7
+ use http:: {
8
+ header:: {
9
+ AUTHORIZATION , CACHE_CONTROL , CONTENT_ENCODING , CONTENT_LENGTH , CONTENT_RANGE ,
10
+ CONTENT_TYPE , HOST , MAX_FORWARDS , SET_COOKIE , TE , TRAILER , TRANSFER_ENCODING ,
11
+ } ,
12
+ HeaderMap , HeaderName , HeaderValue ,
13
+ } ;
6
14
7
15
use super :: io:: WriteBuf ;
16
+ use super :: role:: { write_headers, write_headers_title_case} ;
8
17
9
18
type StaticBuf = & ' static [ u8 ] ;
10
19
@@ -26,7 +35,7 @@ pub(crate) struct NotEof(u64);
26
35
#[ derive( Debug , PartialEq , Clone ) ]
27
36
enum Kind {
28
37
/// An Encoder for when Transfer-Encoding includes `chunked`.
29
- Chunked ,
38
+ Chunked ( Option < Vec < HeaderValue > > ) ,
30
39
/// An Encoder for when Content-Length is set.
31
40
///
32
41
/// Enforces that the body is not longer than the Content-Length header.
@@ -45,6 +54,7 @@ enum BufKind<B> {
45
54
Limited ( Take < B > ) ,
46
55
Chunked ( Chain < Chain < ChunkSize , B > , StaticBuf > ) ,
47
56
ChunkedEnd ( StaticBuf ) ,
57
+ Trailers ( Chain < Chain < StaticBuf , Bytes > , StaticBuf > ) ,
48
58
}
49
59
50
60
impl Encoder {
@@ -55,7 +65,7 @@ impl Encoder {
55
65
}
56
66
}
57
67
pub ( crate ) fn chunked ( ) -> Encoder {
58
- Encoder :: new ( Kind :: Chunked )
68
+ Encoder :: new ( Kind :: Chunked ( None ) )
59
69
}
60
70
61
71
pub ( crate ) fn length ( len : u64 ) -> Encoder {
@@ -67,6 +77,16 @@ impl Encoder {
67
77
Encoder :: new ( Kind :: CloseDelimited )
68
78
}
69
79
80
+ pub ( crate ) fn into_chunked_with_trailing_fields ( self , trailers : Vec < HeaderValue > ) -> Encoder {
81
+ match self . kind {
82
+ Kind :: Chunked ( _) => Encoder {
83
+ kind : Kind :: Chunked ( Some ( trailers) ) ,
84
+ is_last : self . is_last ,
85
+ } ,
86
+ _ => self ,
87
+ }
88
+ }
89
+
70
90
pub ( crate ) fn is_eof ( & self ) -> bool {
71
91
matches ! ( self . kind, Kind :: Length ( 0 ) )
72
92
}
@@ -89,10 +109,17 @@ impl Encoder {
89
109
}
90
110
}
91
111
112
+ pub ( crate ) fn is_chunked ( & self ) -> bool {
113
+ match self . kind {
114
+ Kind :: Chunked ( _) => true ,
115
+ _ => false ,
116
+ }
117
+ }
118
+
92
119
pub ( crate ) fn end < B > ( & self ) -> Result < Option < EncodedBuf < B > > , NotEof > {
93
120
match self . kind {
94
121
Kind :: Length ( 0 ) => Ok ( None ) ,
95
- Kind :: Chunked => Ok ( Some ( EncodedBuf {
122
+ Kind :: Chunked ( _ ) => Ok ( Some ( EncodedBuf {
96
123
kind : BufKind :: ChunkedEnd ( b"0\r \n \r \n " ) ,
97
124
} ) ) ,
98
125
#[ cfg( feature = "server" ) ]
@@ -109,7 +136,7 @@ impl Encoder {
109
136
debug_assert ! ( len > 0 , "encode() called with empty buf" ) ;
110
137
111
138
let kind = match self . kind {
112
- Kind :: Chunked => {
139
+ Kind :: Chunked ( _ ) => {
113
140
trace ! ( "encoding chunked {}B" , len) ;
114
141
let buf = ChunkSize :: new ( len)
115
142
. chain ( msg)
@@ -136,6 +163,53 @@ impl Encoder {
136
163
EncodedBuf { kind }
137
164
}
138
165
166
+ pub ( crate ) fn encode_trailers < B > (
167
+ & self ,
168
+ trailers : HeaderMap ,
169
+ title_case_headers : bool ,
170
+ ) -> Option < EncodedBuf < B > > {
171
+ match & self . kind {
172
+ Kind :: Chunked ( Some ( ref allowed_trailer_fields) ) => {
173
+ let allowed_trailer_field_map = allowed_trailer_field_map ( & allowed_trailer_fields) ;
174
+
175
+ let mut cur_name = None ;
176
+ let mut allowed_trailers = HeaderMap :: new ( ) ;
177
+
178
+ for ( opt_name, value) in trailers {
179
+ if let Some ( n) = opt_name {
180
+ cur_name = Some ( n) ;
181
+ }
182
+ let name = cur_name. as_ref ( ) . expect ( "current header name" ) ;
183
+
184
+ if allowed_trailer_field_map. contains_key ( name. as_str ( ) )
185
+ && valid_trailer_field ( name)
186
+ {
187
+ allowed_trailers. insert ( name, value) ;
188
+ }
189
+ }
190
+
191
+ let mut buf = Vec :: new ( ) ;
192
+ if title_case_headers {
193
+ write_headers_title_case ( & allowed_trailers, & mut buf) ;
194
+ } else {
195
+ write_headers ( & allowed_trailers, & mut buf) ;
196
+ }
197
+
198
+ if buf. is_empty ( ) {
199
+ return None ;
200
+ }
201
+
202
+ Some ( EncodedBuf {
203
+ kind : BufKind :: Trailers ( b"0\r \n " . chain ( Bytes :: from ( buf) ) . chain ( b"\r \n " ) ) ,
204
+ } )
205
+ }
206
+ _ => {
207
+ debug ! ( "attempted to encode trailers for non-chunked response" ) ;
208
+ None
209
+ }
210
+ }
211
+ }
212
+
139
213
pub ( super ) fn encode_and_end < B > ( & self , msg : B , dst : & mut WriteBuf < EncodedBuf < B > > ) -> bool
140
214
where
141
215
B : Buf ,
@@ -144,7 +218,7 @@ impl Encoder {
144
218
debug_assert ! ( len > 0 , "encode() called with empty buf" ) ;
145
219
146
220
match self . kind {
147
- Kind :: Chunked => {
221
+ Kind :: Chunked ( _ ) => {
148
222
trace ! ( "encoding chunked {}B" , len) ;
149
223
let buf = ChunkSize :: new ( len)
150
224
. chain ( msg)
@@ -181,6 +255,40 @@ impl Encoder {
181
255
}
182
256
}
183
257
258
+ fn valid_trailer_field ( name : & HeaderName ) -> bool {
259
+ match name {
260
+ & AUTHORIZATION => false ,
261
+ & CACHE_CONTROL => false ,
262
+ & CONTENT_ENCODING => false ,
263
+ & CONTENT_LENGTH => false ,
264
+ & CONTENT_RANGE => false ,
265
+ & CONTENT_TYPE => false ,
266
+ & HOST => false ,
267
+ & MAX_FORWARDS => false ,
268
+ & SET_COOKIE => false ,
269
+ & TRAILER => false ,
270
+ & TRANSFER_ENCODING => false ,
271
+ & TE => false ,
272
+ _ => true ,
273
+ }
274
+ }
275
+
276
+ fn allowed_trailer_field_map ( allowed_trailer_fields : & Vec < HeaderValue > ) -> HashMap < String , ( ) > {
277
+ let mut trailer_map = HashMap :: new ( ) ;
278
+
279
+ for header_value in allowed_trailer_fields {
280
+ if let Ok ( header_str) = header_value. to_str ( ) {
281
+ let items: Vec < & str > = header_str. split ( ',' ) . map ( |item| item. trim ( ) ) . collect ( ) ;
282
+
283
+ for item in items {
284
+ trailer_map. entry ( item. to_string ( ) ) . or_insert ( ( ) ) ;
285
+ }
286
+ }
287
+ }
288
+
289
+ trailer_map
290
+ }
291
+
184
292
impl < B > Buf for EncodedBuf < B >
185
293
where
186
294
B : Buf ,
@@ -192,6 +300,7 @@ where
192
300
BufKind :: Limited ( ref b) => b. remaining ( ) ,
193
301
BufKind :: Chunked ( ref b) => b. remaining ( ) ,
194
302
BufKind :: ChunkedEnd ( ref b) => b. remaining ( ) ,
303
+ BufKind :: Trailers ( ref b) => b. remaining ( ) ,
195
304
}
196
305
}
197
306
@@ -202,6 +311,7 @@ where
202
311
BufKind :: Limited ( ref b) => b. chunk ( ) ,
203
312
BufKind :: Chunked ( ref b) => b. chunk ( ) ,
204
313
BufKind :: ChunkedEnd ( ref b) => b. chunk ( ) ,
314
+ BufKind :: Trailers ( ref b) => b. chunk ( ) ,
205
315
}
206
316
}
207
317
@@ -212,6 +322,7 @@ where
212
322
BufKind :: Limited ( ref mut b) => b. advance ( cnt) ,
213
323
BufKind :: Chunked ( ref mut b) => b. advance ( cnt) ,
214
324
BufKind :: ChunkedEnd ( ref mut b) => b. advance ( cnt) ,
325
+ BufKind :: Trailers ( ref mut b) => b. advance ( cnt) ,
215
326
}
216
327
}
217
328
@@ -222,6 +333,7 @@ where
222
333
BufKind :: Limited ( ref b) => b. chunks_vectored ( dst) ,
223
334
BufKind :: Chunked ( ref b) => b. chunks_vectored ( dst) ,
224
335
BufKind :: ChunkedEnd ( ref b) => b. chunks_vectored ( dst) ,
336
+ BufKind :: Trailers ( ref b) => b. chunks_vectored ( dst) ,
225
337
}
226
338
}
227
339
}
@@ -327,7 +439,16 @@ impl std::error::Error for NotEof {}
327
439
328
440
#[ cfg( test) ]
329
441
mod tests {
442
+ use std:: iter:: FromIterator ;
443
+
330
444
use bytes:: BufMut ;
445
+ use http:: {
446
+ header:: {
447
+ AUTHORIZATION , CACHE_CONTROL , CONTENT_ENCODING , CONTENT_LENGTH , CONTENT_RANGE ,
448
+ CONTENT_TYPE , HOST , MAX_FORWARDS , SET_COOKIE , TE , TRAILER , TRANSFER_ENCODING ,
449
+ } ,
450
+ HeaderMap , HeaderName , HeaderValue ,
451
+ } ;
331
452
332
453
use super :: super :: io:: Cursor ;
333
454
use super :: Encoder ;
@@ -402,4 +523,145 @@ mod tests {
402
523
assert ! ( !encoder. is_eof( ) ) ;
403
524
encoder. end :: < ( ) > ( ) . unwrap ( ) ;
404
525
}
526
+
527
+ #[ test]
528
+ fn chunked_with_valid_trailers ( ) {
529
+ let encoder = Encoder :: chunked ( ) ;
530
+ let trailers = vec ! [ HeaderValue :: from_static( "chunky-trailer" ) ] ;
531
+ let encoder = encoder. into_chunked_with_trailing_fields ( trailers) ;
532
+
533
+ let headers = HeaderMap :: from_iter (
534
+ vec ! [
535
+ (
536
+ HeaderName :: from_static( "chunky-trailer" ) ,
537
+ HeaderValue :: from_static( "header data" ) ,
538
+ ) ,
539
+ (
540
+ HeaderName :: from_static( "should-not-be-included" ) ,
541
+ HeaderValue :: from_static( "oops" ) ,
542
+ ) ,
543
+ ]
544
+ . into_iter ( ) ,
545
+ ) ;
546
+
547
+ let buf1 = encoder. encode_trailers :: < & [ u8 ] > ( headers, false ) . unwrap ( ) ;
548
+
549
+ let mut dst = Vec :: new ( ) ;
550
+ dst. put ( buf1) ;
551
+ assert_eq ! ( dst, b"0\r \n chunky-trailer: header data\r \n \r \n " ) ;
552
+ }
553
+
554
+ #[ test]
555
+ fn chunked_with_multiple_trailer_headers ( ) {
556
+ let encoder = Encoder :: chunked ( ) ;
557
+ let trailers = vec ! [
558
+ HeaderValue :: from_static( "chunky-trailer" ) ,
559
+ HeaderValue :: from_static( "chunky-trailer-2" ) ,
560
+ ] ;
561
+ let encoder = encoder. into_chunked_with_trailing_fields ( trailers) ;
562
+
563
+ let headers = HeaderMap :: from_iter (
564
+ vec ! [
565
+ (
566
+ HeaderName :: from_static( "chunky-trailer" ) ,
567
+ HeaderValue :: from_static( "header data" ) ,
568
+ ) ,
569
+ (
570
+ HeaderName :: from_static( "chunky-trailer-2" ) ,
571
+ HeaderValue :: from_static( "more header data" ) ,
572
+ ) ,
573
+ ]
574
+ . into_iter ( ) ,
575
+ ) ;
576
+
577
+ let buf1 = encoder. encode_trailers :: < & [ u8 ] > ( headers, false ) . unwrap ( ) ;
578
+
579
+ let mut dst = Vec :: new ( ) ;
580
+ dst. put ( buf1) ;
581
+ assert_eq ! (
582
+ dst,
583
+ b"0\r \n chunky-trailer: header data\r \n chunky-trailer-2: more header data\r \n \r \n "
584
+ ) ;
585
+ }
586
+
587
+ #[ test]
588
+ fn chunked_with_no_trailer_header ( ) {
589
+ let encoder = Encoder :: chunked ( ) ;
590
+
591
+ let headers = HeaderMap :: from_iter (
592
+ vec ! [ (
593
+ HeaderName :: from_static( "chunky-trailer" ) ,
594
+ HeaderValue :: from_static( "header data" ) ,
595
+ ) ]
596
+ . into_iter ( ) ,
597
+ ) ;
598
+
599
+ assert ! ( encoder
600
+ . encode_trailers:: <& [ u8 ] >( headers. clone( ) , false )
601
+ . is_none( ) ) ;
602
+
603
+ let trailers = vec ! [ ] ;
604
+ let encoder = encoder. into_chunked_with_trailing_fields ( trailers) ;
605
+
606
+ assert ! ( encoder. encode_trailers:: <& [ u8 ] >( headers, false ) . is_none( ) ) ;
607
+ }
608
+
609
+ #[ test]
610
+ fn chunked_with_invalid_trailers ( ) {
611
+ let encoder = Encoder :: chunked ( ) ;
612
+
613
+ let trailers = format ! (
614
+ "{},{},{},{},{},{},{},{},{},{},{},{}" ,
615
+ AUTHORIZATION ,
616
+ CACHE_CONTROL ,
617
+ CONTENT_ENCODING ,
618
+ CONTENT_LENGTH ,
619
+ CONTENT_RANGE ,
620
+ CONTENT_TYPE ,
621
+ HOST ,
622
+ MAX_FORWARDS ,
623
+ SET_COOKIE ,
624
+ TRAILER ,
625
+ TRANSFER_ENCODING ,
626
+ TE ,
627
+ ) ;
628
+ let trailers = vec ! [ HeaderValue :: from_str( & trailers) . unwrap( ) ] ;
629
+ let encoder = encoder. into_chunked_with_trailing_fields ( trailers) ;
630
+
631
+ let mut headers = HeaderMap :: new ( ) ;
632
+ headers. insert ( AUTHORIZATION , HeaderValue :: from_static ( "header data" ) ) ;
633
+ headers. insert ( CACHE_CONTROL , HeaderValue :: from_static ( "header data" ) ) ;
634
+ headers. insert ( CONTENT_ENCODING , HeaderValue :: from_static ( "header data" ) ) ;
635
+ headers. insert ( CONTENT_LENGTH , HeaderValue :: from_static ( "header data" ) ) ;
636
+ headers. insert ( CONTENT_RANGE , HeaderValue :: from_static ( "header data" ) ) ;
637
+ headers. insert ( CONTENT_TYPE , HeaderValue :: from_static ( "header data" ) ) ;
638
+ headers. insert ( HOST , HeaderValue :: from_static ( "header data" ) ) ;
639
+ headers. insert ( MAX_FORWARDS , HeaderValue :: from_static ( "header data" ) ) ;
640
+ headers. insert ( SET_COOKIE , HeaderValue :: from_static ( "header data" ) ) ;
641
+ headers. insert ( TRAILER , HeaderValue :: from_static ( "header data" ) ) ;
642
+ headers. insert ( TRANSFER_ENCODING , HeaderValue :: from_static ( "header data" ) ) ;
643
+ headers. insert ( TE , HeaderValue :: from_static ( "header data" ) ) ;
644
+
645
+ assert ! ( encoder. encode_trailers:: <& [ u8 ] >( headers, true ) . is_none( ) ) ;
646
+ }
647
+
648
+ #[ test]
649
+ fn chunked_with_title_case_headers ( ) {
650
+ let encoder = Encoder :: chunked ( ) ;
651
+ let trailers = vec ! [ HeaderValue :: from_static( "chunky-trailer" ) ] ;
652
+ let encoder = encoder. into_chunked_with_trailing_fields ( trailers) ;
653
+
654
+ let headers = HeaderMap :: from_iter (
655
+ vec ! [ (
656
+ HeaderName :: from_static( "chunky-trailer" ) ,
657
+ HeaderValue :: from_static( "header data" ) ,
658
+ ) ]
659
+ . into_iter ( ) ,
660
+ ) ;
661
+ let buf1 = encoder. encode_trailers :: < & [ u8 ] > ( headers, true ) . unwrap ( ) ;
662
+
663
+ let mut dst = Vec :: new ( ) ;
664
+ dst. put ( buf1) ;
665
+ assert_eq ! ( dst, b"0\r \n Chunky-Trailer: header data\r \n \r \n " ) ;
666
+ }
405
667
}
0 commit comments