Wireless・のおと

HTTP のはなし

ブログ
技術解説 昔話 HTTP SSL

TCP 関係の話題続きで、今回は HTTP についてのおはなしです。もはや「誰もが使っている」と言っても過言ではない HTTP ですが、Digest Authentication だとか Chunked Encoding だとかの盲腸的な裏話や、先日制定された HTTP/2 についても言及します。

HTTP/0.9 : Dawn of Internet era

HTTP (Hyper Text Transport Protocol)は今日「インターネット」の代名詞のようになったプロトコルですが、もともとは 1990 年代にヨーロッパの原子核物理研究所(CERN)で「必要に迫られて」「場当たり的」に開発されたものです。各国の研究者が集う CERN には各国・各組織ごとにバラバラな機種・ソフトウェアが持ち込まれ、簡単な図入りドキュメントの共有すら困難でした。いちいち紙に印刷してコピーを配らなければならないようでは、何のためにコンピュータネットワークを使っているのやらわかりません。HTTP はこの不便を解決すべく、イギリスの計算機学者ティム・バーナーズ・リー(Sir Tim Bnerners Lee)によってハイパーテキスト言語(Hyper Text Markup Language, HTML)とセットで開発されたものです(※註)。その目的はなるべく簡易な文法で画像入りの書類を既述し、それをできるかぎり多くの機種・OS からオンライン閲覧することでした。

※註:このときプロトタイプ開発に使われたワークステーションが、当時 Apple 社を離れていたスティーブ・ジョブス氏が創業した NeXT であったことは有名です。また HTML は当時国際標準フォーマットとして普及が推進されつつあった SGML からタグ文法を拝借し大幅に簡略化したものですが、HTML の文法は SGML と「似ている」ものの厳密には SGML 準拠ではありません。後で SGML 下位互換の汎用マークアップ言語 XML が開発され、XML の仕様に基づいて HTML を再定義した XHTML が開発されました。

かくして 1991 年に発表された「暫定版」の HTTP/0.9 は、物凄く単純なプロトコルでした。要求元(クライアント)は TCP で要求先(サーバ)の 80 番ポートを接続し、たった1行のリクエストを送ります。リクエストは「GET /index.html」のように、コマンド「GET」に必要なドキュメント(コンテント)のパス名が引数で渡されるだけです(これを「Simple-Request」と呼びます)。サーバはこれに対し、コンテント本体をガバッと送信してコネクションを切ります。エラーメッセージも何もありません(これを「Simple-Response」と呼びます)。
HTTP/0.9 はエラーの判断もできず、送られてきたコンテントの日付やフォーマットの情報もなく(ファイルの中身や拡張子から察するしかない)、コンテントのサイズもわからない(コネクションが切断されてはじめてファイル終端がわかる)など問題もありました。しかし何よりも「アホみたいに単純」という特徴があり、ソケットプログラミングに慣れた人なら 30 分くらいで HTTP/0.9 のサーバを書けるでしょう。「雑多なアーキテクチャ間でのファイル交換」という CERN の問題解決に特化した設計であり、この単純さゆえに HTTP はあっという間に実装が広まり「似たような」プロトコルを次々と駆逐してゆくことになります(※註)。

※註:HTTP に駆逐された犠牲者の一つが、分散連携ドキュメント管理の先駆者だった Gopher プロトコルでした。Gopher と HTTP の明暗を分けた要因には様々なものがありますが、Gopher 開発元のミネソタ大学が 1993~2000 年にかけてライセンス有償化をほのめかしていたという事情もあります。

HTTP/1.0 : The birth of WWW

HTTP が世界をつなぐ WWW (World Wide Web) として本格的に成功したのは、1996 年に発表された HTTP/1.0(RFC1945) からでした。HTTP/1.0 ではリクエスト・レスポンスともに文法が拡張され、エラー情報やドキュメントの付随情報が交換できるようになました。今では常識のような「存在しないコンテントにアクセスしたとき返されるエラーは 404」などのエラーコードは、HTTP/1.0 になってから定義されたものです。
「アホみたいに単純」だった HTTP/0.9 に対して HTTP/1.0 はだいぶ複雑になり、仕様書 RFC1945 は 138K バイト / 3068 行 になりました。しかし「クライアントからの接続・要求送信」「サーバからの返答返送・切断」という基本動作は HTTP/0.9 と大きく変わっていません。

HTTP/1.0 では「GET」の他に「HEAD」と「POST」のコマンドが拡張されました(※註)。「HEAD」は GET と殆ど同じで、しかしコンテントに関わるヘッダ情報(フォーマット、サイズ、最終更新日付など)だけを返し、コンテント本体の返送を伴わないというコマンドです。まだ低速のダイヤルアップ回線が主流だった時代、ブラウザが更新日付を比較して更新があったファイルのみをダウンロードするような目的で作られたようですが、結局あまり活用されることはなく、現在ではほとんど使われていないと思います。

※註:他にも「PUT」「DELETE」「LINK」「UNLINK」がオプションコマンドとして定義されていますが、実際には殆ど使われていないので割愛します。

「POST」はもっと重要で、HTTP/0.9 が「クライアント(ブラウザ)がサーバからコンテントを取得するだけ」の片方向プロトコルだったのに対し、POST は「クライアント(ブラウザ)からサーバにデータを送る」ことを可能にしたものです(※註)。これによって HTTP は双方向のプロトコルとなり、当時流行した「個人ホームページ」には「掲示板」「足跡帳」「連続小説」のような CGI 機能を使った投稿型コンテンツが備わってゆくことになります。ちなみに CGI とは Common Gateway Interface の略で、プロトコル名ではなく HTTP サーバと拡張プログラムの通信方式(のなかの一つ)を指すものですが、現在では「動的コンテンツを提供する HTTP サーバ」程度の意味で使われることも多いです。

※註:ただし POST も GET もリクエスト~リプライの基本動作は殆ど同じで、異なるのは POST の場合リクエスト・ヘッダの後に任意長のコンテントが追加される点だけです。「クライアントからデータを送る」というだけなら、GET コマンドでも URL 引数を用いてある程度(1KByte 以内くらい)のデータを送ることはできます。これはSX-580 電気スタンドの記事でも解説しましたね。

SSL : Internet became commercial service

SSL (Secure Socket Layer) は TCP とアプリケーション層の間に挿入される認証・暗号化のミドルウェアで、Netscape Communications 社によって開発されました。最初のメジャーリリースは 1995 年に発表された SSL 2.0 です。SSL は必ずしも「HTTP の暗号化」だけを目的に開発された技術ではないのですが、現在では HTTP と SSL(およびその発展型である TLS)は事実上不可分の関係になっています。
SSL を実装した WWW ブラウザ「NetScape Navigator」は大ヒット作となり、インターネット普及期の起爆剤の一つになりました。今では当たり前のように使われているネットショッピングやオンライン予約は、SSL による認証・暗号化機能があって初めて実用になったものです。翌 1996 年にはヘッダ構造の拡張性などを整理した SSL 3.0 が発表され、その基本構造はその後 20 年以上にわたって使われ続けることになります。

インターネット普及期を蜜月のように支えた HTTP と SSL ですが、この2つの仕様制定は必ずしも同期はしていませんでした。HTTP は W3C (World Wide Web Consortium) と IETF (Internet Engineering Task Force) で制定されるオープンスタンダードですが、SSL は仕様が公開されているとはいえ、あくまで Netscape という一企業の社内仕様です。Netscape 社は決してオープン化に後ろ向きだったわけではなく、むしろ積極的に SSL 仕様のドラフトを IETF に提出しているのですが、毎回リジェクトされ RFC 標準としては一度も採択されていません。どうも当時の IETF には、あまりに強大な影響力を持つ一企業の製品仕様をインターネット標準とすることに抵抗があった雰囲気で、特に SSL が採用していた暗号アルゴリズム(RSA 公開鍵暗号および RC4 共通鍵暗号)が RSA 社の特許下にあることに懸念があったようです。

HTTP/1.1 : Thinking too much?

インターネットが爆発的に普及するなか、早くも 1997 年には HTTP/1.1 (RFC2068) がリリースされました。RFC 文書のサイズは 377K バイト / 8176 行と HTTP/1.0 の倍以上になり、IETF の成人病がこの頃から始まったことを伺わせます。
HTTP/1.1 でも HTTP の基本動作...「クライアントからコネクション開設」「GET ないし POST のリクエスト送信」「サーバからレスポンス返送」というところは変わっていません。HTTP/1.1 ではオプションヘッダが山のように追加定義され、幾つか新しい試みも導入されました。

Content-Range

おそらく、HTTP/1.1 でもっとも有用だった追加機能がこの Content-Range ヘッダでしょう。1.0 以前の HTTP では要求したコンテントは必ず全文で返されましたが、インターネットが普及すると数十メガバイトを超えるデータが頻繁に交換されるようになり、99% までダウンロードした所で回線切断、なんてことになるとまた最初からやり直しでした。Content-Range はクライアントからサーバに対し、取得したいデータの範囲を指定する機能です。これによって「最後の 1% だけ取得」という部分要求が可能となり、ダウンロードの中断・再開や分割ダウンロードが柔軟にできるようになりました。
Content-Range は多分に必要に迫られて追加された機能ですが、FTP などの古典的なファイル転送プロトコルには「ありそうで無かった」機能でもあり、HTTP はファイル転送プロトコルとしても多用されるようになってゆきます(※註)。

※註:より本格的なファイル操作・転送プロトコルとして HTTP を大幅拡張した WebDAV (RFC2518, 1999) も定義されましたが、「一部では限定的に使われている」存在にとどまり FTP や SSH を駆逐するには至っていません。これも Microsoft による影響が強く、Windows に実装された WebDAV がエンタープライズ用途に向けたディレクトリサービスや証明書管理を必須とする、一般ユーザーが日常のファイル転送用に使うにはあまりに複雑怪奇な代物だった事情が大きいです。

Persistent Connection

HTTP/1.0 以前は1セッション(リクエスト-レスポンス)毎にコネクションを切断していましたが、埋め込み画像が多用された HTML 文書の表示には数十~数百件のセッションが必要で、接続・切断のオーバーヘッドが馬鹿にできないものになりました。これを合理化しようとしたのが Persistent Connection です。HTTP/1.1 対応のサーバはコンテント送信後もコネクションを維持し続け(※註)、クライアントは毎回毎回新規開設することなく、既存コネクションにリクエストを次々と送ることができます。

※註:Persistent Connection は HTTP/1.1 のデフォルト動作ですが、サーバないしはクライアントから Connection:close ヘッダを送って切断モードを指定することも可能です。

Persistent Connection の効果はかなり大きく、特に埋め込み画像を多用した WEB ページの表示速度は目に見えて向上します(※註)。しかしサーバ側としては「コネクションはブラウザが終了しないかぎり開きっ放しになり、いつ切断されるかわからない」ためリソースを抱え込まれることになり、また初期の HTTP/1.1 対応ブラウザにはリロード連打すると新規コネクションを際限なく新規生成するバグを持ったもの(MSIE 4.0 とか...)があったりして、コネクション保有数に上限のある組み込み WEB サーバにとってはあまり有り難くない機能拡張でもありました。

※註:これはオープン・クローズのオーバーヘッドだけではなく、TCP には回線混雑を避けるための「Slow Start」アルゴリズムが実装されており、新規コネクション成立直後はウィンドウサイズが 1xMSS 値に設定されるため、ファイルを多数取得するときに別々の新規コネクションを使うと通信効率が悪化する、という事情にもよります。また SSL を併用する場合は回線確立のたびに鍵交換・認証に伴うオーバーヘッドがかかりますので、Persistent Connection の効果はより顕著になります。

Transfer-Encoding

HTTP/1.0 で導入されたヘッダにより、サーバは返送するコンテントのサイズを Content-Length ヘッダで事前に知らせることになりました。しかしインターネットが普及して WWW が様々な用途で使われるようになると、たとえばサーチエンジンの検索結果のように、動的に生成されるコンテンツが増えてきました。このような場合、まず検索エンジンを走らせて検索結果を HTML ファイルとして保存し、保存したファイルのサイズをヘッダに付けてから改めて HTML を送信するという2度手間が必要になります。検索エンジンがコンテントとしての HTML を直接送信できれば効率的なのですが、作ってみるまでサイズがわからないのでは Content-Length ヘッダが作成できずデータ送信を開始できない、というニワトリタマゴの状態になります(※註)。

※註:このため Perl CGI スクリプトでは Content-Length を出力せず、コネクション切断をもって送信終了とみなす実装になっているものが多いのですが、これには Persistent Connection による通信効率向上の恩恵が受けられないという副作用があります。

Transfer-Encoding はこれを柔軟化しようとした仕組みで、HTTP/1.1 では "chunked" という分割送信方式が定義されています。Transfer-Encoding: chunked のモードでは、送信データは任意長の「チャンク(塊)」に分割され、それぞれのチャンクがチャンク長を示す「チャンク・ヘッダー」と空行(※)の「チャンク・フッター」に挟まれて送信されます。

※註:仕様上チャンク・フッターは HTTP ヘッダと同形式として定義されており、空行で終端する任意行の情報を付加できることになっていますが、実際にはほとんど空行として実装されています。

例:通常の Content-Length モード

HTTP/1.1 200 OK<CR><LF>
Content-Length: 49<CR><LF>
<CR><LF> <= HTTP ヘッダの終わりを示す空行
The quick red fox jumpes over the brown lazy dog. <= コンテント 49 バイト


例:Chunked Encoding モード

HTTP/1.1 200 OK<CR><LF>
Transfer-Encoding: chunked<CR><LF>
<CR><LF> <= HTTP ヘッダの終わりを示す空行
12<CR><LF> <= チャンク長 0x12=18 バイト(チャンク・ヘッダー)
The quick red fox
<CR><LF> <= チャンクの終わりを示す空行(チャンク・フッター)
b<CR><LF> <= チャンク長 0x0b=11 バイト
jumpes over
<CR><LF>
14<CR><LF> <= チャンク長 0x14=20 バイト
 the brown lazy dog.
<CR><LF>
0<CR><LF> <= チャンク長 0:コンテントの終わり


RFC2068 の cunked encoding では、チャンク長を「ゼロサプレス(先頭1文字が 0 以外から始まる)の」「16 進数」で表記することになっています(※註)。Content-Length など他のヘッダ値は 10 進なのになぜチャンク長だけ 16 進なのか、なぜわざわざ「ゼロサプレス」を明文化してあるのか謎です。おそらく HTTP/1.1 制定メンバーの中に「10 進より 16 進表記のほうが効率的に決まっている」とか「長さ 1 を 00000000000000000000001 とか表記するバカな実装が出てこないようにゼロサプレスを明文化すべきだ」とか余計なことを言い出した人が居たのではないかと思いますが、「IPv6 のはなし」でも触れたように、こういう「考え過ぎは休むに似たり」という仕様練り過ぎの傾向は、2000 番台以降の RFC を肥大化させる IETF の持病になってゆきます。

※註:ただし「ゼロサプレス」の項は改訂版の RFC2616(1999) で撤回されました。こうなると今度は「朝令暮改」という印象を受けますが...。

Digest Authentication

Authentication は HTTP/1.0 で導入された機能で、パスワードなどで保護されたコンテントへのアクセス時、クライアントからサーバへユーザ名・パスワードを伝達する機構です。HTTP/1.0 で定義された「Basic Authentication」は単純な BASE64 符号化を用いているので、通信が傍受できればユーザ名もパスワードも丸見えでした。セキュリティが必要な場合は SSL + Basic Authentication を使えば良いのですが、前述のように HTTP 制定グループは SSL に対して微妙な距離感を持っており、SSL を使わずとも安全なパスワード認証ができるようにと考えられた仕組みが Digest Authentication です。
Digest Authentication は俗に「チャレンジ・レスポンス」と呼ばれる認証方式で、サーバ側で生成した疑似乱数(チャレンジ)を用いてユーザ名とパスワードを乱数化(ダイジェスト)した結果だけが返送され、サーバ側ではダイジェスト値の一致をもって認証の可否を判断するという方式です。

例:Basic Authentication
 

GET /secret.html HTTP/1.1
・最初の1回目は /secret.html が保護コンテンツであることを知らないので Authorization: ヘッダ無しでアクセス。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="WallyWorld"
・サーバは 401 応答で保護コンテンツであることを示す。ブラウザはこれを受けてログイン画面を表示しユーザ名・パスワードの入力を要求する。

GET /secret.html HTTP/1.1
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
・入力されたパスワードを BASE64 で符号化して添付する。
・QWxhZGRpbjpvcGVuIHNlc2FtZQ== を BASE64 で解凍すると「Aladdin:open sesame」になる。
・パスワードキャッシュが有効な場合、これ以降は /secret.html へのアクセス時常に Authorization: ヘッダが付加される。

例:Digest Authentication
 

GET /secret.html HTTP/1.1
・最初の一回目はチャレンジ値が無いので、仮に /secret.html が保護コンテンツであることが判っていたとしても Authorization: ヘッダ無しでアクセス。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"
・最初の一回目は必ず 401 エラー応答になり、エラー応答にチャレンジ値(nonce/opaque)が付加される。
・ブラウザはこれを受けてログイン画面を表示しユーザ名・パスワードの入力を要求する。

GET /secret.html HTTP/1.1
Authorization: Digest username="Mufasa", realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/secret.html", qop=auth, nc=00000001, cnonce="0a4f113b", response="6629fae49393a05397450978507c4ef1", opaque="5ccc069c403ebaf9f0171e9517f40e41"
・エラー返答の nonce/opaque 値を使ってパスワードを暗号化した値が response として付加される。
・サーバ側のチャレンジ有効期間設定によっては、たとえパスワードキャッシュ有効であっても要求→401 応答→Digest 再計算というサイクルが毎回必要となる。


以前に「EAP のはなし」でも触れましたが、チャレンジ・レスポンス方式は盗聴に対して一定の効果があるものの、通信中継者による改竄(Man-in-the-middle attack)に対しては無力という限界があります。これに対して SSL では公開鍵証明書による通信相手の認証が行われるため、中継改竄だけでは簡単には破れません。
それでも一定の(盗聴のみを対策すれば良いような)用途では使われるかも知れなかったのですが、HTTP/1.1 の Digest Authentication には余計なオマケがつきました。RFC2069 Section 2.4 に掲載されている Digest の計算例が何をどうしても合わないのです。実装検証用として提供されているはずのデータが誤っているため、RFC2069 Digest Authentication は仕様書に基づいた実装検証が不可能になってしまいました。しかもこの問題は丸2年も放置され、改訂版の RFC2617 でやっと訂正されましたが、このとき何故か例題パスワードも変更されたので、結局 RFC2069 の正解が何だったのか公式には提示されずじまいという格好に終わりました。
RFC2617 では例示パスワードの誤記訂正にとどまらず、最初の一回だけチャレンジを送ることでセキュリティを強化しようとした MD5-sess 拡張認証方式が追加されたりしましたが、SSL/TLS が普及したこともあって Digest Authentication は結局ほとんど使われていません。そもそも WWW 上でのユーザー認証は HTML の FORM 機能+SSL/TLS を使って実装されるものが圧倒的に多く、HTTP の認証機能じたい(Digest はもちろん、Basic さえも)殆ど使われていないのが実情です。

TLS 1.0 : Struggle inside standard

Netscape 社が IETF に提出した SSL ドラフトは未採択に終わりましたが、すったもんだのすえに仕様を小変更した RFC2246 TLS (Transport Layer Security) として 1999 年に制定されました。動作的に TLS は SSL3.0 と殆ど同じで上位互換性もあり(※註)、何故 SSL を不採用にしたうえ名前まで変えた規格にしたのか、IETF 内部でいろいろキナ臭い話があったのだろうと伺わせます。

※註:TLS1.0とSSL3.0の通信手順はほぼ共通ですが拡張仕様に差異があり、通信開始時に交換されるバージョン番号を用いて処理を変えることで両者をサポートすることができます。SSL3.0のバージョンは0x0300、TLS1.0のバージョンは0x0301です。

TLS と SSL の動作的な違いはわずかですが、TLS の仕様書では SSL が標準採用していた RC4/RSA 暗号に関する言及が少ない一方で、Diffie-Hellman 系アルゴリズムを採用した DSS 鍵交換と DSA 証明書、そして米国政府標準暗号アルゴリズムだった DES/3DES の記述がやたらに多いことが目につきます。当時 IETF が抱いていた「特定企業の特許技術をインターネット標準に採択するなんて、絶対に許さない!」という意識が垣間見えますが、IETF がこれだけ警戒した RSA アルゴリズムの特許失効期限は RFC2246 発表わずか1年後の 2000 年 9 月 20 日で、しかも失効時期限の2週間前には RSA 社自身が特許権の放棄を公言しました。IETF の「RSA 絶対阻止」とは一体何だったのか...まぁ、世間にはよくある話であります。

HTTP/1.2? : Myth on Internet

2011 年頃に「いよいよ HTTP/1.2 が制定」という報道が流れたことがありますが、私の知る限り「HTTP/1.2」という仕様は制定どころかドラフトすらも存在しません。どうやらこのとき「HTTP/1.2」として報道されたものは、HTTP/1.1 と並行して検討されていた PEP (Protocol Extension Protocol) と呼ばれる HTTP 拡張機構だったようです。
PEP がどんなものだったのか、今更詳しく調べてみる考古学趣味はありません。アーカイブをざっと流し読みしてみると「プロトコル層にメニュー構造を取り込む」といったような話もあり、低機能携帯向け簡易 WEB プロトコルとして開発された(そして無惨に失敗した) WAP を連想します。w3.org の議事録によると PEP の検討は 1995~1997 年にかけて行われており、その頃の課題として「低機能携帯向け」というのは理解できますが、何でそんなものが 14 年も後になって突然「いよいよ制定」と報道されたりしたのか全然わかりません。いずれにせよ PEP は検討だけで終わっており HTTP 仕様に取り込まれることはなく、HTTP の公式バージョンは 2016 年現在も 1.1 のままです。

(参考 URL: http://www.w3.org/TR/WD-http-pep.html)

HTTP-ng? : New Generation never came

幻の HTTP/1.2 と異なり HTTP-ng は実在したプロジェクトで、 1999 年頃主にゼロックス社のパロアルト研究所(PARC)で検討されていた「次世代 HTTP」の企画です。HTTP-ng ではオブジェクト指向的な考えを取り入れ、CGI のような後付け拡張ではなく、サーバ内のオブジェクトやメソッドを直接アクセスする RPC (遠隔呼び出し, Remote Procedure Call)のような機構に一新することを考えていたようです。
データ形式は旧 HTTP の「文字列+改行」形式を全廃して RFC1832 XDR (External Data Representation Standard) を採用するとしており、XDR は SNMP で悪名高い OSI の遺物 ASN.1 データフォーマットのサブセットです。HTTP-ng ではメッセージ構造をバイナリのビットフィールドとして実装しようとしており、更には SNMP で採用された BER (Basic Encoding Rule) ではなく可変長ビットの PER (Packed Encoding Rule) の採用が検討されていて、当時ドラフトを見ながら「余計なことをしやがって...」と思ったことを今でも覚えています。
幸か不幸か HTTPng も検討だけに終わり、HTTP は今でも「非効率でいい加減な」文字列+改行のヘッダフォーマットで動作し続けています。「アホみたいに単純にしとけ」...「Keep It Simple and Stupid」という警句(※)がよく適合する1例だと思っています。

(参考 URL: http://www.w3.org/Protocols/HTTP-NG/Activity.html)

※註:略して K.I.S.S。語源には諸説ありますが、ロッキード社の伝説的航空機設計者、クラレンス・「ケリー」ジョンソンの座右の銘だったとも言われます。

HTTP/2 : Necessity is the mother of invention

HTTP/2 は現在進行中の話です。HTTP/2 は Google 社を中心に開発され、RFC7540 として 2015 年 5 月に制定されました。RFC7540 は 204K バイト / 5380 行、ヘッダ圧縮仕様の RFC7541 が 115K バイト / 3084 行と決して小さな仕様ではないですが、とかく肥大化しがちな最近の RFC としては頑張った方でしょう。現行の HTTP/1.1 RFC2616 なんて 421K バイト / 8994 行もありますから...。
さてHTTP/2 は HTTP/1.1 の更新ではなく、HTTP/1.1 の上で動作する追加プロトコルです。HTTP/2 では「HTTP Stream」という概念が導入されており、これは HTTP/1.1 の Chunked Encoding と少し似ていますがもっと高機能(複雑)で、接続・切断の遷移状態やウィンドウ管理、ストリーム ID 管理などを持ちます。片道クローズなどの状態遷移仕様(RFC7540 Figure.2)は TCP そっくりでもあり、TCP の上で動作する HTTP/1.1 の上に「TCP のようなもの」を実装したものが HTTP/2 とも言え、これだけ見ると「何を2度手間なことをやっているんだ」と思わなくもありません。

http2-state-large.jpg

HTTP/2の状態遷移図

既に記したように、HTTP はもともと CERN でのドキュメント共有用に開発されたプロトコルでした。HTTP/0.9 以来、基本動作は一貫して「クライアントからのリクエスト送信→サーバからのレスポンス返送」です。それが WWW として実装され、WWW が発展して HTTP がビデオ配送やら AJAX のような準リアルタイム双方向型の通信に使われるようになると「リクエスト→レスポンス」を前提とする HTTP では色々不都合が出てきました。専用のリアルタイム・分散型通信プロトコルとしては RTP とか SCTP とか QUIC なども開発されているのですが、HTTP/2 はそういった機能を HTTP/1.1 に「上乗せ」する格好で実装したものです。おそらく HTTP/2 の Stream 層を IP や TCP の上に直接実装することだって不可能ではない思いますが、それをあえて「HTP の上に」実装したというのは、それだけトランスポート層インフラとしての HTTP...セキュリティ層の TLS や プロキシや NAT などの「汚い」接続手段も含めて...の存在が無視できない存在になっているという意味でもあります。

仮に SCTP のようなプロトコルでマルチキャスト・ビデオ配送サービスを実装しても、もはや NAT ルータを2段・3段に重ねて使うことすら珍しくなくなっている今の世の中では、「インターネット(WWW)はつながっているのにビデオが見れない!何とかしろ!」という苦情が殺到するのではないかと思います(※註)。HTTP/2 は HTTP/1.1 上で稼動するプロトコルなので、HTTP さえ通るなら HTTP/2 も動く理屈になります。何だか MBone やら IGMP やら IPv6 のフローラベルやら、過去数十年のインターネット機能拡張の努力を全力で否定するような話ですが、好むと好まざるに関わらずそれが現実です。

※註:IPv6 推進者は苦い顔をするでしょうね。IPv6 はまさに「こんな事態」に対応すべく制定され、ストリーム転送に備えて IPv6 ヘッダ領域の 24bit を「フロー・ラベル」に割いたりセキュリティ仕様(IPsec)を「実装必須」として盛り込んだりしたのに、そこを「頭越し」にして HTTP/2 Stream Transport 仕様が上乗せされた格好ですから。

まとめ

TCP/IP プロトコル(RFC791~RFC793)の発表が 1981 年ですから、91 年の HTTP(0.9) はちょうど 10 年後に発表されたことになります。そして HTTP の発表から 25 年、TCP/IP の発表から数えれば 35 年という歳月が経ち、様々な後付けの追加拡張が行われましたが、プロトコルの根幹は基本的に変わっていません。それは基礎設計の優秀さも意味しますが、基礎設計に遡る制限を抱え続けているという意味でもあります。TCPng や HTTPng は何度か提案されていますが成功していませんし、IP アドレスを拡大するだけの IPv6 ですら 20 年もかけて浸透できていません。果たしてそれが良いことなのか悪いことなのか、色々と感慨深いものがあります。
かつては(RFC 番号が 2000 未満だった頃は)新プロトコルなんて割と気安くポンポン発行されていて、V2 版では急に互換性が無くなったりすることも珍しくなかったのですが、それでも結構通用していました。ある時期からそれが通じなくなってきたのは、インターネットが商品化され社会インフラ化されていったことの証でしょう。既に「それがあること、動くこと」前提で社会が回っているのに、ある日突然互換性のない物をポンと出して「こっちの方が良いものだから、明日からこれを使ってね」と言っても、お金や時には人命まで掛かっている社会インフラを急に変えることなんてできません。よく「カビの生えた古臭い技術にしがみつく」などと批判される Microsoft 社などはその怖さを(良くも悪くも)熟知しているのですが、どうも IETF のセンセイがたは「良い仕様を書きさえすれば、いずれ実装されて普及する」という無邪気な良貨論を信じているように思えます。それが良いことなのか悪いことなのかも俄かには判断できません。
HTTP コネクションの上にトランスポート層を乗っけた HTTP/2 は文字通り屋上屋を重ねたような代物で、技術的には不合理というかブザマとすら言えるものですが、「既存インフラを変えることなく、末端両側のサーバとクライアント(ブラウザ)の更新だけで新機能を使える」ことを主眼に置いたものでしょう。それですら本当に普及するかどうかはわかりませんが、仮に普及したならばあくまで舞台裏の機構として、「Chrome で Youtube に接続したとき、実は HTTP/2 でビデオストリームが流れている」のように「気が付いたら使っていた」という仕組みになるのではないかとは思います。まぁ、世の中の優れた発明品は大抵そうやって普及するものですね。

製品のご購入・サービスカスタマイズ・資料請求など
お気軽にお問い合わせください