こんにちは!ネットワークの世界へようこそ。インフラエンジニアとして日々パケットの海を泳いでいる私ですが、Webブラウザで何気なく見ているウェブサイトの裏側では、実はものすごいドラマが繰り広げられているんです。
私たちが普段使っている「HTTP(Hypertext Transfer Protocol)」は、ブラウザとサーバーがおしゃべりするための共通言語です。昔のHTTP(HTTP/1.1)は、とっても真面目だけど少し不器用でした。画像が100枚あるページを開くとき、「画像を1枚ください」「はい、どうぞ」「次の画像をください」「はい、どうぞ」と、わざわざ1往復ずつ丁寧にやり取りしていたんです。これでは道路が大渋滞してしまいますよね。
そこで登場したのがHTTP/2です。HTTP/2は、1本の太いパイプライン(TCP接続)の中で、いくつもの会話(ストリーム)を同時に、しかも効率よくおしゃべりできるように進化しました。
今回はそのHTTP/2の心臓部、「HPACK(エイチパック)」という技術にスポットライトを当てます。特に、ヘッダーを小さくギュッと縮める「インクリメンタルなヘッダー更新」と「Huffman(ハフマン)符号化」について、身近な例えを交えながら一歩ずつ優しく紐解いていきましょう!
—
1. ヘッダーって、実は毎回同じことを喋っていませんか?
ウェブブラウザがサーバーに「このページを見せて!」とお願いするとき、必ず「リクエストヘッダー」というお供を連れていきます。このお供には、次のような情報がたっぷり詰まっています。
- 私はGoogle Chromeですよ (`user-agent`)
- 日本語が読めますよ (`accept-language`)
- このクッキーを持っていますよ (`cookie`)
これらは、ページ内の画像やスタイルシートを読み込むたびに、毎回何十回、何百回と繰り返して送信されていました。 「さっきも言ったよね?」とサーバーがツッコミを入れたくなるくらい、同じような文字列がネットワークを流れていたんです。
これを解決するのが、HPACKによる「ヘッダー圧縮」です。
—
2. 郵便局の「暗号帳」に例えてみる:静的テーブルと動的テーブル
ヘッダーの重複送信をなくすために、HTTP/2はブラウザとサーバーの間で「共通の暗号帳(テーブル)」を持つことにしました。郵便配達に例えてみましょう。
毎回「東京都千代田区〇〇町1-2-3 〇〇マンション101号室」と全文書くのは大変ですよね。そこで、配達員と宛先の間で「この住所は、今後『番号A』と呼びましょう」と取り決めをしておけば、次は「番号A宛てに荷物よろしく!」と書くだけで済みます。
HPACKには、この辞書が2種類用意されています。
① 静的テーブル(Static Table)
最初から世界共通で決まっている辞書です。「このお決まりのヘッダー名と値のペアなら、番号1番ね」という風に、仕様書にあらかじめ61個の定番ペアが登録されています。
(例:`:method: GET` は 番号2番、みたいなイメージです)
② 動的テーブル(Dynamic Table)
通信が始まってから、そのセッション(今回のアクセス中)専用にその都度追加されていく辞書です。これが今回のテーマである「インクリメンタル(段階的・追加的)なヘッダー更新」の正体です。
1. ブラウザが最初に「私の使っているブラウザのバージョンはコレです」と長い文字列を送る。
2. サーバーとブラウザは、「今の情報を、動的テーブルの『番号62番』として覚えようね」とこっそり共有する。
3. 次回からは、長ーい文字列を送る代わりに「62番!」とだけ送れば、サーバー側で元の文字列に脳内変換してくれる。
通信をすればするほど、動的テーブルが育っていき、送るデータがどんどん小さくなっていく。これがインクリメンタルな更新の魔法です。
—
3. よく使う文字を短く!「Huffman符号化」の知恵
さて、辞書に載っていない新しい文字列を送るときや、動的テーブルに登録する前の段階で、もう一つ強力な圧縮技術が使われます。それが「Huffman(ハフマン)符号化」です。
これも身近な例えで考えてみましょう。
日本語の文章で、「あ」や「の」という文字はすごくたくさん使いますが、「ゐ」や「ゑ」はめったに見かけませんよね。
もし文字コードを決めるなら、
- よく使う「あ」には、短く「1ビット」を割り当てる
- めったに使わない「ゐ」には、長ーく「10ビット」を割り当てる
このように、「出現頻度が高い文字には短いビットを、頻度が低い文字には長いビットを割り当てる」ことで、全体のデータ量を文字通りダイエットさせるのがハフマン符号化の仕組みです。
HPACKの中では、英語のアルファベットや記号の出現確率があらかじめ計算されており、最適化されたハフマン木(符号の割り当て表)が使われています。人間が「よし、ここは圧縮しよう」と考える暇もなく、パケットのコンマ数ミリ秒の裏側で、この緻密なパズルが高速に解かれているんです。
—
4. 実務の現場でどう見える?(検証・デバッグの視点)
「へえ、裏でそんな風に小さくしてくれているんだなぁ」で終わらせないのが、頼もしいエンジニアの第一歩です。実際にブラウザの開発者ツール(F12キーなど)を覗いて、通信の様子を確認してみましょう。
Google Chromeのデベロッパーツールを開き、「Network(ネットワーク)」タブの歯車アイコンから `Protocol` や `Size` の列を表示させてみてください。
例えば、以下のような疑似的なHTTP/2のレスポンスヘッダーのやり取りをイメージしてみましょう。
【ブラウザから送られるリクエスト(イメージ)】
初回リクエスト:動的テーブルが空なので、フルで送信しつつテーブルに登録する
:method: GET
:path: /index.html
:scheme: https
:authority: example.com
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)… ➔ 【ここで動的テーブルに登録!】
2回目以降のリクエスト:動的テーブルや静的テーブルの「番号」に置き換わり、驚くほどスリムに!
(内部的なパケット表現のイメージ)
[静的テーブル番号2番] ➔ :method: GET
[動的テーブル番号62番] ➔ user-agent: Mozilla/5.0…
ブラウザの「Response Headers」を見ると、何事もなかったかのように綺麗なプレーンテキストで表示されますが、これはネットワーク上を流れる際にHPACKによって極限まで圧縮・復元された結果です。
もし、NginxやApacheなどのWebサーバー、あるいは自作のプロキシサーバーを構築する機会があれば、ログ出力やパケットキャプチャツール(Wiresharkなど)を使って、実際のバイナリデータがどのようにエンコードされているかを覗いてみるのも最高にエキサイティングですよ!
—
まとめ
いかがでしたでしょうか?
HTTP/2のHPACKがやっていることは、突き詰めれば「お互いに共通の辞書(テーブル)を作って、よく使うものは短い番号で呼び合い、さらに文字の出現頻度に合わせてスマートにスリム化する」という、非常に人間らしくて合理的な工夫の積み重ねです。
- 静的テーブル:最初から用意されたお決まりの便利帳
- 動的テーブル:通信しながらインクリメンタル(段階的)に育っていくマイ辞書
- Huffman符号化:文字の出現頻度を見極めて、ビット単位で賢くダイエット
こうしたインフラ層の地道な最適化の積み重ねがあるからこそ、私たちは現代のリッチでストレスのないWeb体験を享受できています。
難解に見えるプロトコルも、一歩ずつ構造を紐解いていけば、まるでよくできたパズルのように美しく見えてくるはずです。ぜひ、今日の帰り道にでも、ブラウザの向こう側で飛び交うパケットたちのドラマに思いを馳せてみてくださいね。それでは、また次回の技術探検でお会いしましょう!
コメント