chunk

転送符号化 chunked (HTTP)

[23] chunked は、 payload body を複数の (chunk) の連続体として表現する転送符号化です。

[24] のデータの長さはそれぞれのに指定します。そのため、 事前に payload body 全体の長さが分からなくても、 準備のできたデータから順に送信を開始することができますし、 どこまでが当該メッセージに含まれるデータの一部なのかを明確にすることができます。

[414] HTTP/1.1chunked への対応を義務付けています。 HTTP/0.9HTTP/1.0HTTP/2 では使えません。

仕様書

構文

[415] chunked 符号化されている場合、 メッセージ本体は、トレーラー部CRLF によって構成されます >>413

[419] (chunk) は、最後のだけ特別な形になります。 それより前に任意の個数のを置けます。

  1. *
  2. 最後の
  3. *
    1. ヘッダー
    2. CRLF
  4. CRLF

[416] 最後以外のは、サイズ、拡張、CRLF、データ、CRLF によって構成されます >>413

  1. +
    1. 十六進数字
  2. *
    1. 拡張 >>423
  3. CRLF
  4. *
    1. オクテット
  5. CRLF

[420] 最後のは、サイズ、拡張、CRLF によって構成されます >>413

  1. +
    1. 0
  2. *
    1. 拡張 >>423
  3. CRLF

[417] サイズは、1文字以上の十六進数字の列によって構成され、 データのオクテット数を16進数として表しています >>413。 ただし最後ののサイズは、 0 でなければいけません >>413

[421] 先導0は禁止されていません。

[418] データは、1オクテット以上のオクテット列です。

[429] 従って最後以外ののサイズは必ず 0 以外となります。

文脈

[30] chunked 転送符号化は、 chunked という値を Transfer-Encoding: ヘッダーに指定することにより、メッセージ本体に適用することができます。

[49] Transfer-Encoding: の制限のため、 HTTP/1.1 でのみ使うことができます。 また要求で利用できる場面は限られます。

[27] chunked を同じメッセージ本体に複数回適用してはなりません >>25

[28] 複数の転送符号化が適用される時は、chunked を最後に適用しなければなりません >>25

[29] chunked 以外の転送符号化を適用するときは、 接続を閉じることによってメッセージを終端するのでなければ、 最後に chunked を適用しなければなりません >>25

[32] chunked 以外の転送符号化が用いられている場合を除き、 事前にメッセージ本体の長さが分かっている時には、 chunked よりも Content-Length: を使うべきです。 実装によっては chunked に対応していても、 411 で応答することがあります。 >>31

[34] HTTP/2 では chunked を使ってはなりません >>33

[35] DATA フレームの列が、 chunked の列に相当しています。

拡張

[423] には拡張を0個以上指定できます >>413

[424] 拡張は、 ;、名前、=、値を並べたものです。 ただし = と値は省略できます。 >>413 ヘッダー内ではありませんから、 OWS は認められていません。

[425] 名前は字句です。値は字句または引用文字列です。 >>413

  1. ;
  2. 字句
  3. =
  4. |
    1. 字句
    2. 引用文字列

[426] 拡張は、署名ハッシュ値を入れたり、メッセージのサイズをランダムに変化させたり、 long polling など特別な目的で使ったりすることが想定されているようです。 >>413 しかし現時点で利用例はありません。

[36] HTTP/2chunked が禁止されたことで、 今後新たな拡張が規定されることはほとんどあり得ないでしょう。

[427] 受信者は、認識できない拡張を無視しなければなりませんは、要求中の拡張の合計長を適当と考える長さに制限し、 それを超えたら 4xx を返すべき (ought to) です。 >>413

トレーラー部

[428] トレーラー部には、ヘッダーを含めることができます。 詳しくはトレーラー部を参照してください。

構文解析

[26] HTTP/1.1 受信者は、 chunked 転送符号化構文解析して復号できなければなりません >>25

[422] HTTP/1.0 までは転送符号化は存在せず、 chunked 符号化もありませんでした。 HTTP/1.0 と分かっている相手に chunked 符号化を使用することは認められていませんし、 意図通り解釈されることは期待できません。ただし HTTP/1.0プロトコルの版とするメッセージTransfer-Encoding: ヘッダーが含まれていて chunked が指定されている時に、どう解釈するべきなのかは明確ではありません。

[412] 長さがを受信していない場合、メッセージ不完全です >>31

[15] 入力が妥当な chunked 符号化されたデータになっていないときどう処理するべきなのかは不明瞭ですが、 不完全メッセージとして扱うべきであるとの言及があります >>16

[39] FirefoxIE は、復号できるところまで復号して使うようです。 ただし IE では、復号できた部分がある程度たまらないと、エラーとして扱います。 Chrome は最後まで復号できないとエラーとして扱うようです (XHR ではネットワークエラーnavigate では復号できた量により空またはネットワークエラーとして扱うようです)。

[40] 壊れた chunk に遭遇した時、 IE はできるだけ得られたデータを利用しようとし、 Firefox はその chunk の前まででやめるようです。 しかしネットワークエラーになる場合もあるなど、細かく見ていくといろいろありそうです。

[41] FirefoxChrome は省略可能な CR とその後の LF改行とします。 IECRLF のみ改行とします。

[42] FirefoxIE十六進数の後の十六進数以外をすべて無視します。 Chrome; とそれ以降を無視しますが、 ; の前に空白以外があると、 エラーとして扱います。最後の塊については Firefox; の前にあるとエラーとして扱います。

[43] Chrometrailer 部のヘッダー部としての構文エラー (空行がないとか、 ヘッダーの後改行なしに接続が切断されたとか) をネットワークエラーとします。 IEFirefox は無視します。

[45] WindowsIEChrome は、 chunked の送信方法 (TCP セグメントの分割方法) 次第で、正しく処理できなくなることがあるようです。 例えば chunk ごとにサイズの行、データ、改行とそれぞれ別々に送信する場合は、 最初の chunk のサイズの行が 1461 バイト以上無いと、 最初のいくつかの chunk のデータを受信し、それ以後は無視し、接続が閉じられた時点で受信完了とするようです。 chunk ごとにまとめて送信するなら、このようなおかしな動作はしないようです。 TCP セグメントPSH フラグが立っていても、そうなります。 おそらく、受信した OS またはアプリケーション側のバッファリングの問題と、 EOF 受信時に未処理の受信データを捨てるためにそのような結果となるのでしょう。 なお 1460 バイトというのはちょうどイーサーネットにおける MSS です。

歴史

[1] 塊 (chunked) 符号化は、 HTTP/1.1 で導入された転送符号化です。 (転送符号化自体、 HTTP/1.1 で導入されたものです。また現時点で利用出来る唯一の転送符号化でもあります。)

[2] HTTP/1.1 は持続可能接続 (Keep-Alive) を導入しましたが、このためには内容の長さ (あるいは内容の終了の位置) を知る必要があります。 HTTP は非常に単純なプロトコルで、 HTTP/1.0 以前には終了の位置を知る方法はありませんでした。 その代わりに、接続が切れたらそこで終わりでした。 (Content-Length:欄があれば、切れた接続が正常切断だったのかを判断出来ます。)

しかし持続可能接続ではこの方法は使えません。 そこで導入されたのがこの塊転送符号化です。 HTTP/1.1 では塊転送符号化の実装が必須とされています。 (だけど実装が面倒 (というか見たらやる気を失くす。) なので、塊符号化を実装せず、それだけのために HTTP/1.1 非対応の UA も少なくはありません。)

[3] もちろん、 Content-Length:欄を必ずつけることにすればそれで長さは特定できるのですが、 CGI 出力のように長さが事前に定まらない (定まるのを待っていたら時間が掛かる可能性のある) ことがありますので不便です。

[4] 塊符号化は、符号化の内容を任意の長さの塊に分け、それぞれの塊の最初に塊の長さを書いておきます。一番最後におまけで長さ 0 と書いた塊をつけときます。これだけです。実はそんなに厄介でもありません。

[21] RFC 2616 より、 3.6.1 Chunked Transfer Coding

The chunked encoding modifies the body of a message in order to transfer it as a series of chunks, each with its own size indicator, followed by an OPTIONAL trailer containing entity-header fields. This allows dynamically produced content to be transferred along with the information necessary for the recipient to verify that it has received the full message.

chunked (塊) 符号化は、メッセージの本体を、転送のためにいじって 塊の連続とします。各塊は大きさ指示が付きますし、 最後には任意で、 trailer (おまけ) 実体頭欄を続けることが出来ます。 これにより、動的に生成した内容が、受信者が完全なメッセージを受け取ったか確認するのに必要な情報と共に転送出来るようになります。

       Chunked-Body   = *chunk
                        last-chunk
                        trailer
                        CRLF
       chunk          = chunk-size [ chunk-extension ] CRLF
                        chunk-data CRLF
       chunk-size     = 1*HEX
       last-chunk     = 1*("0") [ chunk-extension ] CRLF
       chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
       chunk-ext-name = token
       chunk-ext-val  = token | quoted-string
       chunk-data     = chunk-size(OCTET)
       trailer        = *(entity-header CRLF)

The chunk-size field is a string of hex digits indicating the size of the chunk-data in octets. The chunked encoding is ended by any chunk whose size is zero, followed by the trailer, which is terminated by an empty line.

chunk-size (塊長) 欄は、 chunk-data の長さをオクテット単位で表す 16進数字の文字列です。 chunked (塊)符号化は、長さ 0 の chunk (塊)、それに続く, 空行で終わる trailer (おまけ)で終わります。

訳注: 上記段落での追加部分は Errata (>>8) から補った。

The trailer allows the sender to include additional HTTP header fields at the end of the message. The Trailer header field can be used to indicate which header fields are included in a trailer (see section 14.40).

trailer により、送信者はメッセージの終わりに追加の HTTP 頭領域を 入れることが出来ます。 Trailer 頭領域は trailer に含まれている 頭領域を示すのに使うことが出来ます。 (14.40節参照)

A server using chunked transfer-coding in a response MUST NOT use the trailer for any header fields unless at least one of the following is true:

応答に chunked 転送符号化を使うサーバーは 次の条件の一つ以上を満たさずに trailer を使ってはいけません

a)the request included a TE header field that indicates "trailers" is acceptable in the transfer-coding of the response, as described in section 14.39; or,

a) 14.39節で説明するように、要求が、 「trailers」を応答の転送符号化で 使っても良いと示す TE 頭領域を含んでいる場合

b)the server is the origin server for the response, the trailer fields consist entirely of optional metadata, and the recipient could use the message (in a manner acceptable to the origin server) without receiving this metadata. In other words, the origin server is willing to accept the possibility that the trailer fields might be silently discarded along the path to the client.

b) サーバーが、応答の原サーバーで、 trailer 領域が 完全に任意のメタ情報であって、受信者がそのメタ情報を受信せずとも (原サーバー的に認められる方法で) メッセージを使うことが出来る場合。 別の言い方をすると、原サーバーが、 trailer 領域がクライアントへの 経路中に黙って捨てられる可能性があることを認める意思があるという場合

This requirement prevents an interoperability failure when the message is being received by an HTTP/1.1 (or later) proxy and forwarded to an HTTP/1.0 recipient. It avoids a situation where compliance with the protocol would have necessitated a possibly infinite buffer on the proxy.

この要求事項は、メッセージが HTTP/1.1 (以上) の串が受信して HTTP/1.0 受信者に転送される時に、相互通信性的に失敗するのを 防ぐものです。これにより、このプロトコルに従うことが、 串で不定バッファーが必要になるかもしれない状況を避けることが出来ます。

An example process for decoding a Chunked-Body is presented in appendix 19.4.6.

塊本体の復号処理の例を附属書 19.4.6 に示しました。

All HTTP/1.1 applications MUST be able to receive and decode the "chunked" transfer-coding, and MUST ignore chunk-extension extensions they do not understand.

全ての HTTP/1.1 応用は「chunked」転送符号化を受信して復号する ことが出来なければならず、理解できない chunk-extension (塊拡張) を無視しなければなりません

RFC 2068・2616 (HTTP/1.1) 19.4.4; Introduction of Transfer-Encoding

HTTP/1.1 introduces the Transfer-Encoding header field (section 14.40 14.41). Proxies/gateways MUST remove any transfer-coding prior to forwarding a message via a MIME-compliant protocol.

[17] HTTP/1.1 は Transfer-Encoding 頭欄を導入しました。 串・関門は MIME に従うプロトコルにメッセージを転送する前に transfer-coding を解かなければなりません

A process for decoding the "chunked" transfer-coding (section 3.6) can be represented in pseudo-code as:

[18] "chunked" transfer-coding の復号過程は擬似 code で次のように表せます。

length := 0
read chunk-size, chunk-extension (if any) and CRLF
while (chunk-size > 0) {
   read chunk-data and CRLF
   append chunk-data to entity-body
   length := length + chunk-size
   read chunk-size and CRLF
}
read entity-header
while (entity-header not empty) {
   append entity-header to existing header fields
   read entity-header
}
Content-Length := length
Remove "chunked" from Transfer-Encoding
       長さ := 0
       chunk-size と chunk-extension (あれば) と CRLF を読む
       (chunk-size > 0) の間 {
          chunk-data と CRLF を読む
          chunk-data を entity-body に付加する
          長さ := 長さ + chunk-size
          chunk-size と CRLF を読む
       }
       entity-header を読む
       (entity-header が空でない) の間 {
          entity-header を既存の頭欄に付加
          entity-header を読む
       }
       Content-Length := 長さ
       "chunked" を Transfer-Encoding から削除

[5] この塊符号化に関する部分の実装の不具合が問題になったこともありました。 (Apache などの古い版を使っている人は新しい版にしましょう。)

実装

[431] chunked-extension に対応していなかった実装は他にもありました >>430

[22] IE9 Beta Minor Changes List - EricLaw's IEInternals - Site Home - MSDN Blogs ( 版) <http://blogs.msdn.com/b/ieinternals/archive/2010/09/15/ie9-beta-minor-change-list.aspx>

関連

[6] HTTP と似た仕組みを使う SIP では、この塊符号化の使用は禁止されています。

[7] >>6 RTSP も禁止。 chunked は嫌われ者だねぇ

メモ

[19] TAKESAKO @ Yet another Cybozu Labs: ニコニコ動画勉強会に行ってきました ( 版) <http://labs.cybozu.co.jp/blog/takesako/2007/04/nicovideo.html>

[20] Chunked + Gzip | Apache | Users (Referenced: ) <http://www.gossamer-threads.com/lists/apache/users/356732>

[432] draft-maes-lemonade-http-binding-04 - IMAP and SMTP HTTP Binding ( ( 版)) <http://tools.ietf.org/html/draft-maes-lemonade-http-binding-04#section-2.1.2>

[433] draft-maes-lemonade-tcp-challenged-environments-01 - Lemonade in TCP Challenged Environments ( ( 版)) <https://tools.ietf.org/html/draft-maes-lemonade-tcp-challenged-environments-01#page-7>

[434] draft-maes-lemonade-p-imap-12 - Push Extensions to the IMAP Protocol (P-IMAP) ( ( 版)) <http://tools.ietf.org/html/draft-maes-lemonade-p-imap-12#page-47>

[436] RFC 3507 - Internet Content Adaptation Protocol (ICAP) ( ( 版)) <http://tools.ietf.org/html/rfc3507#section-4.4>

[437] RFC 4387 - Internet X.509 Public Key Infrastructure Operational Protocols: Certificate Store Access via HTTP ( ( 版)) <http://tools.ietf.org/html/rfc4387#section-2.5.4>

[37] chunking without chunking (Adrien de Croy 著, 版) <https://lists.w3.org/Archives/Public/ietf-http-wg/2009AprJun/0717.html>

[38] How to enable chunked transfer encoding with IIS ( 版) <https://support.microsoft.com/en-us/kb/278998>

[44] JVNDB-2012-005998 - JVN iPedia - 脆弱性対策情報データベース ( 版) <http://jvndb.jvn.jp/ja/contents/2012/JVNDB-2012-005998.html>

Apache Tomcat は、チャンク転送コーディング (chunked transfer coding) のチャンク拡張 (chunk extension) を適切に処理しないため、サービス運用妨害 (DoS) 状態にされる脆弱性が存在します。

[46] Stream-based requests (Request with ReadableStream) (yutakahirano著, ) <https://github.com/whatwg/fetch/commit/0c470b5860fe690b1136b0242951f682405103cc>

[47] FFmpeg Protocols Documentation () <https://ffmpeg.org/ffmpeg-protocols.html#http>

chunked_post

If set to 1 use chunked Transfer-Encoding for posts, default is 1.

[48] DocuSign REST API Guide - Chunked Transfer-Encoding Support () <https://www.docusign.com/p/RESTAPIGuide/RESTAPIGuide.htm#Chunked Transfer-Encoding Support/Chunked Transfer-Encoding Support.htm%3FTocPath%3D_____8>

[50] KeepAlive On な Apache+mod_php で HTTP/1.0 クライアントに HTTP/1.1 を返すとタイムアウトを待ってしまう - ngyukiの日記 ( 版) <http://ngyuki.hatenablog.com/entry/2017/08/02/081959> には、 HTTP/1.0要求を送信しているのに HTTP/1.1Transfer-Encoding: chunked応答が返される事例が紹介されています。 Apache 単体では再現できなかったので、 PHP の挙動でしょうか。

[51] ストリーミング転送  |  Cloud Storage ドキュメント  |  Google Cloud Platform () <https://cloud.google.com/storage/docs/streaming?hl=ja>

Google Cloud Storage では、HTTP チャンク転送エンコーディングを使用して gsutil ツールまたは boto ライブラリでのストリーミング転送をサポートしています。

[52] Chunked upload Cronet API. - Google グループ () <https://groups.google.com/a/chromium.org/forum/#!topic/net-dev/yvmWG4hexBE>