nghttp2を使用する #2

HTTP2のライブラリ「nghttp2」を紹介するエントリです。

nghttp2のインストールは[#1](http://qiita.com/0xfffffff7/items/c8f195c9f1782ca64e92)をご覧ください。

#ベンチマークツールを使用する

h2loadコマンドでベンチマークを実施できます。
次の例は、100リクエスト、10コネクション、各コネクション最大で5つのDATAストリームを使用する設定でベンチマークを計測してます。-tでスレッド数も指定できます。

```
h2load -n100 -c10 -m5 https://nghttp2.test:8080/doc/manual/html

starting benchmark...
spawning thread #0: 10 concurrent clients, 100 total requests
Protocol: TLSv1.2
Cipher: ECDHE-RSA-AES128-GCM-SHA256
progress: 10% done
progress: 20% done
progress: 30% done
progress: 40% done
progress: 50% done
progress: 60% done
progress: 70% done
progress: 80% done
progress: 90% done
progress: 100% done

finished in 34.98ms, 2858 req/s, 150.75KB/s
requests: 100 total, 100 started, 100 done, 100 succeeded, 0 failed, 0 errored
status codes: 0 2xx, 100 3xx, 0 4xx, 0 5xx
traffic: 5400 bytes total, 4260 bytes headers, 0 bytes data
                     min         max         mean         sd        +/- sd
time for request:      173us     12.23ms      3.20ms      3.84ms    75.00%
```

**注意**
**この計測はインターネットに公開されているサーバーに対して行わないでください。**


# HPACK tools

ヘッダー圧縮をテストするツールです。
これもGitHubのREADMEに使い方が紹介されていて、その通りに使うだけです。

HTTP2ではリクエストヘッダーのセマンティクスは次のようなものです。

```
:method: GET
:scheme: https
:path: /
user-agent: nghttp2
```

これがハフマンコーディングで圧縮されるか、プレーンテキストで送信されます。

nghttp2の圧縮ツールは、JSONかHTTP2セマンティクスのヘッダーのキーバリューを入力して、その値を検証してくれます。

下記のcasesキーで始まるJSONファイルを用意します。

```
{
  "cases":
  [
    {
      "headers": [
        { ":method": "GET" },
        { ":path": "/" }
      ]
    },
    {
      "headers": [
        { ":method": "POST" },
        { ":path": "/" }
      ]
    }
  ]
}
```

標準入力から読み込ませてテストする。

```
deflatehd < testjson
```

結果が標準出力に表示されます。

```
{
  "cases":
  [
{
  "seq": 0,
  "input_length": 16,
  "output_length": 5,
  "percentage_of_original_size": 31.25,
  "wire": "3fe11f8284",
  "headers": [
    {
      ":method": "GET"
    },
    {
      ":path": "/"
    }
  ],
  "header_table_size": 4096
}
,
{
  "seq": 1,
  "input_length": 17,
  "output_length": 2,
  "percentage_of_original_size": 11.76470588235294,
  "wire": "8384",
  "headers": [
    {
      ":method": "POST"
    },
    {
      ":path": "/"
    }
  ]
}
  ]
}
Overall: input=33 output=7 ratio=0.21
```

最初のシーケンスは「:methodGET:path/」で16バイトで、圧縮後は5バイトになり、圧縮率は31.25%で、HTTP2おけるに圧縮後のバイト列は「3fe11f8284」になるという結果。

HTTP2には一度送信したヘッダー値を覚えておいて、次回からは番号だけで指定できる仕組みがあります。これを動的テーブルといいます。

-dオプションを指定すると、動的テーブルを使用したバージョンで動作し、動的テーブルの状態が続けて出力されます。

たとえば次のようなものがseqの後に出力される。

```
"header_table": {
        "entries": [
          {
            "index": 1,
            "name": "user-agent",
            "value": "nghttp2",
            "referenced": true,
            "size": 49
          },
          {
            "index": 2,
            "name": ":scheme",
            "value": "https",
            "referenced": true,
            "size": 44
          },
```


次に圧縮データの解凍を行います。

解凍はinflatehdでできます。
圧縮後のヘッダーデータを用意します。

```
{
  "cases":
  [
    { "wire": "8285" },
    { "wire": "8583" }
  ]
}
```

最初のデータ「8285」は「x82x85」を表現していて２バイトです。
これは「:method GET」と「:path /index.html」を圧縮した結果です。

なぜこれだけの長さを２バイトに圧縮できるのか？

HTTP2では、あらかじめ静的テーブルが定義されていて、よく使用するヘッダーフィルド名、および値とのペアが番号だけで指定できるようになっています。

[HTTP2の静的テーブル](http://syucream.github.io/hpack-spec-ja/header-compression-10-ja.html#static.table.definition)

この表だと「:method GET」が2で「:path /index.html」が5として定義されています。
2 :method GET
5 :path /index.html


静的テーブルや動的テーブルで番号指定でヘッダーフィールドを表現する場合は、MSB(上位4ビット)の最上位に1を立てて、下位7ビットでインデクスを表現します。


```
  0   1   2   3   4   5   6   7
+---+---+---+---+---+---+---+---+
| 1 |        Index (7+)         |
+---+---------------------------+

10000002  :method GET
10000005  :path /index.html

↓

0x82 10000002
0x85 10000005

↓

8285
```

上記の仕組みでかなり高い圧縮率を実現できる場合があります。

次にこの圧縮データを解凍してみます。

```
inflatehd < jsondata2
```

```
{
  "cases":
  [
{
  "seq": 0,
  "wire": "8285",
  "headers": [
    {
      ":method": "GET"
    },
    {
      ":path": "/index.html"
    }
  ]
}
,
{
  "seq": 1,
  "wire": "8583",
  "headers": [
    {
      ":path": "/index.html"
    },
    {
      ":method": "POST"
    }
  ]
}
  ]
}
```

正しく解凍できました。