fix(download): single-threaded fallback produces empty file when Content-Length unknown #138

Closed
opened 2026-07-13 18:07:57 +00:00 by the.auditor · 1 comment
Owner

internal/downloader/download.go:162-172 — When server doesn't provide Content-Length, fileSize stays 0. SplitChunks(0, 1) returns [{Start: 0, End: 0}], and runWorker does io.CopyN(ws, body, 0) — reads 0 bytes. Result: empty output file.

This path triggers when:

  • HTTP server doesn't support ranges AND doesn't send Content-Length
  • HTTP server supports ranges but doesn't send Content-Length
  • FTP server doesn't support REST AND SIZE fails

The single-threaded fallback should download the entire file without Range headers and without a size limit. Suggestion:

// In Download(), for single-threaded fallback with unknown size:
if !parallel && fileSize <= 0 {
    // Download full file without Range header, no size limit
    chunks = []Chunk{{Start: 0, End: math.MaxInt64}}
} else {
    chunks = SplitChunks(fileSize, chunkCount)
}

And in runWorker/downloadChunkHTTP, when chunk.End == math.MaxInt64, don't set Range header and use io.Copy instead of io.CopyN.

Discovered during review of PR #137.

`internal/downloader/download.go:162-172` — When server doesn't provide `Content-Length`, `fileSize` stays 0. `SplitChunks(0, 1)` returns `[{Start: 0, End: 0}]`, and `runWorker` does `io.CopyN(ws, body, 0)` — reads 0 bytes. Result: empty output file. This path triggers when: - HTTP server doesn't support ranges AND doesn't send Content-Length - HTTP server supports ranges but doesn't send Content-Length - FTP server doesn't support REST AND SIZE fails The single-threaded fallback should download the entire file without Range headers and without a size limit. Suggestion: ```go // In Download(), for single-threaded fallback with unknown size: if !parallel && fileSize <= 0 { // Download full file without Range header, no size limit chunks = []Chunk{{Start: 0, End: math.MaxInt64}} } else { chunks = SplitChunks(fileSize, chunkCount) } ``` And in `runWorker`/`downloadChunkHTTP`, when chunk.End == math.MaxInt64, don't set Range header and use `io.Copy` instead of `io.CopyN`. Discovered during review of PR #137.
Owner

Closing — already handled. When fileSize <= 0 and parallel is false, fullDownload=true and runFullDownload uses io.Copy (reads until EOF) instead of io.CopyN with expected size.

Closing — already handled. When fileSize <= 0 and parallel is false, fullDownload=true and runFullDownload uses io.Copy (reads until EOF) instead of io.CopyN with expected size.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/pget#138
No description provided.