Skip to content

Commit 38ce91a

Browse files
committed
admin::render_readmes: Support out of order tar files
Achieves this by pulling the entire tar file into memory in the form of a hashmap. Then we can look for the files as needed. Before, we would stream the pkg response from the server and un-tar it on the fly, but this behavior is incorrect if the Cargo.toml isn't at the beginning of the archive. Could imagine this might be problematic if the entire archive won't fit in memory - but that seems unlikely? We could mitigate by limiting to certain file types, but we won't know what type the README will be. Added a test to for this case - the test fails before this change and passes with this change
1 parent 824055f commit 38ce91a

File tree

1 file changed

+38
-33
lines changed

1 file changed

+38
-33
lines changed

src/admin/render_readmes.rs

+38-33
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
schema::{crates, readme_renderings, versions},
55
uploaders::Uploader,
66
};
7-
use std::{io::Read, path::Path, sync::Arc, thread};
7+
use std::{collections::HashMap, ffi::OsString, io::Read, sync::Arc, thread};
88

99
use cargo_registry_markdown::text_to_html;
1010
use chrono::{TimeZone, Utc};
@@ -196,16 +196,28 @@ fn get_readme(
196196
render_pkg_readme(archive, &pkg_name)
197197
}
198198

199-
fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> Option<String> {
200-
let mut entries = archive
199+
fn render_pkg_readme<R: Read>(mut pkg: Archive<R>, pkg_name: &str) -> Option<String> {
200+
// Pulls the entire archive into memory - since we need to access files
201+
// within the archive potentially out of order (first Cargo.toml, then README)
202+
let entries: HashMap<_, _> = pkg
201203
.entries()
202-
.unwrap_or_else(|_| panic!("[{}] Invalid tar archive entries", pkg_name));
204+
.unwrap_or_else(|_| panic!("[{}] Invalid tar archive entries", pkg_name))
205+
.filter_map(|e| {
206+
let mut file = e.ok()?;
207+
let path = file.path().ok()?.as_os_str().to_owned();
208+
let mut contents = String::new();
209+
file.read_to_string(&mut contents)
210+
.unwrap_or_else(|_| panic!("[{}] Couldn't read file contents", pkg_name));
211+
Some((path, contents))
212+
})
213+
.collect();
203214

204215
let manifest: Manifest = {
205216
let path = format!("{}/Cargo.toml", pkg_name);
206-
let contents = find_file_by_path(&mut entries, Path::new(&path), pkg_name)
217+
let contents = entries
218+
.get(&OsString::from(path))
207219
.unwrap_or_else(|| panic!("[{}] couldn't open file: Cargo.toml", pkg_name));
208-
toml::from_str(&contents)
220+
toml::from_str(contents)
209221
.unwrap_or_else(|_| panic!("[{}] Syntax error in manifest file", pkg_name))
210222
};
211223

@@ -216,9 +228,9 @@ fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> Option
216228
.clone()
217229
.unwrap_or_else(|| "README.md".into());
218230
let path = format!("{}/{}", pkg_name, readme_path);
219-
let contents = find_file_by_path(&mut entries, Path::new(&path), pkg_name)?;
231+
let contents = entries.get(&OsString::from(path))?;
220232
text_to_html(
221-
&contents,
233+
contents,
222234
&readme_path,
223235
manifest.package.repository.as_deref(),
224236
)
@@ -237,31 +249,6 @@ fn render_pkg_readme<R: Read>(mut archive: Archive<R>, pkg_name: &str) -> Option
237249
}
238250
}
239251

240-
/// Search an entry by its path in a Tar archive.
241-
fn find_file_by_path<R: Read>(
242-
entries: &mut tar::Entries<'_, R>,
243-
path: &Path,
244-
pkg_name: &str,
245-
) -> Option<String> {
246-
let mut file = entries
247-
.find(|entry| match *entry {
248-
Err(_) => false,
249-
Ok(ref file) => {
250-
let filepath = match file.path() {
251-
Ok(p) => p,
252-
Err(_) => return false,
253-
};
254-
filepath == path
255-
}
256-
})?
257-
.unwrap_or_else(|_| panic!("[{}] file is not present: {}", pkg_name, path.display()));
258-
259-
let mut contents = String::new();
260-
file.read_to_string(&mut contents)
261-
.unwrap_or_else(|_| panic!("[{}] Couldn't read file contents", pkg_name));
262-
Some(contents)
263-
}
264-
265252
#[cfg(test)]
266253
mod tests {
267254
use std::io::Write;
@@ -325,6 +312,24 @@ readme = "README.md"
325312
assert!(result.contains("readme"))
326313
}
327314

315+
#[test]
316+
fn test_render_pkg_readme_tar_order_backward() {
317+
let mut pkg = tar::Builder::new(vec![]);
318+
add_file(&mut pkg, "foo-0.0.1/README.md", b"readme");
319+
add_file(
320+
&mut pkg,
321+
"foo-0.0.1/Cargo.toml",
322+
br#"
323+
[package]
324+
readme = "README.md"
325+
"#,
326+
);
327+
let serialized_archive = pkg.into_inner().unwrap();
328+
let result =
329+
render_pkg_readme(tar::Archive::new(&*serialized_archive), "foo-0.0.1").unwrap();
330+
assert!(result.contains("readme"))
331+
}
332+
328333
#[test]
329334
fn test_render_pkg_readme_w_link() {
330335
let mut pkg = tar::Builder::new(vec![]);

0 commit comments

Comments
 (0)