@@ -8,16 +8,37 @@ use http_types::Response;
8
8
const CR : u8 = b'\r' ;
9
9
const LF : u8 = b'\n' ;
10
10
11
+ /// The encoder state.
12
+ #[ derive( Debug ) ]
13
+ enum State {
14
+ /// Streaming out chunks.
15
+ Streaming ,
16
+ /// Receiving trailers from a channel.
17
+ ReceiveTrailers ,
18
+ /// Streaming out trailers.
19
+ EncodeTrailers ,
20
+ /// Writing the final CRLF
21
+ EndOfStream ,
22
+ /// The stream has finished.
23
+ Done ,
24
+ }
25
+
11
26
/// An encoder for chunked encoding.
12
27
#[ derive( Debug ) ]
13
28
pub ( crate ) struct ChunkedEncoder {
14
- done : bool ,
29
+ /// How many bytes we've written to the buffer so far.
30
+ bytes_written : usize ,
31
+ /// The internal encoder state.
32
+ state : State ,
15
33
}
16
34
17
35
impl ChunkedEncoder {
18
36
/// Create a new instance.
19
37
pub ( crate ) fn new ( ) -> Self {
20
- Self { done : false }
38
+ Self {
39
+ state : State :: Streaming ,
40
+ bytes_written : 0 ,
41
+ }
21
42
}
22
43
/// Encode an AsyncBufRead using "chunked" framing. This is used for streams
23
44
/// whose length is not known up front.
@@ -40,23 +61,33 @@ impl ChunkedEncoder {
40
61
/// ```
41
62
pub ( crate ) fn encode (
42
63
& mut self ,
43
- mut res : & mut Response ,
64
+ res : & mut Response ,
44
65
cx : & mut Context < ' _ > ,
45
66
buf : & mut [ u8 ] ,
46
67
) -> Poll < io:: Result < usize > > {
47
- let mut bytes_read = 0 ;
48
-
49
- // Return early if we know we're done.
50
- if self . done {
51
- return Poll :: Ready ( Ok ( 0 ) ) ;
68
+ self . bytes_written = 0 ;
69
+ match self . state {
70
+ State :: Streaming => self . encode_stream ( res, cx, buf) ,
71
+ State :: ReceiveTrailers => self . encode_trailers ( res, cx, buf) ,
72
+ State :: EncodeTrailers => self . encode_trailers ( res, cx, buf) ,
73
+ State :: EndOfStream => self . encode_eos ( cx, buf) ,
74
+ State :: Done => Poll :: Ready ( Ok ( 0 ) ) ,
52
75
}
76
+ }
53
77
78
+ /// Stream out data using chunked encoding.
79
+ fn encode_stream (
80
+ & mut self ,
81
+ mut res : & mut Response ,
82
+ cx : & mut Context < ' _ > ,
83
+ buf : & mut [ u8 ] ,
84
+ ) -> Poll < io:: Result < usize > > {
54
85
// Get bytes from the underlying stream. If the stream is not ready yet,
55
86
// return the header bytes if we have any.
56
87
let src = match Pin :: new ( & mut res) . poll_fill_buf ( cx) {
57
88
Poll :: Ready ( Ok ( n) ) => n,
58
89
Poll :: Ready ( Err ( e) ) => return Poll :: Ready ( Err ( e) ) ,
59
- Poll :: Pending => match bytes_read {
90
+ Poll :: Pending => match self . bytes_written {
60
91
0 => return Poll :: Pending ,
61
92
n => return Poll :: Ready ( Ok ( n) ) ,
62
93
} ,
@@ -65,25 +96,20 @@ impl ChunkedEncoder {
65
96
// If the stream doesn't have any more bytes left to read we're done.
66
97
if src. len ( ) == 0 {
67
98
// Write out the final empty chunk
68
- let idx = bytes_read ;
99
+ let idx = self . bytes_written ;
69
100
buf[ idx] = b'0' ;
70
101
buf[ idx + 1 ] = CR ;
71
102
buf[ idx + 2 ] = LF ;
103
+ self . bytes_written += 3 ;
72
104
73
- // Write the final CRLF
74
- buf[ idx + 3 ] = CR ;
75
- buf[ idx + 4 ] = LF ;
76
- bytes_read += 5 ;
77
-
78
- log:: trace!( "done sending bytes" ) ;
79
- self . done = true ;
80
- return Poll :: Ready ( Ok ( bytes_read) ) ;
105
+ self . state = State :: ReceiveTrailers ;
106
+ return self . receive_trailers ( res, cx, buf) ;
81
107
}
82
108
83
109
// Each chunk is prefixed with the length of the data in hex, then a
84
110
// CRLF, then the content, then another CRLF. Calculate how many bytes
85
111
// each part should be.
86
- let buf_len = buf. len ( ) . checked_sub ( bytes_read ) . unwrap_or ( 0 ) ;
112
+ let buf_len = buf. len ( ) . checked_sub ( self . bytes_written ) . unwrap_or ( 0 ) ;
87
113
let amt = src. len ( ) . min ( buf_len) ;
88
114
// Calculate the max char count encoding the `len_prefix` statement
89
115
// as hex would take. This is done by rounding up `log16(amt + 1)`.
@@ -94,28 +120,66 @@ impl ChunkedEncoder {
94
120
let len_prefix = format ! ( "{:X}" , amt) . into_bytes ( ) ;
95
121
96
122
// Write our frame header to the buffer.
97
- let lower = bytes_read ;
98
- let upper = bytes_read + len_prefix. len ( ) ;
123
+ let lower = self . bytes_written ;
124
+ let upper = self . bytes_written + len_prefix. len ( ) ;
99
125
buf[ lower..upper] . copy_from_slice ( & len_prefix) ;
100
126
buf[ upper] = CR ;
101
127
buf[ upper + 1 ] = LF ;
102
- bytes_read += len_prefix. len ( ) + 2 ;
128
+ self . bytes_written += len_prefix. len ( ) + 2 ;
103
129
104
130
// Copy the bytes from our source into the output buffer.
105
- let lower = bytes_read ;
106
- let upper = bytes_read + amt;
131
+ let lower = self . bytes_written ;
132
+ let upper = self . bytes_written + amt;
107
133
buf[ lower..upper] . copy_from_slice ( & src[ 0 ..amt] ) ;
108
134
Pin :: new ( & mut res) . consume ( amt) ;
109
- bytes_read += amt;
135
+ self . bytes_written += amt;
110
136
111
137
// Finalize the chunk with a final CRLF.
112
- let idx = bytes_read ;
138
+ let idx = self . bytes_written ;
113
139
buf[ idx] = CR ;
114
140
buf[ idx + 1 ] = LF ;
115
- bytes_read += 2 ;
141
+ self . bytes_written += 2 ;
116
142
117
143
// Finally return how many bytes we've written to the buffer.
118
- log:: trace!( "sending {} bytes" , bytes_read) ;
119
- Poll :: Ready ( Ok ( bytes_read) )
144
+ log:: trace!( "sending {} bytes" , self . bytes_written) ;
145
+ Poll :: Ready ( Ok ( self . bytes_written ) )
146
+ }
147
+
148
+ /// Receive trailers sent to the response, and store them in an internal
149
+ /// buffer.
150
+ fn receive_trailers (
151
+ & mut self ,
152
+ res : & mut Response ,
153
+ cx : & mut Context < ' _ > ,
154
+ buf : & mut [ u8 ] ,
155
+ ) -> Poll < io:: Result < usize > > {
156
+ // TODO: actually wait for trailers to be received.
157
+ self . state = State :: EncodeTrailers ;
158
+ self . encode_trailers ( res, cx, buf)
159
+ }
160
+
161
+ /// Send trailers to the buffer.
162
+ fn encode_trailers (
163
+ & mut self ,
164
+ _res : & mut Response ,
165
+ cx : & mut Context < ' _ > ,
166
+ buf : & mut [ u8 ] ,
167
+ ) -> Poll < io:: Result < usize > > {
168
+ // TODO: actually encode trailers here.
169
+ self . state = State :: EndOfStream ;
170
+ self . encode_eos ( cx, buf)
171
+ }
172
+
173
+ /// Encode the end of the stream.
174
+ fn encode_eos ( & mut self , _cx : & mut Context < ' _ > , buf : & mut [ u8 ] ) -> Poll < io:: Result < usize > > {
175
+ let idx = self . bytes_written ;
176
+ // Write the final CRLF
177
+ buf[ idx] = CR ;
178
+ buf[ idx + 1 ] = LF ;
179
+ self . bytes_written += 2 ;
180
+
181
+ log:: trace!( "finished encoding chunked stream" ) ;
182
+ self . state = State :: Done ;
183
+ return Poll :: Ready ( Ok ( self . bytes_written ) ) ;
120
184
}
121
185
}
0 commit comments