HTTP は、HTML ドキュメントなどのリソースを取り出すことを可能にするプロトコルです。これはウェブにおけるデータ交換の基礎をなすクライアントサーバープロトコルであり、リクエストは受け取り者 (一般にはウェブブラウザー) が生成します。テキスト、レイアウトの定義、画像、動画、スクリプトなど、取り込まれたさまざまなサブドキュメントから、完全なドキュメントが再構成されます。
クライアントとサーバーは、(データの流れとは対照的に) 個々のメッセージを交換することによって通信します。クライアント (通常はウェブブラウザー) が送信するメッセージはリクエスト (要求) と呼ばれます。また、サーバーが回答として送信するメッセージはレスポンス (応答) と呼ばれます。
1990 年代初頭に設計された HTTP は、時間をかけて進化した拡張可能なプロトコルです。HTTP は、TCP または TLS (暗号化された TCP 接続) を使用して送信されるアプリケーション層のプロトコルですが、理論上は信頼性のある任意のトランスポート層プロトコルを使用できます。HTTP は拡張性があるため、ハイパーテキストドキュメントだけでなく画像や動画の取り込みや、HTML フォームの入力結果などをクライアントからサーバーへ送信することもできます。また、要求に応じてウェブページを更新するために、ドキュメントの一部を取り込むこともできます。
HTTP ベースシステムの構成要素
HTTP はクライアントサーバープロトコルであり、リクエストはユーザーエージェント (または代理のプロキシ) というひとつの実体から送信されます。ほとんどの場合、ユーザーエージェントはウェブブラウザーですが、例えば検索エンジンのインデックスを収集および保守するためにウェブをクロールするロボットなど、どれでもクライアントになることができます。
個々のリクエストはサーバーに送信され、処理した後に レスポンス と呼ばれる回答を提供します。リクエストとレスポンスの間には、まとめてプロキシとして設計された多くの実体が存在しており、これらはさまざまな処理を行って、例えばゲートウェイやキャッシュとして同際します。
実際はブラウザーとサーバーの間に、ルーターやモデムなどリクエストを扱うコンピューターがさらに存在します。ウェブが階層構造で設計されたおかげで、これらはネットワークやトランスポート層の中に隠されています。HTTP はアプリケーション層の最上位に存在します。ネットワークの問題を診断することは重要ですが、HTTP を説明する際に下層のことはほとんど重要ではありません。
クライアント: ユーザーエージェント
ユーザーエージェントは、ユーザのために働くツールです。この役割は主に、ウェブブラウザーが担います。ただし、エンジニアや自身のアプリケーションをデバッグするウェブ開発者が使用するプログラムのような例外もあります。
ブラウザーは常に、リクエストを生成する実体です。サーバーにはなりません (もっとも、サーバーが生成するメッセージをシミュレートする仕組みが近年追加されましたが)。
ウェブページを提供するため、ブラウザーはページから HTML ドキュメントを取り込む最初のリクエストを送信します。このファイルを解析して、実行するスクリプト、表示するレイアウトの情報 (CSS)、ページに含まれるサブリソース (通常、画像や動画) に対応する追加のリクエストを発行します。そして、ウェブブラウザーはこれらのリソースを混ぜ合わせて、完全なドキュメントであるウェブページをユーザーに提供します。ブラウザーによって実行されるスクリプトが後の段階でさらにリソースを取り込んで、それに応じてブラウザーがウェブページを更新することがあります。
ウェブページは、ハイパーテキストドキュメントです。これは表示されているテキストの一部が新たなウェブページの取り込みを (通常、マウスのクリックによって) 発生させるリンクであり、ユーザーがユーザーエージェントを導いてウェブ内を移動できるということです。ブラウザーはこれらの導きを HTTP リクエストに変換して、さらにユーザーへ明確な応答を返すために HTTP レスポンスを解釈します。
ウェブサーバー
通信路の反対側には、クライアントの要求に応じてドキュメントを提供するサーバーがいます。サーバーは、実質 1 台だけのマシンとして見えます。これは、実際は複数のサーバーの集合体であるかもしれないためであり、これらは負荷 (負荷分散)、あるいは他のコンピューター (キャッシュ、データベースサーバー、電子商取引サーバーなど) に問い合わせを行う複雑なひとまとまりのソフトウェアを分け合って、要求に応じて全面的または部分的にドキュメントを生成しています。
サーバーは 1 台のマシンである必要性はありませんが、複数のサーバーを同じマシンで運用することができます。HTTP/1.1 と Host
ヘッダーによって、同じ IP アドレスを共有できます。
プロキシ
ウェブブラウザーとウェブサーバーの間で、多数のコンピューターやマシンが HTTP メッセージを中継するします。ウェブスタックの階層構造のため、これらの処理のほとんどはトランスポート層、ネットワーク層、物理層で行われ、HTTP 層では透過的であり、パフォーマンスにかなりの影響を与えます。アプリケーション層で行われる処理は、通常プロキシと呼ばれます。これらは透過的である場合とそうでない場合 (プロキシを通過するときにリクエストを変更するか否か) があり、さまざまな機能を実行します:
- キャッシュ (キャッシュは共用、あるいはブラウザーキャッシュのように個人用にできます)
- フィルタリング (アンチウィルススキャンやペアレンタルコントロールなど)
- 負荷分散 (複数のサーバーが別々のリクエストに対応できるようにする)
- 認証 (さまざまなリソースへのアクセスを制御する)
- ログ記録 (履歴情報の保管を可能にする)
HTTP の基本方針
HTTP はシンプル
HTTP メッセージをフレーム内にカプセル化することにより HTTP/2 で複雑さが増したにもかかわらず、HTTP は全体的にシンプルで人間が読めるように設計されています。HTTP メッセージは人間が読んで理解することができ、開発者によるテストを容易にしています。また、初心者に対する複雑さも軽減します。
HTTP は拡張可能
HTTP/1.0 で導入した HTTP ヘッダー は、プロトコルの拡張や実験を容易にしました。新しい機能でも、クライアントとサーバーが新たなヘッダーの意味について単純に合意すれば導入できます。
HTTP はステートレスであるがセッションレスではない
HTTP はステートレスです。同じコネクション上であってもそうでなくても、連続的に実行される 2 つのリクエスト間に関係性はありません。これは電子商取引のショッピングバスケットなど、ユーザーが一貫した方法でページと対話したいときに、さっそく問題になります。HTTP Cookie がワークフローに追加されるヘッダー拡張を使用して、同じ状況または状態を共有する個々の HTTP リクエストを作成するセッションを実現できます。
HTTP 自体の核心部分はステートレスですが、HTTP Cookie がステートフルなセッションを可能にします。
HTTP とコネクション
コネクションはトランスポート層で制御されますので、HTTP の範囲から根本的に外れています。ただし、HTTP は下層のトランスポートプロトコルがコネクションベースであることは要求しません。信頼性がある、またはメッセージがあいまいではない (すなわち、少なくともエラーは報告する) ことだけが必要です。インターネットでもっとも一般的な 2 つのトランスポートプロトコルでは、TCP が信頼性あり、UDP が信頼性なしです。後に HTTP は、常にコネクションが必要ではないとしても、コネクションベースである TCP 標準に依存しています。
HTTP/1.0 はそれぞれのリクエスト/レスポンスの交換に対して TCP コネクションを開いており、2 つの欠点が発生しました。コネクションを開くときにいくつかのメッセージのやり取りが必要であるために遅くなります。しかし、メッセージを複数送信したり定期的に送信したりすると効率的になっていきます。ウォーム状態のコネクションはより効率的、コールド状態のコネクションは非効率的です。
この欠点を軽減するため、HTTP/1.1 でパイプライン (実装が難しいことが立証されました) や持続的接続を導入しました。Connection
ヘッダーを使用して、下層の TCP コネクションを部分的に制御できます。HTTP/2 はひとつのコネクションに複数のメッセージを多重化するように進化しました。コネクションををウォーム状態に保つのを助けて、効率が向上します。
より HTTP に適したトランスポートプロトコルを設計する実験が進んでいます。たとえば Google は、より信頼性があり効率的なトランスポート層プロトコルを提供するため、UDP 上に構築する QUIC の実験を行っています。
HTTP の拡張性により時間をかけて、ウェブの制御性や機能性が向上できました。キャッシュや認証の方法は、HTTP の歴史の早いうちから制御されていた機能です。対照的に、オリジン制約を緩和する機能は 2010 年代にようやく追加されました。
HTTP で制御できる一般的な機能は以下のとおりです。
- キャッシュ
ドキュメントをどのようにキャッシュするかを、HTTP で制御できます。サーバーはプロキシやクライアントに対して、何をどれだけの間キャッシュするかを指示できます。クライアントは中間のキャッシュプロキシに対して、保存されているドキュメントを無視するよう指示できます。 - オリジン制約を緩和する
のぞき見や他のプライバシー侵害を避けるため、ウェブブラウザーはウェブサイト間を厳密に分割するよう強制しています。同一オリジンのページだけが、ウェブページの情報すべてにアクセスできます。この制約はサーバーに対して負担になるため、HTTP ヘッダーでサーバーサイドの厳密な分割を緩和できます。これにより、さまざまなドメインの情報ソースを寄せ集めたドキュメントを生成できます (このようにする、セキュリティ上の理由があるかもしれません)。 - 認証
特定のユーザーしかアクセスできないように保護されたページがあるでしょう。基本的な認証は HTTP が提供しており、Authenticate
などのヘッダーを使用するか、HTTP Cookie を使用した特別なセッションを設定します。 - プロキシとトンネリング
サーバーやクライアントがイントラネット内に配置されて、本当の IP アドレスがほかから見えなくなっていることがよくあります。このネットワーク境界を渡るため、HTTP リクエストはプロキシを通過します。すべてのプロキシが HTTP プロキシであるとは限りません。たとえば、より低レベルで動作する SOCKS プロトコルがあります。ほかにも FTP など、それらのプロキシで処理されるものがあります。 - セッション
HTTP Cookie を使用して、リクエストとサーバーのセッションを関連付けできます。これにより HTTP がステートレスプロトコルであるにもかかわらず、セッションを作成できます。これは電子商取引のショッピングバスケットだけでなく、出力内容にユーザー設定を適用できるサイトでも有用です。
HTTP のフロー
クライアントがサーバー (最終目的地のサーバーまたは中間のプロキシ) と通信したいとき、クライアントは以下の段階を踏みます:
- TCP コネクションを開く: TCP コネクションはひとつまたは複数のリクエストを送信したり、回答を受け取ったりするために使用します。クライアントは新しいコネクションを開く、既存のコネクションを再使用する、あるいはサーバーに対して複数の TCP コネクションを開くことができます。
- HTTP メッセージを送信する: HTTP メッセージを (HTTP/2 より前) は人間が読むことができます。HTTP/2 では単純なメッセージがフレーム内にカプセル化されており、直接読むことが不可能になりましたが、原理は変わっていません。
GET / HTTP/1.1 Host: developer.mozilla.org Accept-Language: fr
- サーバーから送信されたレスポンスを読み取る:
HTTP/1.1 200 OK Date: Sat, 09 Oct 2010 14:28:02 GMT Server: Apache Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT ETag: "51142bc1-7449-479b075b2891b" Accept-Ranges: bytes Content-Length: 29769 Content-Type: text/html <!DOCTYPE html... (ここに、要求した 29769 バイトのウェブページがある)
- 次のリクエストのために、コネクションを閉じるか再使用する
HTTP パイプラインが有効である場合は、最初のレスポンスが完全に返るのを待たずに複数のリクエストを送信できます。HTTP パイプラインは既存のネットワークで実装するのが難しいことが立証されており、古いソフトウェアと最新バージョンのソフトウェアが共存しています。HTTP パイプラインは、HTTP/2 のフレーム内にリクエストを強力に多重化する機能によって置き換えられました。
HTTP メッセージ
HTTP/1.1 以前の HTTP メッセージは、人間が読むことができます。HTTP/2 ではこれらのメッセージが新たなバイナリ構造に埋め込まれており、ヘッダーの圧縮や多重化といった最適化が可能になりました。本来の HTTP メッセージの部分だけがこのバージョンの HTTP で送信されていても、各メッセージの意味は変わっておらず、クライアントは本来の HTTP/1.1 メッセージを (事実上) 再構成します。したがって、HTTP/2 メッセージを HTTP/1.1 形式で理解することは役に立ちます。
HTTP メッセージはリクエストとレスポンスの 2 種類あり、それぞれ固有の形式になっています。
リクエスト
HTTP リクエストの例です:
リクエストは以下の要素で構成されます:
- HTTP メソッド。通常、クライアントが実行したい操作を定義する
GET
やPOST
のような動詞か、OPTIONS
やHEAD
のような名詞です。一般的にクライアントはリソースを取り込む (GET
を使用) か HTML フォーム の値を送信する (POST
を使用) ことを望みますが、場合によってはほかの操作が必要になります。 - 取り込むリソースのパス。状況から明らかであればリソースの URL はこの要素から取り除かれます。たとえばプロトコル (
https://
)、ドメイン (ここではdeveloper.mozilla.org
)、TCP ポート (ここでは80
) が取り除かれます。 - HTTP プロトコルのバージョン。
- サーバーに追加の情報を与える任意の ヘッダー。
POST
のようなメソッドではレスポンスと同様に、送信するリソースを包含したボディがあります。
レスポンス
レスポンスの例です:
レスポンスは以下の要素で構成されます:
- 準拠する HTTP プロトコルのバージョン。
- ステータスコード。リクエストが成功したか否か、およびその理由を示します。
- ステータスメッセージ。ステータスコードの簡単な説明ですが、権威はありません。
- リクエストと同様の HTTP ヘッダー。
- 省略可能ですがリクエストより一般的に、取り込んだリソースを包含するボディがあります。
まとめ
HTTP は容易に使用できる、拡張可能なプロトコルです。クライアントサーバー構造と単純にヘッダーを追加できる機能性を組み合わせて、HTTP はウェブの機能拡張に合わせて進化できます。
HTTP/2 でパフォーマンスを向上するため、フレーム内に HTTP メッセージを埋め込むことにより複雑さがいくらか増しましたが、メッセージの基本的な構造は HTTP/1.0 から同じままです。セッションのフローは依然として単純であり、フローの調査やシンプルな HTTP メッセージモニター でデバッグすることができます。