Chunked Transferとは

一般にHTTP KeepAliveを利用するには、レスポンスのボディがどこで終わり、次のレスポンスがどこから始まるかをクライアントが知る必要があります、そのためHTTP/1.0ではKeepAliveを行う為にボディの長さをContent-Lengthをヘッダに入れなければなりませんでしたが、サイズを測るためにデータをすべてメモリに読み込むなどの処理が必要になり、レスポンス開始までの時間もかかります。(一般的なアプリケーションにはあまり影響がありませんが)

そこでHTTP/1.1ではChunked Transferという仕組みが入っていて、事前に全体のレスポンスの長さが分からなくても、chunk=固まり毎にサイズを記してレスポンスを返していき、最後に0byteと送信することで、コンテンツの切れ目がわかるようになっています。

HTTP/1.1 200 OK
Date: Fri, 24 May 2013 05:39:21 GMT
Server: Plack::Handler::Monoceros
Content-Type: text/plain
Transfer-Encoding: chunked

78
aaaaa......aaaaa
1e
bbb....bbbbbbb
9
ccccccccc
0

上の「78」「1e」「9」「0」がChunkのサイズになります。16進数で書かれていて、それぞれ「120byte」「30byte」「9byte」「0byte」となってます

MonocerosもContent-Lengthが指定されていない場合、Chunked Transferを使う事でKeepAlive通信ができるようになっています。ちなみに、アプリケーションからのレスポンスボディを複数の要素が入っている配列にするとそれぞれ別のchunkとして送り出します。Starmanも同様です。

sub {
    [200, ['Content-Type'=>'text/plain'],[
        'a'x120,
        'b'x30,
        'c'x9
    ]]
}

Chunkのビジュアライズ

前置きはこれくらいにして、実際のサーバからのレスポンスがどのように分割されて送られて来ているのか調べるツールを書いてみました。

github https://github.com/kazeburo/chunkview

中身はだいぶFurlからのコピペです。

使い方

 $ git clone https://github.com/kazeburo/chunkview
 $ cd chunkview
 $ carton install
 $ carton exec -- ./chunkview.pl [url]

上のMonocerosサーバに向けて実行すると

$ carton exec -- ./chunkview.pl http://localhost:5000/
* Chunk View
** Headers
 transfer-encoding: chunked
 content-encoding: 
 content-length: 
 server: Plack::Handler::Monoceros
** chunk table
.------------------------------------------.
| chunk size | byte | content              |
+------------+------+----------------------+
|         78 |  120 | aaaaaaaaaaaaaaaaaaaa |
|         1e |   30 | bbbbbbbbbbbbbbbbbbbb |
|          9 |    9 | ccccccccc            |
|          0 |    0 |                      |
'------------+------+----------------------'

と取れます。ふむふむ。

調子乗って他のサーバにも実行してみます。まずYahoo!

$ ./chunkview.pl  http://www.yahoo.co.jp/
* Chunk View
** Headers
 transfer-encoding: chunked
 content-encoding: gzip
 content-length: 
 server: 
** chunk table
.---------------------------------------------------.
| chunk size | byte  | content                      |
+------------+-------+------------------------------+
|    00084c9 | 33993 | <!DOCTYPE HTML PUBLI(128637) |
|          0 |     0 |                              |
'------------+-------+------------------------------'

gzipが使われた上で、1つのchunkに収まっています。contentのカッコ内は圧縮展開後のサイズです

次、はてなブックマーク

$ ./chunkview.pl  http://b.hatena.ne.jp/         
* Chunk View
** Headers
 transfer-encoding: chunked
 content-encoding: gzip
 content-length: 
 server: nginx/0.8.52
** chunk table
.------------------------------------------------------------------.
| chunk size | byte  | content                                     |
+------------+-------+---------------------------------------------+
|        56f |  1391 | <!DOCTYPE html>%0A<htm(3533)                |
|        47e |  1150 |  + '?&login_date=' +(2896)                  |
|        6b7 |  1719 |  name="q" type="text(5792)                  |
|        223 |   547 |           </script>%0A(2896)                |
|        9be |  2494 | %E7%94%98%E3%81%84%E7.....%9D%E3%81(14480)  |
|        7cd |  1997 | ass="date">2013/05/2(11584)                 |
|       4256 | 16982 | %81%8C%E5%87%BA%E3%82%8B%..E3%81%AB(121682) |
|          0 |     0 |                                             |
'------------+-------+---------------------------------------------'

細かく分かれて転送されてきますね。

次、livedoorblog

$ ./chunkview.pl  http://blog.livedoor.jp/staff/
* Chunk View
** Headers
 transfer-encoding: chunked
 content-encoding: gzip
 content-length: 
 server: Plack::Handler::Starlet
** chunk table
.-------------------------------------------------.
| chunk size | byte | content                     |
+------------+------+-----------------------------+
|         10 |   16 | (0)                         |
|       20cd | 8397 | <!DOCTYPE html PUBLI(33005) |
|        26a |  618 | <div class="plugin-r(2896)  |
|        706 | 1798 | f/archives/51801785.(8235)  |
|          0 |    0 |                             |
'------------+------+-----------------------------'

なにやら最初の16byteがあやしいです。gzip展開すると0byteです。

実はここにはRFC 1952で仕様化されてるgzipのヘッダだけが収まっています。なんとなく無駄っぽい動作。Naverまとめだと

./chunkview.pl  http://matome.naver.jp/       
* Chunk View
** Headers
 transfer-encoding: chunked
 content-encoding: gzip
 content-length: 
 server: Apache
** chunk table
.----------------------------------------------------------------.
| chunk size | byte | content                                    |
+------------+------+--------------------------------------------+
|         10 |   16 | (0)                                        |
|        b60 | 2912 | <!DOCTYPE html>%0D%0A<ht(8184)             |
           <   略   >
|        4dc | 1244 | "NL:matomeimage" ><i(8184)                 |
|        3d7 |  983 | %0D%0A<p class="mdSubMTM(4231)             |
|          a |   10 | (0)                                        |
|          0 |    0 |                                            |
'------------+------+--------------------------------------------'

最後のフッタまで別chunkになってる。Apacheかその下のアプリケーションサーバの仕様なんですかね。

まとめとアップデートのお知らせ

ちなみにこの 16byteのchunkはPlack::Middleware::Deflaterでも生成されることが分かったのでバッファして次のchunkと一緒書き出されるようにして先ほどリリースしました。

どうぞご利用ください

https://metacpan.org/release/Plack-Middleware-Deflater

このブログ記事について

このページは、Masahiro Naganoが2013年5月24日 15:26に書いたブログ記事です。

ひとつ前のブログ記事は「Monoceros が HTTP/1.1に対応しました & nginx と組み合わせたベンチマーク」です。

次のブログ記事は「Monoceros 運用系の機能追加のお知らせ。最大保持コネクション数とステータス表示ミドルウェアが追加されました」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 4.27-ja