テクノロジー · 実用ガイド

curl の再開でファイルが破損?すべての範囲を強い ETag に結び付ける

バイトオフセットは表現の識別子ではありません。Range と If-Range を使い、正しい 206 だけを追記し、200 ならゼロから置換して検証します。

curl の再開でファイルが破損?すべての範囲を強い ETag に結び付ける
オリジナルイラスト: 200 と 206 を安全な書き込みへ分岐します。 Watch at Ease

安全な再開では、追記する全バイトが同じ HTTP 表現に属する必要があります。curl -C - が使うローカル長はオフセットであり、内容の識別子ではありません。

部分ファイルを最終 URL と強い ETag に結び付け、Range と If-Range で残りを要求します。206 なら検証して追記し、200 なら新規ファイルとして置換し、最後に原子的に昇格します。

1. ローカル長を識別子ではなくオフセットとして扱う

curl -C - は出力長を調べて後続バイトを要求しますが、リモートが同じ版かは証明しません。最終 URL、強い ETag、ローカル長、期待総長、配布元ダイジェストを一つの出力に結び付けたサイドカーへ保存します。

専用 .part を使い、複数 URL で一つの ETag ファイルを共有しません。

2. 再開前に強い検証子を要求する

Range: bytes=N- と保存済み強 ETag の If-Range を送ります。RFC 9110 は弱い ETag を If-Range に使うことを禁じます。強い検証子がなければ新規ファイルへゼロから取得します。

--etag-compare は If-None-Match であり If-Range ではありません。ラッパーは If-Range と応答ヘッダーを明示的に扱います。

3. 応答から書き込み方式を決める

206 で Content-Range が N から始まり、総長と検証子が整合する場合だけ追記します。応答は一度 tail 一時ファイルへ置き、ヘッダー検証後に結合します。

If-Range 不一致の 200 は完全応答なので新しい一時ファイルへ置換します。追記してはいけません。416 では長さ、総長、検証子を再確認します。

4. 転送障害だけを再試行する

一時的な転送障害だけを上限とバックオフ付きで再試行します。認証失敗、リダイレクト変更、検証子欠落、異常状態、Content-Range 不整合、ダイジェスト不一致は停止またはクリーン再開です。

curl 8.5.0 実験では無検証の変更版が 206 で混合ファイルを作り、古い ETag の If-Range は 200 で安全に全置換し、同じ版では 206 の結合後ダイジェストが一致しました。

5. 検証してから原子的に昇格する

完了後は配布元ダイジェストを優先し、なければ総長と同じ強い ETag を確認しつつ長さだけでは完全性を証明しないと明記します。形式固有の検査も実行します。

受け入れ後だけ同一ファイルシステムで原子的に改名します。サイドカーとログを残し、混合または未検証の部分ファイルは隔離します。

バイトオフセットは表現の識別子ではありません。Range と If-Range を使い、正しい 206 だけを追記し、200 ならゼロから置換して検証します。
オリジナルイラスト: リモート識別子、部分ファイル、受け入れ証拠を結び付けます。 Watch at Ease