@@ -144,6 +144,16 @@ fn ureq_agent() -> ureq::Agent {
144
144
. clone ( )
145
145
}
146
146
147
+ /// Makes boxed custom ureq status errors for `download_crate()`.
148
+ fn ureq_status_error ( status_code : u16 , msg : & str ) -> Box < ureq:: Error > {
149
+ assert ! ( status_code >= 400 ) ;
150
+
151
+ Box :: new ( ureq:: Error :: Status (
152
+ status_code,
153
+ ureq:: Response :: new ( status_code, msg, msg) . unwrap ( ) ,
154
+ ) )
155
+ }
156
+
147
157
/// Downloads the crate file from the upstream download server
148
158
/// (usually <https://crates.io/>).
149
159
fn download_crate ( site_url : & Url , crate_info : & CrateInfo ) -> Result < Vec < u8 > , Box < ureq:: Error > > {
@@ -160,13 +170,11 @@ fn download_crate(site_url: &Url, crate_info: &CrateInfo) -> Result<Vec<u8>, Box
160
170
161
171
if let Some ( content_len) = response. header ( "Content-Length" ) {
162
172
let Ok ( len) = content_len. parse :: < usize > ( ) else {
163
- // HTTP 400 Invalid Header
164
- return Err ( Box :: new ( ureq:: Error :: Status ( 400 , response) ) ) ;
173
+ return Err ( ureq_status_error ( 400 , "Invalid header" ) ) ;
165
174
} ;
166
175
167
176
if len > MAX_CRATE_SIZE {
168
- // HTTP 507 Insufficient Storage
169
- return Err ( Box :: new ( ureq:: Error :: Status ( 507 , response) ) ) ;
177
+ return Err ( ureq_status_error ( 507 , "Insufficient storage" ) ) ;
170
178
}
171
179
172
180
let mut data: Vec < u8 > = Vec :: with_capacity ( len) ;
@@ -177,8 +185,25 @@ fn download_crate(site_url: &Url, crate_info: &CrateInfo) -> Result<Vec<u8>, Box
177
185
178
186
Ok ( data)
179
187
} else {
180
- // HTTP 502 Bad Gateway
181
- Err ( Box :: new ( ureq:: Error :: Status ( 502 , response) ) )
188
+ // Got no "Content-Length" header, most likely because "Transfer-Encoding: chunked"
189
+ // is being sent by the server (crates.io servers do not do this).
190
+ //
191
+ // Using an arbitrary initial estimate for the total response size...
192
+ let mut data: Vec < u8 > = Vec :: with_capacity ( MAX_CRATE_SIZE / 256 ) ;
193
+
194
+ response
195
+ . into_reader ( )
196
+ . take ( MAX_CRATE_SIZE as u64 )
197
+ . read_to_end ( & mut data)
198
+ . map_err ( |e| Box :: new ( e. into ( ) ) ) ?;
199
+
200
+ // Abort download here if the crate file has been truncated by
201
+ // the `reader.take()` limit above.
202
+ if data. len ( ) >= MAX_CRATE_SIZE {
203
+ return Err ( ureq_status_error ( 507 , "Insufficient storage" ) ) ;
204
+ }
205
+
206
+ Ok ( data)
182
207
}
183
208
}
184
209
0 commit comments