ヘッダーとボディ — 伝票を読み終えてから、箱を受け取る
宅急便の荷物には、伝票が貼ってあります。宛先、差出人、中身の種類、「ワレモノ」「冷蔵」の指示。配達員は伝票だけを見て荷物を仕分け、箱を開けることはありません。
ネットワークの通信も同じ形をしています。前半のヘッダーが伝票、後半のボディが中身のメロン。図1で、届いたHTTPレスポンスを1行ずつ読んでみてください。受け取る側がどこで伝票を読み終え、どうやって中身の終わりを知るのかが見えてきます。
伝票は1行ずつ、「名前: 値」で書く
HTTPのヘッダーは、「名前: 値」の行を並べただけの単純な形です。受け取る側は上から1行ずつ読み、知っている名前なら意味を解釈し、知らない名前は読み飛ばします。だから新しいヘッダーを足しても、古い機器が壊れないのです。
伝票の終わりは空行で示します。図1で空行を抜いて送ると、受け取る側はメロンの中身まで伝票だと思って読み、「名前: 値」になっていないので行き詰まります。空行は、たった1行で伝票と箱の境目を担っているのです。
中身の終わりは、伝票に書いてある
ボディには決まった書式がありません。HTMLでも画像でも動画でも、ただのバイト列です。だから受け取る側は、中身を読んで終わりを探すことができません。代わりに伝票の Content-Length に何バイトあるかが書いてあり、その数だけ受け取ったら「箱は終わり」と判断します。
ここが分業の要です。途中の中継役(プロキシやCDN)は、伝票だけ読めば仕事ができます。「保管してよい期間」を見てキャッシュし、「中身の大きさ」を見て転送する。メロンの箱を開ける必要はありません。
同じ中身でも、伝票しだいで扱いが変わる
ボディがただのバイト列だということは、それをどう解釈するかは伝票が決めるということです。その役目を持つのが Content-Type。図2で、中身はまったく同じまま伝票だけを書き換えてみてください。
text/html なら <b> を太字の指示として解釈し、text/plain なら記号ごと文字として表示し、application/octet-stream なら「よく分からないファイル」として保存を促す。中身は同じなのに、結果はまるで違います。
APIでJSONを返したのに text/html と書いてしまい、受け取った側が正しく扱えない――というのはよくある不具合です。中身を正しく作るのと同じくらい、伝票を正しく書くことが大切なのです。
- ヘッダー
- 通信の前半にある「名前: 値」の行の並び。宛先や中身の種類など、扱い方の指示。
- ボディ
- ヘッダーの後ろに続く本体のデータ。書式は決まっておらず、ただのバイト列。
- 空行
- ヘッダーの終わりを示す、何も書かれていない1行。
- Content-Type
- ボディの種類。受け取った側はこれを見て解釈のしかたを決める。
- Content-Length
- ボディの大きさ(バイト数)。受け取る側はこれで中身の終わりを知る。
まとめ
通信は伝票(ヘッダー)と中身(ボディ)に分かれています。伝票は1行ずつ読み、空行で終わり。中身は伝票に書かれた大きさぶんを丸ごと受け取り、伝票に書かれた種類として扱う。中継役は伝票だけで仕事ができ、受け取り手は伝票を信じて箱を開ける。この分業が、どんな中身でも同じ仕組みで運べる理由です。