Skip to content

Commit 2260f6a

Browse files
committed
Improve loading binary package lists
Write out a format that can be loaded very quickly.
1 parent d7f9d94 commit 2260f6a

4 files changed

Lines changed: 1520 additions & 141 deletions

File tree

src/download.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,91 @@ pub fn download_first_available_(
349349
download_first_available__(client_, urls, local_path, etag)
350350
}
351351

352+
/// What a conditional fetch found. See [`fetch_optional_if_modified_`].
353+
pub enum ConditionalFetch {
354+
/// The server has no such resource (404).
355+
NotFound,
356+
/// The server confirmed the caller's copy is still current (304). There is
357+
/// no body to go with this.
358+
NotModified,
359+
/// New content, with the `ETag` identifying it if the server sent one.
360+
Fetched {
361+
bytes: Vec<u8>,
362+
etag: Option<String>,
363+
},
364+
}
365+
366+
/// Conditionally fetch a resource into memory.
367+
///
368+
/// Like `download_optional_if_newer_`, but nothing is written to disk and
369+
/// there is no TTL check: the caller decides when to call this and what to
370+
/// keep. That is what you want when the downloaded bytes are not the artifact
371+
/// being cached — the binary indices are parsed and stored in a different
372+
/// format entirely, so writing the response out only to delete it again would
373+
/// be pure overhead.
374+
///
375+
/// Passing `etag` sends `If-None-Match`. Only do that while you still hold the
376+
/// content it describes: a 304 comes with no body, so asking for one you
377+
/// cannot use leaves you with nothing.
378+
pub fn fetch_optional_if_modified_(
379+
url: &str,
380+
etag: Option<&str>,
381+
client: Option<&reqwest::Client>,
382+
) -> Result<ConditionalFetch, Box<dyn Error>> {
383+
let client_ = match client {
384+
Some(c) => c,
385+
None => &reqwest::Client::new(),
386+
};
387+
fetch_optional_if_modified__(client_, url, etag)
388+
}
389+
390+
#[tokio::main]
391+
async fn fetch_optional_if_modified__(
392+
client: &reqwest::Client,
393+
url: &str,
394+
etag: Option<&str>,
395+
) -> Result<ConditionalFetch, Box<dyn Error>> {
396+
fetch_optional_if_modified(client, url, etag).await
397+
}
398+
399+
async fn fetch_optional_if_modified(
400+
client: &reqwest::Client,
401+
url: &str,
402+
etag: Option<&str>,
403+
) -> Result<ConditionalFetch, Box<dyn Error>> {
404+
let mut req = client.get(url);
405+
if let Some(etag) = etag {
406+
req = req.header("If-None-Match", etag);
407+
}
408+
info!("Checking for updates for {}", url);
409+
let resp = req.send().await?;
410+
411+
match resp.status() {
412+
StatusCode::NOT_MODIFIED => Ok(ConditionalFetch::NotModified),
413+
414+
StatusCode::OK => {
415+
let etag = resp
416+
.headers()
417+
.get("etag")
418+
.and_then(|v| v.to_str().ok())
419+
.map(|s| s.to_string());
420+
let bytes = resp.bytes().await?.to_vec();
421+
Ok(ConditionalFetch::Fetched { bytes, etag })
422+
}
423+
424+
StatusCode::NOT_FOUND => {
425+
debug!("No such resource (404): {}", url);
426+
Ok(ConditionalFetch::NotFound)
427+
}
428+
429+
status => {
430+
OUTPUT.error(&format!("Failed to download {}, status: {}", url, status));
431+
error!("Failed to download {}, status: {}", url, status);
432+
bail!("Failed to download {}, status: {}", url, status);
433+
}
434+
}
435+
}
436+
352437
/// Like `download_if_newer`, but a 404 is a normal outcome rather than an
353438
/// error: it returns `Ok(None)` instead of failing and printing to the
354439
/// terminal. Used for optional per-package metadata that simply may not exist

0 commit comments

Comments
 (0)