FileSharer Development Notes 4 — Range Boundaries When Resuming a Download

한국어 버전

FileSharer Development Notes · 04 / 10

Three developers align pieces of a long file to explain byte ranges and resumed downloads.
AI illustration · Three developers align pieces of a long file to explain byte ranges and resumed downloads.

When a connection drops just before a file finishes downloading, starting over can feel costly. An HTTP Range request specifies the byte interval the client needs. FileSharer handles partial requests through the same transfer function for both individual files and ZIP archives it has already generated.

A report of actual return values from the original TryParseRange function. The function tests assume a 1,000-byte file; they do not measure actual transfer speeds.
A report of actual return values from the original TryParseRange function. The function tests assume a 1,000-byte file; they do not measure actual transfer speeds.

The first thing to examine in download resumption is the range arithmetic behind the claim of “Range support.” An off-by-one error at either end can omit or duplicate a byte.

Count the end position, too

A 1,000-byte file has positions from 0 to 999. The length requested by bytes=100-199 is 100 bytes, not 99.

Transfer length = end position - start position + 1
100 = 199 - 100 + 1

The original ServeFile also calculates the length as end - start + 1 and calls Seek on the file stream when the start position is greater than 0. It constructs a 206 response with Content-Range for a valid partial response, and a 416 response for a request it cannot satisfy. These response semantics are described in the Range provisions of RFC 9110.

Inputs sent to the actual parser

I extracted and ran TryParseRange unchanged from the original source. The results below assume a total file size of 1,000 bytes. Output variables are not used when the range is invalid.

Range input Result Selected interval
bytes=0-99 Accepted 0–99
bytes=100- Accepted 100–999
bytes=-50 Accepted 950–999
bytes=900-1200 Accepted 900–999
bytes=1000- Rejected None
bytes=100-99 Rejected None
bytes=-0 Rejected None
bytes=0-9,20-29 Accepted First interval, 0–9, only

I also checked the invalid unit items, an input containing spaces, bytes = 10-19, and a zero-byte file, for a total of 11 inputs. The invalid-unit and empty-file requests returned false; the input containing spaces returned true. These results describe this implementation's behavior, rather than a conformance test covering the full syntax.

Multiple ranges and multiple connections are separate features

When the current parser encounters a comma, it takes only the first interval. This differs from an implementation that accepts several ranges in one request and returns all of them in a multipart response.

The server can handle multiple client connections, so a download tool splitting intervals across separate requests is a separate matter. The pump in the built-in encryption page, however, fetches intervals sequentially and retries on failure. Describing that page as automatically downloading several intervals at once would misrepresent the code.

Any claim of improved throughput would require a separate measurement with the connection count, network connection, file size, and encryption setting held fixed. This test checked the parser's return values; it did not measure speed multipliers.

Correct positions are not enough if the file changes

If the first part received in the initial request belongs to version A and the remaining part received on retry belongs to version B, each interval can be valid while the combined file is wrong. Resuming requires checking not only byte positions, but also whether the client is still receiving the same representation of the file.

The current response includes an ETag derived from the modification time and file size. Sending an ETag, however, is different from actually evaluating a conditional request. I did not find If-Range validation in this transfer path. Nor can the browser's retry logic be assumed to validate file changes throughout the process.

Possible improvements include fixing the file version when the share is created, or associating a version identifier with the transfer session and stopping resumption if the version changes. Comparing hashes after receipt is a separate step for verifying that the completed result matches.

What to check after the parser

These executions make the boundary calculations directly visible. The next integration tests should check resumption after a connection drops, changes to the source file during transfer, HEAD requests, and empty files, together with the actual HTTP responses. Breaking “resume support” into several smaller contracts makes it much easier to identify where a failure occurs.

Source references
FileSharer.cs:5154–5197 TryParseRange · FileSharer.cs:5250–5322 ServeFile header handling · FileSharer.cs:5343–5367 Seeking and transfer · FileSharer.cs:5800–5878 Browser pump
Proposed improvements are distinguished from implemented behavior.

 

댓글