Cargo.lock 🔗
@@ -3507,6 +3507,7 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
+ "isahc",
"language",
"log",
"lsp",
gcp-cherry-pick-bot[bot] , Max Brunsfeld , and Marshall created
Cherry-picked Validate content-length of downloaded extension tar gz
files (#10430)
Release Notes:
- Fixed a bug where extension installation would appear to succeed even
if the download did not complete due to network interruptions
([#10330](https://github.com/zed-industries/zed/issues/10330)).
Co-authored-by: Marshall <marshall@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Marshall <marshall@zed.dev>
Cargo.lock | 1 +
crates/extension/Cargo.toml | 1 +
crates/extension/src/extension_store.rs | 18 +++++++++++++++++-
3 files changed, 19 insertions(+), 1 deletion(-)
@@ -3507,6 +3507,7 @@ dependencies = [
"fs",
"futures 0.3.28",
"gpui",
+ "isahc",
"language",
"log",
"lsp",
@@ -23,6 +23,7 @@ collections.workspace = true
fs.workspace = true
futures.workspace = true
gpui.workspace = true
+isahc.workspace = true
language.workspace = true
log.workspace = true
lsp.workspace = true
@@ -605,7 +605,23 @@ impl ExtensionStore {
)
.await?;
- let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut()));
+ let content_length = response
+ .headers()
+ .get(isahc::http::header::CONTENT_LENGTH)
+ .and_then(|value| value.to_str().ok()?.parse::<usize>().ok());
+
+ let mut body = BufReader::new(response.body_mut());
+ let mut tgz_bytes = Vec::new();
+ body.read_to_end(&mut tgz_bytes).await?;
+
+ if let Some(content_length) = content_length {
+ let actual_len = tgz_bytes.len();
+ if content_length != actual_len {
+ bail!("downloaded extension size {actual_len} does not match content length {content_length}");
+ }
+ }
+ let decompressed_bytes = GzipDecoder::new(BufReader::new(tgz_bytes.as_slice()));
+ // let decompressed_bytes = GzipDecoder::new(BufReader::new(tgz_bytes));
let archive = Archive::new(decompressed_bytes);
archive.unpack(extension_dir).await?;
this.update(&mut cx, |this, cx| {