@@ -5,7 +5,7 @@ use tokio::io::{AsyncRead, AsyncReadExt};
55use tokio:: time:: timeout;
66
77use crate :: request_body_budget:: NativeRequestBodyReservation ;
8- use crate :: { DownstreamHttp1Policy , NativeHttp1Error } ;
8+ use crate :: { DownstreamHttp1Policy , NativeHttp1Error , NativeHttp1RequestBody } ;
99
1010const READ_CHUNK_BYTES : usize = 8192 ;
1111
@@ -17,7 +17,7 @@ pub(super) async fn read_body<S>(
1717 head_len : usize ,
1818 framing : Http1BodyFraming ,
1919 reservation : & mut NativeRequestBodyReservation ,
20- ) -> Result < Vec < u8 > , NativeHttp1Error >
20+ ) -> Result < NativeHttp1RequestBody , NativeHttp1Error >
2121where
2222 S : AsyncRead + Unpin ,
2323{
@@ -48,33 +48,45 @@ async fn read_body_inner<S>(
4848 framing : Http1BodyFraming ,
4949 max_body_bytes : usize ,
5050 reservation : & mut NativeRequestBodyReservation ,
51- ) -> Result < Vec < u8 > , NativeHttp1Error >
51+ ) -> Result < NativeHttp1RequestBody , NativeHttp1Error >
5252where
5353 S : AsyncRead + Unpin ,
5454{
5555 match framing {
5656 Http1BodyFraming :: NoBody => {
57+ sanitization:: unsafe_wipe:: volatile_sanitize_bytes ( & mut buffer[ ..head_len] ) ;
5758 buffer. drain ( ..head_len) ;
58- Ok ( Vec :: new ( ) )
59+ Ok ( NativeHttp1RequestBody :: empty ( ) )
5960 }
6061 Http1BodyFraming :: ContentLength ( length) => {
6162 let length = usize:: try_from ( length) . map_err ( |_| Http1ParseError :: BodyTooLarge ) ?;
6263 if length > max_body_bytes {
6364 return Err ( Http1ParseError :: BodyTooLarge . into ( ) ) ;
6465 }
65- let required = head_len
66- . checked_add ( length)
66+ let available = buffer. len ( ) . saturating_sub ( head_len) . min ( length) ;
67+ let body_end = head_len
68+ . checked_add ( available)
6769 . ok_or ( Http1ParseError :: BodyTooLarge ) ?;
68- while buffer. len ( ) < required {
70+ let mut initial = std:: mem:: take ( buffer) ;
71+ let remainder = initial. split_off ( body_end) ;
72+ sanitization:: unsafe_wipe:: volatile_sanitize_bytes ( & mut initial[ ..head_len] ) ;
73+ initial. drain ( ..head_len) ;
74+ * buffer = remainder;
75+ let mut body = NativeHttp1RequestBody :: from_vec ( initial) ;
76+ body. replace_capacity ( length) ?;
77+ while body. len ( ) < length {
78+ let remaining = length - body. len ( ) ;
6979 let mut chunk = [ 0u8 ; READ_CHUNK_BYTES ] ;
70- let read = stream. read ( & mut chunk) . await ?;
80+ let read = stream
81+ . read ( & mut chunk[ ..remaining. min ( READ_CHUNK_BYTES ) ] )
82+ . await ?;
7183 if read == 0 {
7284 return Err ( Http1ParseError :: InvalidContentLength . into ( ) ) ;
7385 }
74- buffer. extend_from_slice ( & chunk[ ..read] ) ;
86+ let append_result = body. extend_from_slice ( & chunk[ ..read] ) ;
87+ sanitization:: unsafe_wipe:: volatile_sanitize_bytes ( & mut chunk[ ..read] ) ;
88+ append_result?;
7589 }
76- let body = buffer[ head_len..required] . to_vec ( ) ;
77- buffer. drain ( ..required) ;
7890 Ok ( body)
7991 }
8092 Http1BodyFraming :: Chunked => {
@@ -89,19 +101,17 @@ async fn read_chunked_body<S>(
89101 head_len : usize ,
90102 max_body_bytes : usize ,
91103 reservation : & mut NativeRequestBodyReservation ,
92- ) -> Result < Vec < u8 > , NativeHttp1Error >
104+ ) -> Result < NativeHttp1RequestBody , NativeHttp1Error >
93105where
94106 S : AsyncRead + Unpin ,
95107{
96108 buffer. drain ( ..head_len) ;
97109 let limits = Http1ChunkLimits :: default ( ) . with_max_body_bytes ( max_body_bytes) ;
98110 let mut decoder = Http1ChunkedDecoder :: new ( limits) ;
99- let mut body = Vec :: new ( ) ;
100- let initial = std:: mem:: take ( buffer) ;
101- reservation
102- . grow_to ( initial. len ( ) . min ( max_body_bytes) )
103- . await ?;
104- if let Some ( decoded) = decoder. push ( & initial, & mut body) ? {
111+ let mut body = NativeHttp1RequestBody :: empty ( ) ;
112+ let initial = NativeHttp1RequestBody :: from_vec ( std:: mem:: take ( buffer) ) ;
113+ reserve_chunked_body_growth ( & mut body, reservation, initial. len ( ) , max_body_bytes) . await ?;
114+ if let Some ( decoded) = decoder. push_to ( & initial, & mut body) ? {
105115 buffer. extend_from_slice ( & initial[ decoded. consumed_len ..] ) ;
106116 return Ok ( body) ;
107117 }
@@ -115,10 +125,10 @@ where
115125 let prospective_encoded = fed_len
116126 . checked_add ( read)
117127 . ok_or ( Http1ParseError :: EncodedBodyTooLarge ) ?;
118- reservation
119- . grow_to ( prospective_encoded . min ( max_body_bytes ) )
120- . await ? ;
121- if let Some ( decoded) = decoder . push ( & chunk [ ..read ] , & mut body ) ? {
128+ reserve_chunked_body_growth ( & mut body , reservation, read , max_body_bytes ) . await ? ;
129+ let decoded = decoder . push_to ( & chunk [ ..read ] , & mut body ) ;
130+ sanitization :: unsafe_wipe :: volatile_sanitize_bytes ( & mut chunk [ ..read ] ) ;
131+ if let Some ( decoded) = decoded ? {
122132 let consumed_from_chunk = decoded
123133 . consumed_len
124134 . checked_sub ( fed_len)
@@ -132,3 +142,58 @@ where
132142 fed_len = prospective_encoded;
133143 }
134144}
145+
146+ async fn reserve_chunked_body_growth (
147+ body : & mut NativeHttp1RequestBody ,
148+ reservation : & mut NativeRequestBodyReservation ,
149+ possible_growth : usize ,
150+ max_body_bytes : usize ,
151+ ) -> Result < ( ) , NativeHttp1Error > {
152+ let required = body
153+ . len ( )
154+ . checked_add ( possible_growth)
155+ . ok_or ( Http1ParseError :: BodyTooLarge ) ?
156+ . min ( max_body_bytes) ;
157+ if required <= body. capacity ( ) {
158+ return Ok ( ( ) ) ;
159+ }
160+ let geometric = body. capacity ( ) . max ( READ_CHUNK_BYTES ) . saturating_mul ( 2 ) ;
161+ let admitted_capacity = required. max ( geometric) . min ( max_body_bytes) ;
162+ reservation. grow_to ( admitted_capacity) . await ?;
163+ body. reserve_capacity ( admitted_capacity) ?;
164+ Ok ( ( ) )
165+ }
166+
167+ #[ cfg( test) ]
168+ mod tests {
169+ use super :: * ;
170+ use crate :: NativeRequestBodyBudget ;
171+
172+ #[ tokio:: test]
173+ async fn content_length_moves_body_and_releases_connection_buffer_capacity ( ) {
174+ let head = b"POST / HTTP/1.1\r \n Host: local.test\r \n Content-Length: 5\r \n \r \n " ;
175+ let pipeline = b"GET /next HTTP/1.1\r \n Host: local.test\r \n \r \n " ;
176+ let mut buffer = Vec :: with_capacity ( 1024 * 1024 ) ;
177+ buffer. extend_from_slice ( head) ;
178+ buffer. extend_from_slice ( b"hello" ) ;
179+ buffer. extend_from_slice ( pipeline) ;
180+ let original_capacity = buffer. capacity ( ) ;
181+ let mut reservation = NativeRequestBodyBudget :: new ( 1024 ) . reserve ( 5 ) . await . unwrap ( ) ;
182+
183+ let body = read_body_inner (
184+ & mut tokio:: io:: empty ( ) ,
185+ & mut buffer,
186+ head. len ( ) ,
187+ Http1BodyFraming :: ContentLength ( 5 ) ,
188+ 1024 ,
189+ & mut reservation,
190+ )
191+ . await
192+ . unwrap ( ) ;
193+
194+ assert_eq ! ( body. as_ref( ) , b"hello" ) ;
195+ assert_eq ! ( buffer, pipeline) ;
196+ assert ! ( buffer. capacity( ) < original_capacity) ;
197+ assert ! ( body. capacity( ) >= body. len( ) ) ;
198+ }
199+ }
0 commit comments