nghttpd で TLS なしの HTTP/2(h2c)を利用する
nghttpd に --no-tls
を指定すると TLS なしの h2c (HTTP/2 cleartext) を利用できる
/usr/sbin/nghttpd 8000 --no-tls -v -d ./web
この状態での nghttpd は HTTP/1 をサポートしないので、コネクションプリフェイス (PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
) を送信できる HTTP クライアントが必要になる。curl の場合、--http2-prior-knowledge
オプションを指定する
curl -v --http2-prior-knowledge http://localhost:8000
* Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
* h2h3 [:method: GET]
* h2h3 [:path: /]
* h2h3 [:scheme: http]
* h2h3 [:authority: localhost:8000]
* h2h3 [user-agent: curl/7.88.1]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0x55a9bf4ddce0)
> GET / HTTP/2
> Host: localhost:8000
> user-agent: curl/7.88.1
> accept: */*
>
< HTTP/2 200
< server: nghttpd nghttp2/1.52.0
< cache-control: max-age=3600
< date: Wed, 10 Jul 2024 04:09:08 GMT
< content-length: 6
< last-modified: Wed, 10 Jul 2024 03:26:27 GMT
< content-type: text/html
<
hello
* Connection #0 to host localhost left intact
HTTP/1.1 GET リクエストを送信すると次のようなレスポンスが返される
curl -v --http1.1 http://localhost:8000
* Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.88.1
> Accept: */*
>
* Received HTTP/0.9 when not allowed
* Closing connection 0
curl: (1) Received HTTP/0.9 when not allowed
返されるレスポンスはバイナリデータの HTTP/2 フレームであり、冒頭の行には HTTP のバージョンが含まれないため、HTTP/0.9 と誤解されている
nghttpd のログを見ると最初に次のような Settings フレームが送信されている。Settings フレームはクライアントとサーバーがデータのやりとりをするための取り決めである。
[id=9] [256.282] send SETTINGS frame <length=6, flags=0x00, stream_id=0>
(niv=1)
[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
フレームの構造はヘッダーとペイロードから構成される。ヘッダーは固定長の9バイト、ペイロードは任意の長さである。ヘッダーはペイロードの長さ(3バイト)、フレームの種類(1バイト)、フラグ(1バイト)、ストリームの id (4バイト)で構成される。
SETTINGS
フレームを表す値は「0x04」である。ログの記録では長さは0x06、フラグは 0x00、ストリーム id は 0x00 である。したがってヘッダーは次のようにあらわされる
0x00 0x00 0x06 # ペイロードの長さ
0x04 # フレームの種類(Settings)
0x00 # フラグ
0x00 0x00 0x00 0x00 # ストリーム id
わかりやすいように改行で示したが、実際のデータは9バイトのバイト列である。
次にSettings フレームのペイロードを調べる。設定項目ごとに識別子(2バイト)と値(4バイト)で構成される。ペイロードの長さは6バイトなので、設定項目は1つということになる。
上記のログから SETTINGS_MAX_CONCURRENT_STREAMS
の識別子は 0x03
で値は 100
(16進数で 0x64
) であることがわかる。Settings フレーム全体のバイト列(15 = 9 + 6)は次のようになる。
0x00 0x00 0x06 # ペイロードの長さ
0x04 # フレームの種類(Settings)
0x00 # フラグ
0x00 0x00 0x00 0x00 # ストリーム id
0x00 0x03 # 識別子 SETTINGS_MAX_CONCURRENT_STREAMS
0x00 0x00 0x00 0x64 # 値
Discussion