feat: resume an interrupted download instead of refetching it (#18)
Downloads buffered the whole file in memory and wrote it out only on success, so an interrupted transfer left nothing behind and always started over. The START_DOWNLOAD offset was hardcoded to zero on the download side and discarded on the upload side, so neither half of the protocol's resume support was wired up. Bytes now stream into `<file>.part` as they arrive and it is renamed only once the file is whole. A new attempt sends the partial file's length as the START_DOWNLOAD offset, and `serve_file` seeks to the offset a peer asks for rather than replaying from the start. A `.part` at or past the expected size is treated as stale and restarted. Partial files are kept out of the share index, since the download directory is shared by default.
This commit is contained in:
@@ -198,6 +198,14 @@ fn scan_root(
|
||||
if meta.is_dir() {
|
||||
stack.push(path);
|
||||
} else if meta.is_file() {
|
||||
// The download directory is usually shared too; an in-progress
|
||||
// `.part` file is not something to offer other peers.
|
||||
if path
|
||||
.to_string_lossy()
|
||||
.ends_with(crate::utils::path::PART_SUFFIX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let virtual_path = virtual_path_for(root_name, root, &path);
|
||||
files.push(SharedFile {
|
||||
virtual_path,
|
||||
@@ -253,6 +261,18 @@ mod tests {
|
||||
let _ = std::fs::remove_dir_all(root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_progress_downloads_are_not_shared() {
|
||||
let root = temp_tree();
|
||||
std::fs::write(root.join("half.mp3.part"), b"dd").unwrap();
|
||||
|
||||
let shares = Shares::scan(&root).unwrap();
|
||||
assert_eq!(shares.file_count(), 3, ".part files stay out of the index");
|
||||
let base = root.file_name().unwrap().to_string_lossy().into_owned();
|
||||
assert!(shares.get(&format!("{base}\\half.mp3.part")).is_none());
|
||||
let _ = std::fs::remove_dir_all(root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_files_use_backslash_virtual_paths() {
|
||||
let root = temp_tree();
|
||||
|
||||
Reference in New Issue
Block a user