LoginSignup
1
3

More than 5 years have passed since last update.

WEBRick で HTTP streaming (Chunked transfer encoding)

Last updated at Posted at 2017-06-22

HTTP Streamingとは

Chunked transfer encodingとも呼ばれます。
HTTP/1.1の4.1. Chunked Transfer Codingで定義されています。
HTTPレスポンスを分割して返却します。

HTTP/1.1 の Transfer-Encoding: chunked をビジュアライズするツール書いてみた - blog.nomadscafe.jp

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

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

主な用途

レスポンスヘッダとレスポンスボディを分けて返す

ブラウザはヘッダで指定されたasset(css, javascript, 画像...)とボディを並列にダウンロードできます。
ボディのダウンロード完了を待たない分、ページの表示が早くなります。

Why HTTP Streaming? | Riding Rails

ボディを分割して返す

複数のバックエンドサーバから検索してレスポンスを返す場合など
ボディの取得に時間がかかるときに、サーバは取得した部分からボディを返却できます。
ブラウザは取得できたボディからページに表示することができます。
レスポンスが向上します。

ボディが巨大な場合に、サーバはボディを分割して返却できます。
サーバはメモリに、ボディの返却する部分のみを読み込みます。
ボディすべてをメモリに読み込む必要がなくなり、必要なメモリ容量が減ります。

WEBRickで試す

Rubyに標準添付のHTTPサーバフレームワークwebrickで試してみましょう。

WEBRickを使った簡易サーバ

単に文字列を返却するサーバを用意します。

hello_webrick.rb
require 'webrick'

srv = WEBrick::HTTPServer.new({ Port: 8080 })

srv.mount_proc '/' do |req, res|
  res.body = 'hello'
end

srv.start

ruby hello_webrick.rbで実行します。

curl localhost:8080 -i
HTTP/1.1 200 OK
Server: WEBrick/1.3.1 (Ruby/2.4.1/2017-03-22)
Date: Thu, 22 Jun 2017 00:45:40 GMT
Content-Length: 5
Connection: Keep-Alive

hello

HTTP streamingを有効に

WEBrick::HTTPResponse#chunked=

真に設定するとクライアントに返す内容(エンティティボディ)を chunk に分けるようになります。

hello_webrick.rb
require 'webrick'

srv = WEBrick::HTTPServer.new({ Port: 8080 })

srv.mount_proc '/' do |req, res|
  res.chunked = true
  res.body = 'hello'
end

srv.start

ruby hello_webrick.rbで実行します。

curl localhost:8080 -i
HTTP/1.1 200 OK
Server: WEBrick/1.3.1 (Ruby/2.4.1/2017-03-22)
Date: Thu, 22 Jun 2017 00:50:44 GMT
Transfer-Encoding: chunked
Connection: Keep-Alive

hello

HTTPヘッダーがContent-Length: 5からTransfer-Encoding: chunkedに変わりました。

送受信パケットの変化

MacOSではtcpdumpコマンドで実際に送受信しているパケットを見ることができます。
sudo tcpdump -i lo0 port 8080 -X -t の実行結果を抜粋します。

リクエスト

HTTPリクエスト HTTP: GET / HTTP/1.1 が飛んでいます。

IP6 localhost.55855 > localhost.http-alt: Flags [P.], seq 1:79, ack 1, win 12743, options [nop,nop,TS val 341903535 ecr 341903535], length 78: HTTP: GET / HTTP/1.1
    0x0000:  6003 672d 006e 0640 0000 0000 0000 0000  `.g-.n.@........
    0x0010:  0000 0000 0000 0001 0000 0000 0000 0000  ................
    0x0020:  0000 0000 0000 0001 da2f 1f90 1ab5 823c  ........./.....<
    0x0030:  2389 6a6e 8018 31c7 0076 0000 0101 080a  #.jn..1..v......
    0x0040:  1461 08af 1461 08af 4745 5420 2f20 4854  .a...a..GET./.HT
    0x0050:  5450 2f31 2e31 0d0a 486f 7374 3a20 6c6f  TP/1.1..Host:.lo
    0x0060:  6361 6c68 6f73 743a 3830 3830 0d0a 5573  calhost:8080..Us
    0x0070:  6572 2d41 6765 6e74 3a20 6375 726c 2f37  er-Agent:.curl/7
    0x0080:  2e34 332e 300d 0a41 6363 6570 743a 202a  .43.0..Accept:.*
    0x0090:  2f2a 0d0a 0d0a                           /*....

リクエストには変化はありません。

HTTP streamingなしレスポンス

HTTPヘッダーが帰ってきます。

IP6 localhost.http-alt > localhost.55855: Flags [P.], seq 1:148, ack 79, win 12741, options [nop,nop,TS val 341903535 ecr 341903535], length 147: HTTP: HTTP/1.1 200 OK
    0x0000:  6002 29cb 00b3 0640 0000 0000 0000 0000  `.)....@........
    0x0010:  0000 0000 0000 0001 0000 0000 0000 0000  ................
    0x0020:  0000 0000 0000 0001 1f90 da2f 2389 6a6e  .........../#.jn
    0x0030:  1ab5 828a 8018 31c5 00bb 0000 0101 080a  ......1.........
    0x0040:  1461 08af 1461 08af 4854 5450 2f31 2e31  .a...a..HTTP/1.1
    0x0050:  2032 3030 204f 4b20 0d0a 5365 7276 6572  .200.OK...Server
    0x0060:  3a20 5745 4272 6963 6b2f 312e 332e 3120  :.WEBrick/1.3.1.
    0x0070:  2852 7562 792f 322e 342e 312f 3230 3137  (Ruby/2.4.1/2017
    0x0080:  2d30 332d 3232 290d 0a44 6174 653a 2054  -03-22)..Date:.T
    0x0090:  6875 2c20 3232 204a 756e 2032 3031 3720  hu,.22.Jun.2017.
    0x00a0:  3031 3a34 313a 3137 2047 4d54 0d0a 436f  01:41:17.GMT..Co
    0x00b0:  6e74 656e 742d 4c65 6e67 7468 3a20 350d  ntent-Length:.5.
    0x00c0:  0a43 6f6e 6e65 6374 696f 6e3a 204b 6565  .Connection:.Kee
    0x00d0:  702d 416c 6976 650d 0a0d 0a              p-Alive....

その後ボディが帰ってきます。

IP6 localhost.http-alt > localhost.55855: Flags [P.], seq 148:153, ack 79, win 12741, options [nop,nop,TS val 341903536 ecr 341903535], length 5: HTTP
    0x0000:  6002 29cb 0025 0640 0000 0000 0000 0000  `.)..%.@........
    0x0010:  0000 0000 0000 0001 0000 0000 0000 0000  ................
    0x0020:  0000 0000 0000 0001 1f90 da2f 2389 6b01  .........../#.k.
    0x0030:  1ab5 828a 8018 31c5 002d 0000 0101 080a  ......1..-......
    0x0040:  1461 08b0 1461 08af 6865 6c6c 6f         .a...a..hello

HTTP streamingありレスポンス

ヘッダーが帰ってきます。

IP6 localhost.http-alt > localhost.55880: Flags [P.], seq 1:157, ack 79, win 12741, options [nop,nop,TS val 342094266 ecr 342094262], length 156: HTTP: HTTP/1.1 200 OK
    0x0000:  600f 56d5 00bc 0640 0000 0000 0000 0000  `.V....@........
    0x0010:  0000 0000 0000 0001 0000 0000 0000 0000  ................
    0x0020:  0000 0000 0000 0001 1f90 da48 7621 f890  ...........Hv!..
    0x0030:  5105 966f 8018 31c5 00c4 0000 0101 080a  Q..o..1.........
    0x0040:  1463 f1ba 1463 f1b6 4854 5450 2f31 2e31  .c...c..HTTP/1.1
    0x0050:  2032 3030 204f 4b20 0d0a 5365 7276 6572  .200.OK...Server
    0x0060:  3a20 5745 4272 6963 6b2f 312e 332e 3120  :.WEBrick/1.3.1.
    0x0070:  2852 7562 792f 322e 342e 312f 3230 3137  (Ruby/2.4.1/2017
    0x0080:  2d30 332d 3232 290d 0a44 6174 653a 2054  -03-22)..Date:.T
    0x0090:  6875 2c20 3232 204a 756e 2032 3031 3720  hu,.22.Jun.2017.
    0x00a0:  3031 3a34 343a 3238 2047 4d54 0d0a 5472  01:44:28.GMT..Tr
    0x00b0:  616e 7366 6572 2d45 6e63 6f64 696e 673a  ansfer-Encoding:
    0x00c0:  2063 6875 6e6b 6564 0d0a 436f 6e6e 6563  .chunked..Connec
    0x00d0:  7469 6f6e 3a20 4b65 6570 2d41 6c69 7665  tion:.Keep-Alive
    0x00e0:  0d0a 0d0a                                ....

続いてボディが1つ帰ってきます。

IP6 localhost.http-alt > localhost.55880: Flags [P.], seq 157:167, ack 79, win 12741, options [nop,nop,TS val 342094266 ecr 342094266], length 10: HTTP
    0x0000:  600f 56d5 002a 0640 0000 0000 0000 0000  `.V..*.@........
    0x0010:  0000 0000 0000 0001 0000 0000 0000 0000  ................
    0x0020:  0000 0000 0000 0001 1f90 da48 7621 f92c  ...........Hv!.,
    0x0030:  5105 966f 8018 31c5 0032 0000 0101 080a  Q..o..1..2......
    0x0040:  1463 f1ba 1463 f1ba 350d 0a68 656c 6c6f  .c...c..5..hello
    0x0050:  0d0a

50d 0a68 656c 6c6f 0d0aがHTTPボディです。
これは

5

hello

です。HTTP Streamingでは

送信サイズ
改行
送信データ
改行

の形式でチャンクを送信します。
つづいて、もう1つボディが帰ってきます。

IP6 localhost.http-alt > localhost.55880: Flags [P.], seq 167:172, ack 79, win 12741, options [nop,nop,TS val 342094266 ecr 342094266], length 5: HTTP
    0x0000:  600f 56d5 0025 0640 0000 0000 0000 0000  `.V..%.@........
    0x0010:  0000 0000 0000 0001 0000 0000 0000 0000  ................
    0x0020:  0000 0000 0000 0001 1f90 da48 7621 f936  ...........Hv!.6
    0x0030:  5105 966f 8018 31c5 002d 0000 0101 080a  Q..o..1..-......
    0x0040:  1463 f1ba 1463 f1ba 300d 0a0d 0a         .c...c..0....

HTTPボディは0a0d 0aです。

これは

0

です。HTTP Streamingでは送信終了の印に、0と改行を送ります。

https://tools.ietf.org/html/rfc7230#section-4.1

chunked-body   = *chunk
                 last-chunk
                 trailer-part
                 CRLF

chunk          = chunk-size [ chunk-ext ] CRLF
                 chunk-data CRLF
chunk-size     = 1*HEXDIG
last-chunk     = 1*("0") [ chunk-ext ] CRLF

chunk-data     = 1*OCTET ; a sequence of chunk-size octets

定義されている通りです。

参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3