はじめに
こんにちは。システム統括本部 プラットフォーム開発本部 大久保諒です。本稿では、次世代 ウェブ プロトコルである HTTP/2 を使用するにあたり必要となる、クライアントとサーバー間でどのプロトコルを使用するかの合意を取る処理(プロトコルネゴシエーション)に関する簡単な説明と、Yahoo! JAPAN において広く使用されている HTTP プロキシサーバー・アクセラレータである ATS(Apache Traffic Server) におけるサポート状況について解説します。また、本稿を執筆するに辺り HTTP/2 へのプロトコルネゴシエーション方法の一つである HTTP/1.1 からの Upgrade 機能を ATS に追加するパッチを書きましたので、それに関する解説も行います。
ATS や HTTP/2 については既に何度か本ブログにて紹介しておりますので、ぜひそちらもご覧ください。本稿では以前の記事で記述した ATS や HTTP/2 に関する解説は極力省かせていただこうと思います。
- HTTP/2 入門
- Yahoo! JAPAN における HTTP/2 への取り組み
- HTTP/2 への取り組みの続報と ATS Summit 参加報告
- Apache Traffic Server の脆弱性の発見と報告
HTTP/2 のプロトコルネゴシエーションについて
HTTP/2 では HTTP/1.1 と同様、80 番と 443 番ポートを使用して通信を行います。これにより新たに HTTP/2 用にポートを開放するなどといった作業が不要になっています。しかしながら HTTP/1.1 と同じポートを使用する都合、使用しているプロトコルが HTTP/1.1 なのか、HTTP/2 なのか、あるいは SPDY なのか判別できるようにする必要があります。そこで、どのプロトコルを使用するかクライアントとサーバーで合意を取るプロトコルネゴシエーション処理が必要となります。
プロトコルネゴシエーションは使用スキーマが http か https かで方法が異なります。https の場合は TLS の拡張である NPN(Next Protocol Negotiation) もしくは ALPN(Application-Layer Protocol Negotiation) で行います。http の場合は HTTP Upgrade を使用して、クライアントが いったん HTTP/1.1 で Upgrade ヘッダ付きリクエストをサーバーに送出し、Upgrade のトークンによって HTTP/2 にスイッチするという方針を取ります。ただし主要ブラウザーベンダは TLS 拡張によるネゴシエートのみサポートする方針と取っており、HTTP Upgrade はそれほど積極的にサポートされていないのが現状です。
なお、事前に通信相手が HTTP/2 を解釈できることを知っている場合、プロトコルネゴシエーションを行わずに HTTP/2 リクエストを送ることも可能です。(この方法は Direct と呼ばれることがあります)相手の HTTP/2 対応状況を知る手段の一つとして Alt-Svc が提案されていますが、2014 年 12 月現在仕様策定が進んでおらず、これに対応している HTTP/2 実装も非常に限られています。
NPN
NPN(Next Protocol Negotiation) は、サーバーがサポートするプロトコルのリストをクライアントに送り、クライアントが使用プロトコルを選択することでプロトコルネゴシエーションを行う TLS 拡張です。OpenSSL においては 1.0.1d 以上でサポートされています。
NPN で使用プロトコルを決定するまでの流れは下図のようになっています。まずクライアントが ClientHello に NP Extension を付与し、NPN の使用を宣言します。サーバーは NP Extension を伴う ClientHello が来た際、ServerHello に NP Extension と自分が解釈できるプロトコルのリストを含ませます。その後、クライアントが NextProtocol タイプの Handshake Message を送信し、選択したプロトコルをサーバーに伝えます。
ALPN
ALPN(Application-Layer Protocol Negotiation) は NPN と同じくプロトコルネゴシエーションを行うための TLS 拡張です。ただし ALPN では NPN とは異なり、使用プロトコルをサーバーが選択します。OpenSSL においては 1.0.2 以上でサポートされる予定です。
ALPN で使用プロトコルを決定するまでの流れは下図のようになっています。まずクライアントは ClientHello に ALPN Extension と自分が使用できるプロトコルのリストを付与してサーバーに送ります。その後サーバーが ServerHello の拡張として ALPN Extension と選択したプロトコルを含めてクライアントに返信します。クライアントはサーバーに選択されたプロトコルに問題がなければそれを使用します。
Upgrade
http スキームで HTTP リクエストを行う際は TLS 拡張によるネゴシエーションが使用できないため、サーバーが HTTP/2 を解釈できるか分からないという課題があります。HTTP Upgrade メカニズムはこの問題を解決する仕組みとなっています。これは HTTP セッションをコネクションを切らずに別のプロトコルに切り替えて再利用する機構です。WebSocket の通信開始時にもこの仕組みが使用されます。
HTTP/2 へのアップグレード時の処理の流れは下図のようになっています。
まずは http スキーマで HTTP/1.1 セッションを開始します。ここでクライアントはリクエストを送る際、h2c トークンを値とする Upgrade ヘッダ、base64url エンコードした HTTP/2 SETTINGS フレームのペイロードを値とした HTTP2-Settings ヘッダ、Upgrade と Http2-Settings の値を含む Connection ヘッダの三つを付与します。サーバーが HTTP/2 へのアップグレードに対応していれば、HTTP/1.1 で 101 Switching Protocols を返した後 HTTP/2 通信を開始することができます。また、Upgrade を要求した際の HTTP/1.1 リクエストに対するレスポンスは HTTP/2 のコンテキストに移った際に HTTP/2 のレスポンスとして返します。
もし HTTP/2 へのアップグレードに対応していない場合は Upgrade ヘッダを無視して通常通り HTTP/1.1 リクエストを処理して 200 OK などを返すこともできます。
ATS における実装
NPN と ALPN
ATS では 3.1.3 から NPN が、5.0.0 から ALPN がサポートされており、現在の安定版では使用可能な状態になっています。
ただしこれらを使用する SPDY は最近リリースされたバージョンである 5.0.0 からサポートされたという背景もあり、実際に運用していくのに便利な設定値など周辺機能のサポートやや不足している状態です。たとえば NPN で通知する、あるいは ALPN で受け入れるプロトコルのリストは現在は設定値で変更不可能です。(これについては 設定可能にするチケット が発行されています)ちなみに現在 HTTP/2 の実装が完了していないため、NPN, ALPN で使用されるプロトコル名リストには HTTP/2 が含まれないようになっています。
Upgrade
ATS には現状 HTTP アップグレードメカニズムを使用した HTTP/2 へのアップグレードには対応しておらず、NPN もしくは ALPN によるプロトコルネゴシエーションのみサポートされている状態です。しかしながらアップグレード対応の為の実装作業はそれほど大掛かりな作業ではなく、http スキームからのネゴシエーション方法もサポートしておきたいため(本稿のためのネタ作りも兼ねて!)今回実装してパッチを送りました。
ATS の HTTP 処理に入るまでの流れを大ざっぱに図示すると下記のようになります。
http スキームでリクエストが来た際はまず ProtocolProbeSessionAccept によってリクエストが HTTP/2 (ネゴシエーションを行わない Direct の場合)なのか HTTP/1.1 なのか判別します。HTTP/1.1 のリクエストであることが判別できたら HttpSessionAccept, HttpClientSession によってリクエストを処理するまでの前処理が行われ、HttpSM と HttpTransact による HTTP/1.1 処理が開始されます。また現状 HTTP/2 にもこれらに対応する Http2SessionAccept, Http2ClientSession, Http2ConnectionState といったクラスが実装されています。
今回書いたパッチの大まかな処理の流れは下図の通りです。
h2c トークンを伴う Upgrade ヘッダがあるかどうか判別可能になるのは、HTTP リクエストの処理が行われる HttpSM, HttpTransact まで移り、リクエストヘッダのパース処理を行う状態に遷移してからになります。ここで Upgrade, Connection, HTTP2-Settings ヘッダをチェックして問題がない場合 HTTP/2 へのアップグレードが可能となるので、いったん HTTP/1.1 101 Switching Protocols レスポンスを返します。その後 HTTP/2 処理に移る都合 HttpClientSession や HttpSM など HTTP/1.1 処理をするオブジェクトを、TCP コネクションのイベント処理を行う NetVConnection オブジェクトのみ残してリソースを解放してしまいます。NetVConnection と Upgrade 時の HTTP/1.1 リクエストのヘッダを Http2ClientSession に渡し、そのリクエストに対するレスポンスを返してしまえば後は NPN, ALPN を用いて HTTP/2 セッションを開始した状態と同等になり、アップグレードが完了します。
上記の Upgrade サポート用パッチは JIRA に投稿済 です。
おわりに
以上、HTTP/2 処理を開始するまでに必要となるプロトコルネゴシエーションに関する簡単な説明と、Yahoo! JAPAN で HTTP/2 対応実装を進めている ATS におけるサポートの解説でした。今回、ただ単に解説を行うだけでは面白くないと思い、既存の HTTP/1.x に特化した実装からの HTTP/2 へのアップグレードに取り組んでみました。
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました