fix(download): treat a short clean EOF as resumable, not complete - #503
Open
A831ARD0 wants to merge 1 commit into
Open
fix(download): treat a short clean EOF as resumable, not complete#503A831ARD0 wants to merge 1 commit into
A831ARD0 wants to merge 1 commit into
Conversation
Follow-up to spotiflacapp#491, which was fixed in 4.8.5 by making mid-download resume opt-in (safe default: fail and delete the staged file, since switching networks can route a stable URL to a different CDN object). That fix only changes what happens on a *real* read error. It doesn't help if a transport surfaces a mid-transfer connection drop as a plain io.EOF instead of io.ErrUnexpectedEOF - fileDownload's copyBody loop still breaks out and promotes the file on any clean EOF, regardless of whether written bytes actually reached Content-Length. This app's uTLS-based client (used for TLS-fingerprint spoofing) is exactly the kind of custom transport where that guarantee isn't necessarily upheld. Now a clean EOF short of a known Content-Length is routed through the same resume-or-fail path as a real read error, so it respects the same opt-in `resume` option: fails and cleans up the staged file by default, or resumes via Range/If-Range when the caller explicitly requested it and the server provided a validator. extension_runtime_file_download_integrity_test.go adds three tests: - fails by default even when a validator is present (matches the opt-in policy from 4.8.5) - resumes correctly when `resume: true` is passed - fails and cleans up when there's no validator at all All three fail on the pre-fix code and pass with it. Note: fileDownloadChunked's unknown-total-size path (used for YouTube's CDN) has an analogous but harder-to-fix ambiguity - without a known length there's no way to distinguish a legitimately short final chunk from a truncated one - left as a follow-up.
A831ARD0
force-pushed
the
fix/verify-download-content-length
branch
from
August 1, 2026 20:38
c29cb75 to
610266c
Compare
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #491, which was fixed in 4.8.5 by making mid-download resume opt-in (safe default: fail and delete the staged partial file, since switching networks can route a stable URL to a different CDN object even with an unchanged validator).
The remaining gap
That fix only changes behavior for a real read error.
fileDownload'scopyBodyloop still breaks out and promotes the file on any cleanio.EOF, without checking whether the bytes written actually reached the response'sContent-Length. Some transports (this app's uTLS-based client, used for TLS-fingerprint spoofing, is exactly this kind of custom transport) can surface a mid-transfer connection drop as a plainio.EOFinstead ofio.ErrUnexpectedEOF— so a network change mid-download could still get silently promoted as a "successful" truncated file, bypassing the new opt-in resume logic entirely (it never even reaches thecanResumecheck).The fix
A clean EOF short of a known
Content-Lengthis now routed through the same resume-or-fail path as a real read error, so it respects the sameresumeoption introduced in 4.8.5:Range/If-Rangewhen the caller passedresume: trueand the server provided a validator.Tests
extension_runtime_file_download_integrity_test.goadds three tests, verified to fail on the pre-fix code and pass with it:TestFileDownloadShortCleanEOFFailsByDefaultEvenWithValidator— fails and cleans up by default, even with a validator present.TestFileDownloadResumesAfterShortCleanEOFWhenEnabled— resumes and completes correctly withresume: true.TestFileDownloadShortCleanEOFWithoutValidatorFails— fails and leaves no file behind when there's no validator at all.Known follow-up (not in this PR)
fileDownloadChunked's unknown-total-size path (used for YouTube's CDN, where the total length isn't known upfront) has an analogous but harder-to-fix ambiguity: without a known length, there's no reliable way to distinguish a legitimately short final chunk from a truncated one.Verification
go build ./...,go vet ./...,gofmt -l— cleango test ./...— all green, including the 3 new testsmainafter the 4.8.5 resume changes landed