してみるテストロゴ
Apache 2.4系でHTTP/2サーバを構築してみるテスト。

HTTP/2の動作確認

libnghttp2を作る際に、nghttpコマンドもインストールされています。これは、http/2に対応したクライアントです。このコマンドを使うと、動作確認ができます。

これは、/usr/local/binにインストールされているはずです。

h2c(非暗号接続)の場合

HTTP/2のサーバを構築する場合、TLS(SSL)が絡まない分、h2cのほうが簡単に動きます。そのため、まずは、h2c(非暗号接続)から動作確認すると良いでしょう。

ただ、実際のWebブラウザは、非暗号接続のh2cに対応していない可能性が高いです。構築時の参考程度に考えておくといいかもしれません。

負荷分散装置といった、リバースProxyの後方で運用するWebサーバで、利用するといった用途が、メインかと思われます。

アップグレード

nghttpコマンドに-uオプションを付けると、一旦、httpで接続してから、h2cにアップグレードをおこないます。

-vオプションは、おなじみの、verboseモードつまり、デバッグ情報が出ます。

こんな感じで使います。

$ /usr/local/bin/nghttp -uv http://example.com

次の実行例のように、HTTP Upgrade successの文字が出れば、成功

$ /usr/local/bin/nghttp -uv http://example.com
[  0.001] Connected
[  0.001] HTTP Upgrade request
GET / HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: AAMAAABkAAQAAP__
Accept: */*
User-Agent: nghttp2/1.3.4


[  0.002] HTTP Upgrade response
HTTP/1.1 101 Switching Protocols
Upgrade: h2c
Connection: Upgrade


[  0.002] HTTP Upgrade success
…(省略)…

このHTTPからのアップグレードは、最初のリクエストをHTTP/1.1で投げた後に、サーバからのレスポンスによって、アップグレード成功を確認して初めて、HTTP/2の恩恵にあずかれます。

それまでは、スピードが売りの、HTTP/2の利点がちょこっと失われます。特に、負荷分散装置といった、リバースProxyの後方で運用するWebサーバでは、もったいないことになります。

そこで、次のダイレクトモードが用意されています。

ダイレクトモード

ダイレクトモードは、HTTP接続後に、以下の24 bytesを送信することで、HTTP/1.1から、HTTP/2に切り替える方法です。

PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n

HTTPからのアップグレードと違い、サーバからのレスポンスを待つことなく、HTTP/2を利用できます。

HTTP/2の利点を最初から生かしつつ、HTTP/1.1との互換も確保できます。

この機能は、httpd.confの、H2Directをonにすることで利用できます。

/usr/local/apache2/conf/httpd.confの一部
…(省略)…
LoadModule http2_module modules/mod_http2.so
<IfModule http2_module>
LogLevel http2:info
ProtocolsHonorOrder On
Protocols h2c http/1.1
H2Direct on
</IfModule>
…(省略)…

ただし、apacheモジュールによっては、このダイレクトモードの挙動では問題があるようです(参考:英語)。apache 2.4.17では、H2Directは、デフォルトでonでしたが、以降のリリースからoffになるかもしれません。

ダイレクトモードを利用する場合は、apacheモジュールとの互換性を確認の上、onとしてください。

コマンドとして、-uオプション(HTTPからのアップブレードを実行する)を使用せず、単純に

$ /usr/local/bin/nghttp -v http://example.com

とします。次の実行例のように

$ /usr/local/bin/nghttp -v http://example.com
[  0.001] Connected
[  0.001] send SETTINGS frame 
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.001] send PRIORITY frame 
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.001] send PRIORITY frame 
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.001] send PRIORITY frame 
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.001] send PRIORITY frame 
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.001] send PRIORITY frame 
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.001] send HEADERS frame 
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: http
          :authority: example.com
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.3.4
[  0.002] recv SETTINGS frame 
          (niv=3)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65536]
          [SETTINGS_MAX_HEADER_LIST_SIZE(0x06):65536]
[  0.002] send SETTINGS frame 
          ; ACK
          (niv=0)
[  0.002] recv SETTINGS frame 
          ; ACK
          (niv=0)
[  0.003] recv (stream_id=13) :status: 200
[  0.003] recv (stream_id=13) date: Sat, 07 Nov 2015 04:45:04 GMT
[  0.003] recv (stream_id=13) server: Apache/2.4.17 (Unix) OpenSSL/1.0.2d
[  0.003] recv (stream_id=13) last-modified: Tue, 17 Mar 2015 12:24:21 GMT
[  0.003] recv (stream_id=13) etag: "2ba2-5117b0be0092a"
[  0.003] recv (stream_id=13) accept-ranges: bytes
[  0.003] recv (stream_id=13) content-length: 11170
[  0.003] recv (stream_id=13) content-type: application/xhtml+xml
[  0.003] recv HEADERS frame 
          ; END_HEADERS
          (padlen=0)
          ; First response header
…(省略)…

特徴的なメッセージが無いのですが、recv SETTINGS frameや、recv HEADERS frameが来ていれば、成功です。

ちなみに、httpd.confの、H2Directが offだと、以下の通り失敗します。

/usr/local/apache2/conf/httpd.confの一部
…(省略)…
LoadModule http2_module modules/mod_http2.so
<IfModule http2_module>
LogLevel http2:info
ProtocolsHonorOrder On
Protocols h2c http/1.1
H2Direct off
</IfModule>
…(省略)…
$ /usr/local/bin/nghttp -v http://example.com
[  0.001] Connected
[  0.002] send SETTINGS frame 
          (niv=2)
          [SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100]
          [SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[  0.002] send PRIORITY frame 
          (dep_stream_id=0, weight=201, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=0, weight=101, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=0, weight=1, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=7, weight=1, exclusive=0)
[  0.002] send PRIORITY frame 
          (dep_stream_id=3, weight=1, exclusive=0)
[  0.002] send HEADERS frame 
          ; END_STREAM | END_HEADERS | PRIORITY
          (padlen=0, dep_stream_id=11, weight=16, exclusive=0)
          ; Open new stream
          :method: GET
          :path: /
          :scheme: http
          :authority: example.com
          accept: */*
          accept-encoding: gzip, deflate
          user-agent: nghttp2/1.3.4
Some requests were not processed. total=1, processed=0

Some requests were not processed. total=1, processed=0と出て失敗します。

h2(TLS接続)の場合

h2cの接続がうまくいっていれば、libnghttp2の機能に問題はないはずなので、あとは、TLS関連がうまくいっていれば、動作確認できます。

TLS接続の確認については、コチラをご覧ください。

nghttp2のインストールのところでも説明しましたが、自力でインストールしたOpenSSLを利用する場合、

$ source ~/.profile_ssl
Add /usr/local/ssl/lib.

として、OpenSSLのライブラリがインストールされている/usr/local/ssl/libを、ダイナミックリンクの対象とします。

コマンドオプションとしては、-uオプションを使わないのはh2cのダイレクトモードと同じです。h2c のダイレクトモードとの違いは、http://をhttps://に変えるだけです。

$ /usr/local/bin/nghttp -v https://example.com
s これを実行して、次の表示がでれば、OKです。
$ /usr/local/bin/nghttp -v https://example.com
[  0.001] Connected
The negotiated protocol: h2
…(省略)…

The negotiated protocol: h2と出れば、成功です。

h2cの時とはちがって、数行みれば、成功したか、失敗したかわかります。

代表的な失敗は以下の2通りです。

次の実行例は、TLS接続で、ALPN拡張が使えない場合の応答です。

$ /usr/local/bin/nghttp -v https://example.com
[  0.034] Connected
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)

原因には、2つの可能性があって、ひとつは、サーバ側のmod_sslがOpenSSL 1.0.2よりも前のライブラリを使用している可能性があります。

もうひとつは、クライアント側のnghttpがOpenSSL 1.0.2よりも前のライブラリを使用している可能性があります。

Unix系のOSには、最初からOpenSSLが同梱されていることが多いので、OpenSSL 1.0.2よりも前のものがOSに同梱されている場合、両方疑ってみるといいと思います。このエラーは、時代とともに減ってはくるとはおもいます。

次の実行例は、従来のTLS/SSL接続となってしまった例です。

$ /usr/local/bin/nghttp -v https://example.com
[  0.034] Connected
The negotiated protocol: http/1.1
[ERROR] HTTP/2 protocol was not selected. (nghttp2 expects h2)

これは、クライアントに対して、ALPN拡張で提示されたプロトコルがHTTP/1.1(つまり従来のTLS/SSL接続)だったことを意味します。

このメッセージがでたら、/usr/local/apache2/conf/extra/httpd-ssl.confを確認します。

ProtocolsHonorOrderがOnになっているか?Protocolsの順番は正しいか?確認してください。

/usr/local/apache2/conf/extra/httpd-ssl.confの一部
<VirtualHost _default_:443>
…(省略)…
<IfModule http2_module>
	ProtocolsHonorOrder On
	Protocols h2 http/1.1
</IfModule>
…(省略)…
</VirtualHost>

Chromeで確認

実際のブラウザでも確認してみましょう。GUIで動くブラウザは、TLS1.2のALPN経由(つまりh2)にしか対応していないことも多いので注意してください。

GUIで動くブラウザには、でデバッグモードだったり、デベロッパツールだったりが付いていると思います。chromeだと、URL欄右のプルダウンメニューから「その他メニュー」→「デベロッパツール」といきます。

ここで表示されたウィンドウの、Networkタブ を選択してから、F5キーを押します。すると下記のような表示になり、プロトコルを確認できます。

chromeのスクリーンショット

3カラム目が、プロトコルを意味します。h2と出ていれば、OKですね。

NEXT >> HTTP/2のチューニング

©Copyrights 2015-2023, non-standard programmer

このサイトは、あくまでも私の個人的体験を、綴ったものです。 軽く参考程度にご利用ください。