@@ -88,7 +88,12 @@ pub struct StreamingBlob {
8888 pub date_updated : DateTime < Utc > ,
8989 pub etag : Option < ETag > ,
9090 pub compression : Option < CompressionAlgorithm > ,
91- pub content_length : usize ,
91+ /// initially the original content length on S3,
92+ /// what we return to the user might be bigger because of
93+ /// streaming decompression.
94+ /// Will be emptied when we decompress the blob because then
95+ /// re return more data than we have on S3.
96+ pub content_length : Option < usize > ,
9297 pub content : Box < dyn AsyncBufRead + Unpin + Send > ,
9398}
9499
@@ -100,6 +105,7 @@ impl fmt::Debug for StreamingBlob {
100105 . field ( "date_updated" , & self . date_updated )
101106 . field ( "etag" , & self . etag )
102107 . field ( "compression" , & self . compression )
108+ . field ( "content_length" , & self . content_length )
103109 . finish ( )
104110 }
105111}
@@ -133,14 +139,15 @@ impl StreamingBlob {
133139 ) ;
134140
135141 self . compression = None ;
142+ self . content_length = None ;
136143 // not touching the etag, it should represent the original content
137144 Ok ( self )
138145 }
139146
140147 /// consume the inner stream and materialize the full blob into memory.
141148 pub async fn materialize ( mut self , max_size : usize ) -> Result < Blob > {
142149 let mut content = SizedBuffer :: new ( max_size) ;
143- content. reserve ( self . content_length ) ;
150+ content. reserve ( self . content_length . unwrap_or ( 16 * 1024 ) ) ;
144151
145152 io:: copy ( & mut self . content , & mut content) . await ?;
146153
@@ -163,7 +170,7 @@ impl From<Blob> for StreamingBlob {
163170 date_updated : value. date_updated ,
164171 etag : value. etag ,
165172 compression : value. compression ,
166- content_length : value. content . len ( ) ,
173+ content_length : Some ( value. content . len ( ) ) ,
167174 content : Box :: new ( Cursor :: new ( value. content ) ) ,
168175 }
169176 }
@@ -188,7 +195,7 @@ mod test {
188195 date_updated : Utc :: now ( ) ,
189196 compression : alg,
190197 etag : Some ( compute_etag ( & content) ) ,
191- content_length : content. len ( ) ,
198+ content_length : Some ( content. len ( ) ) ,
192199 content : Box :: new ( Cursor :: new ( content) ) ,
193200 }
194201 }
@@ -200,14 +207,17 @@ mod test {
200207 // without decompression
201208 {
202209 let stream = streaming_blob ( CONTENT , None ) ;
210+ assert_eq ! ( stream. content_length, Some ( CONTENT . len( ) ) ) ;
203211 let blob = stream. materialize ( usize:: MAX ) . await ?;
204212 assert_eq ! ( blob. content, CONTENT ) ;
205213 assert ! ( blob. compression. is_none( ) ) ;
206214 }
207215
208216 // with decompression, does nothing
209217 {
210- let stream = streaming_blob ( CONTENT , None ) ;
218+ let stream = streaming_blob ( CONTENT , None ) . decompress ( ) . await ?;
219+ // no compression, content length stays valid
220+ assert_eq ! ( stream. content_length, Some ( CONTENT . len( ) ) ) ;
211221 let blob = stream. decompress ( ) . await ?. materialize ( usize:: MAX ) . await ?;
212222 assert_eq ! ( blob. content, CONTENT ) ;
213223 assert ! ( blob. compression. is_none( ) ) ;
@@ -225,6 +235,7 @@ mod test {
225235 // Doesn't fail because we don't call `.decompress`
226236 {
227237 let stream = streaming_blob ( NOT_ZSTD , Some ( alg) ) ;
238+ assert_eq ! ( stream. content_length, Some ( NOT_ZSTD . len( ) ) ) ;
228239 let blob = stream. materialize ( usize:: MAX ) . await ?;
229240 assert_eq ! ( blob. content, NOT_ZSTD ) ;
230241 assert_eq ! ( blob. compression, Some ( alg) ) ;
@@ -267,6 +278,7 @@ mod test {
267278 // without decompression
268279 {
269280 let stream = streaming_blob ( compressed_content. clone ( ) , Some ( alg) ) ;
281+ assert_eq ! ( stream. content_length, Some ( compressed_content. len( ) ) ) ;
270282 let blob = stream. materialize ( usize:: MAX ) . await ?;
271283 assert_eq ! ( blob. content, compressed_content) ;
272284 assert_eq ! ( blob. content. last_chunk:: <3 >( ) . unwrap( ) , & ZSTD_EOF_BYTES ) ;
@@ -275,11 +287,12 @@ mod test {
275287
276288 // with decompression
277289 {
278- let blob = streaming_blob ( compressed_content. clone ( ) , Some ( alg) )
290+ let stream = streaming_blob ( compressed_content. clone ( ) , Some ( alg) )
279291 . decompress ( )
280- . await ?
281- . materialize ( usize:: MAX )
282292 . await ?;
293+ // content length becomes unknown with decompression
294+ assert ! ( stream. content_length. is_none( ) ) ;
295+ let blob = stream. materialize ( usize:: MAX ) . await ?;
283296 assert_eq ! ( blob. content, CONTENT ) ;
284297 assert ! ( blob. compression. is_none( ) ) ;
285298 }
0 commit comments