范围请求让客户端请求表示的一部分。媒体播放器、可断点续传的下载工具与诊断客户端会用到它,但服务器如果错误地声明或返回范围,就可能产生静默损坏,而不是干净的失败。

稳定的编码后表示共 N 字节,提供参考大小。标尺选中含端点的 0 到 15,恰好 16 字节。分支区分匹配正文与 Content-Range 的 206、忽略 Range 时的完整 200,以及从 N 开始的越界请求被拒绝时的 416 和 bytes */N。强 If-Range 验证器防止拼接不同版本。禁止弱 ETag;只有无 ETag 且日期满足强验证器条件时才能使用 HTTP 日期。表示改变时须返回完整的最新正文。
保持表示与内容编码一致,同时核对区间、总大小与实际正文字节。忽略 Range 时,完整的 200 可以合法;续传需要合适的强验证器。

建立完整大小

选择至少 16 字节、测试期间不变的下载文件。在同一个一次性 shell 会话和临时工作目录中依次运行以下片段,保留 url 与 size。显式的 set -eu 会让顺序执行的传输与字节断言失败时停止;HTTP 状态与范围响应头仍需按下文人工核对:

set -eu
url=https://www.example.test/downloads/archive.tar.gz
curl -fsS -H 'Accept-Encoding: identity' -D full.headers -o full.body "$url"
size=$(wc -c < full.body)
test "$size" -ge 16
awk 'tolower($1) ~ /^(content-length:|content-encoding:|etag:|last-modified:|accept-ranges:)$/ { print }' full.headers

确认完整响应为 200,且没有意外的内容编码。这些命令请求 identity 表示,不做自动解压;字节范围针对所选的编码后表示,因此改变编码会使比较失效。Content-Length 可能缺失,所以用完整正文确定大小。

Accept-Ranges: bytes 是显式信号,但缺少这个头并不能证明范围请求不受支持。去实际请求一次。

请求一个已知区间

请求开头 16 字节,同时保留响应头与正文:

set -eu
curl -sS -D range.headers -o range.body \
  -H 'Accept-Encoding: identity' -H 'Range: bytes=0-15' "$url"

wc -c range.body
head -c 16 full.body > expected.body
cmp expected.body range.body
awk 'tolower($1) ~ /^(content-range:|content-length:|etag:)$/ || /^HTTP\// { print }' range.headers

成功的字节范围响应应使用 206 Partial Content。Content-Range 应指出 0-15 与完整表示长度,而正文应正好包含 16 个字节。

这里的字节比较专门检查部分正文。合法但大于 16 字节的完整 200 会使比较失败并停止当前 shell 会话;先查看已保存的响应头,再判断是否发生损坏。反过来,完整文件恰好为 16 字节时,即便返回 200 也可能通过 cmp,所以仍需核对状态码与 Content-Range。

测试一个不可能的区间

从完整字节数开始请求:大小为 N 的文件,最后一个有效偏移量是 N−1。与任意写一个大常量不同,这确实超出了已知表示:

set -eu
curl -sS -D - -o /dev/null \
  -H 'Accept-Encoding: identity' -H "Range: bytes=${size}-" "$url"

服务器用 416 Range Not Satisfiable 拒绝无法满足的字节范围时,应附带 Content-Range(例如 bytes */12345)报告当前大小。当服务器忽略 Range 时返回 200 也可能合法,但它必须返回完整表示,而不是一个标签错误的片段。

让验证器留在故事里

续传工具可以用 If-Range 配合强 ETag。不能使用弱 ETag;只有在没有 ETag、且日期符合强验证器条件时,才能使用 HTTP 日期。如果表示已改变,安全响应是完整的最新表示,而不是从两个版本拼接出的字节。在宣称某个下载"可续传"之前,先测试这一行为。

来源