KDOC 97: Partial Contentの使いどころ

この文書のステータス

  • 作成
    • 2024-02-17 貴島
  • レビュー
    • 2024-02-25 貴島

概要

CDNのリクエストログを見ていて、HTTPステータスが 200 に混じって 206 があった。これが何かを教えてもらったのでメモする。

イントロ

ドキュメントを見る。

206 Partial Content - HTTP | MDN

HTTP 206 Partial Content は成功ステータスレスポンスコードで、そのリクエストが成功したこと、そしてリクエストの Range ヘッダーに記述された通り、要求された範囲のデータが本文に含まれていることを示します。

このユースケースはどういうときか。

  • URLが画像だった場合にプレビュー画像を表示したいとき

    Slackなんかにある機能。先にそのURLへアクセスして、一部だけ取得する(画像の場合バイナリ)。一部取得したファイルを、ファイルヘッダをチェックして判定する。プレビューできるとなったら、もう一度リクエストしてファイル全体を取得して、プレビュー画像としてセットする。1回めのチェックリクエストの段階ですべて取得する意味はないので、ファイルの一部だけを取得してくる。

  • 動画再生

    途中から再生しているのに、動画ファイルの最初からダウンロードを開始するのは意味がない。ファイルのある範囲を指定することで、一部だけダウンロードしている。再生位置から20秒間分ダウンロードする、みたいな感じでダウンロードを断続的に走らせることでリソースの無駄をなくし、どこからでも再生できる。

検証

実際の動きをcurlで確かめる。リンク先のバイナリのファイルヘッダを確認するシチュエーション。対応してないサーバでやってもRange指定は無視されるだけなので、Wikipediaのメディアサーバで確かめる。

まず、範囲指定しない場合。画像ファイル全体をダウンロードする。種別を確認したいだけなのにもったいない。

tmpfile=$(mktemp)
result=`curl -v --output $tmpfile https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Wikipedia-logo-v2.svg/100px-Wikipedia-logo-v2.svg.png`
file -b --mime-type $tmpfile
rm $tmpfile
image/png

↓このときのリクエスト+レスポンスはこうなる。

> GET /wikipedia/commons/thumb/8/80/Wikipedia-logo-v2.svg/100px-Wikipedia-logo-v2.svg.png HTTP/2
> Host: upload.wikimedia.org
> User-Agent: curl/8.4.0
> Accept: */*
>

< HTTP/2 200 👈
< date: Sat, 17 Feb 2024 02:23:17 GMT
< etag: c4e5e81aa0475e2f1dae37acfadacd6c
< server: ATS/9.1.4
< content-type: image/png
< content-disposition: inline;filename*=UTF-8''Wikipedia-logo-v2.svg.png
< last-modified: Sun, 10 Sep 2023 12:27:10 GMT
< content-length: 14729 👈
< age: 16405
< x-cache: cp5032 miss, cp5032 hit/117
< x-cache-status: hit-front
< server-timing: cache;desc="hit-front", host;desc="cp5032"
< strict-transport-security: max-age=106384710; includeSubDomains; preload
< report-to: { "group": "wm_nel", "max_age": 604800, "endpoints": [{ "url": "https://intake-logging.wikimedia.org/v1/events?stream=w3c.reportingapi.network_error&schema_uri=/w3c/reportingapi/network_error/1.0.0" }] }
< nel: { "report_to": "wm_nel", "max_age": 604800, "failure_fraction": 0.05, "success_fraction": 0.0}
< x-client-ip: 240b:10:91c1:d500:877b:f95b:ff57:d2fc
< x-content-type-options: nosniff
< access-control-allow-origin: *
< access-control-expose-headers: Age, Date, Content-Length, Content-Range, X-Content-Duration, X-Cache
< timing-allow-origin: *
< accept-ranges: bytes
<
  • HTTPステータス200を返している
  • content-lengthは1万を超えている

次に、Rangeを指定して結果を比較する。一部だけ取得するので無駄な資源を使わず環境に優しい。curlでは r オプションを使って指定する。

tmpfile=$(mktemp)
result=`curl -v --output $tmpfile -r 0-100 https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Wikipedia-logo-v2.svg/100px-Wikipedia-logo-v2.svg.png`
file -b --mime-type $tmpfile
rm $tmpfile
image/png

↑ファイルタイプはファイルヘッダーにあるので、バイナリが一部だけでも判定できるのを確認できた。

↓このときのリクエスト+レスポンスはこうなる。

> GET /wikipedia/commons/thumb/8/80/Wikipedia-logo-v2.svg/100px-Wikipedia-logo-v2.svg.png HTTP/2
> Host: upload.wikimedia.org
> Range: bytes=0-100 👈
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/2 206 👈
< date: Sat, 17 Feb 2024 02:23:17 GMT
< etag: c4e5e81aa0475e2f1dae37acfadacd6c
< server: ATS/9.1.4
< content-type: image/png
< content-disposition: inline;filename*=UTF-8''Wikipedia-logo-v2.svg.png
< last-modified: Sun, 10 Sep 2023 12:27:10 GMT
< age: 18113
< x-cache: cp5032 miss, cp5032 hit/128
< x-cache-status: hit-front
< server-timing: cache;desc="hit-front", host;desc="cp5032"
< strict-transport-security: max-age=106384710; includeSubDomains; preload
< report-to: { "group": "wm_nel", "max_age": 604800, "endpoints": [{ "url": "https://intake-logging.wikimedia.org/v1/events?stream=w3c.reportingapi.network_error&schema_uri=/w3c/reportingapi/network_error/1.0.0" }] }
< nel: { "report_to": "wm_nel", "max_age": 604800, "failure_fraction": 0.05, "success_fraction": 0.0}
< x-client-ip: 240b:10:91c1:d500:877b:f95b:ff57:d2fc
< x-content-type-options: nosniff
< access-control-allow-origin: *
< access-control-expose-headers: Age, Date, Content-Length, Content-Range, X-Content-Duration, X-Cache
< timing-allow-origin: *
< accept-ranges: bytes
< content-range: bytes 0-100/14729
< content-length: 101 👈
<
  • リクエストヘッダーにRangeヘッダーが追加された
  • HTTPステータス206を返している
  • content-lengthが指定したサイズになっている

ということで、リソースの一部だけ必要なときは206を使う。

関連