Skip to content

Commit 168efcb

Browse files
committed
fix: Accept Transfer-Encoding: chunked downloads
Upstream crates.io servers do not use this encoding, but apparently the `tiny-http` crate server used by crates-io-proxy does. crates-io-proxy should now be able to proxy itself. Resolves #8
1 parent e9d47b8 commit 168efcb

File tree

1 file changed

+31
-6
lines changed

1 file changed

+31
-6
lines changed

src/main.rs

+31-6
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ fn ureq_agent() -> ureq::Agent {
144144
.clone()
145145
}
146146

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+
147157
/// Downloads the crate file from the upstream download server
148158
/// (usually <https://crates.io/>).
149159
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
160170

161171
if let Some(content_len) = response.header("Content-Length") {
162172
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"));
165174
};
166175

167176
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"));
170178
}
171179

172180
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
177185

178186
Ok(data)
179187
} 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)
182207
}
183208
}
184209

0 commit comments

Comments
 (0)