HTTP 1.1 以前の実装については触れません。なぜなら、すでに時代遅れで、私がインターネットを利用していた時には接触できなかったからです。主に HTTP/1.1 と HTTP/2 について説明します。
HTTP/1.1#
HTTP/1.1 プロトコルメッセージの概要#
CRLF:
\r\n
METHOD: HTTP リクエスト,
GET
、POST
、PUT
、DELETE
...URI: 統一リソース識別子,例えば
/
,/index.html
...HTTPVersion: HTTP プロトコルのバージョン番号,例えば
HTTP/1.1
,HTTP/2
HEADERS: リクエストヘッダー,例えば
Host:localhost
,Accept: */*
.BODY: リクエストボディ,例えば JSON データ
{"name":"fzdwx"}
HTTPStatus: HTTP レスポンスステータス,一般的なものに
200
,404
などがあります。HTTPStatusDesc: HTTP レスポンスステータスの説明,
200
に対応するOK
.
リクエスト#
METHOD<SPACE>URI<SPACE>HTTPVersion
HEADERS
<CRLF>
BODY
例:
GET /hello HTTP/1.1
Host: 192.168.1.107:8889
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
レスポンス#
HTTPVersion HTTPStatus HTTPStatusDesc
HEADERS
<CRLF>
BODY
例:
レスポンスで
transfer-encoding: chunked
を使用してContent-Length
を置き換えた場合、これはサイズが不定のレスポンスであることを示し、通常は0\r\n
で区切られます。
HTTP/1.1 200 OK
transfer-encoding: chunked
content-type: application/json; charset=utf-8
0/r/n
HTTP/1.1 の主な新機能#
- デフォルトは持続的接続 (
Connection: Keep-alive
)、1 つの TCP 接続で複数のリクエストを処理できます。 - キャッシュ戦略、リクエストヘッダーで
Cache-Control
,Expires
,Last-Modified
,ETag
などを使用して制御します。 - レスポンスをチャンク化することが許可されており、上記で言及した
transfer-encoding: chunked
により、サーバーが複数回レスポンスボディを返すことができます。
ただし、一定の問題も存在します。例えば、TCP 接続がブロックされた場合、新しい TCP 接続を開いてリクエストを処理します。
H2#
HTTP2 の主な概念:
Connection
: 1 つの TCP 接続は 1 つ以上のStream
を含み、すべての通信は1 つの TCP 接続上で行われます。Stream
: 双方向通信が可能なデータストリームで、1 つまたは複数のMessage
を含み、各データストリームには一意の識別子とオプションの優先度情報があります。Message
: HTTP/1.1 のリクエストまたはレスポンスに対応し、1 つまたは複数のFrame
を含みます。Frame
: 最小の伝送単位で、バイナリでエンコードされます。
HTTP/1.1 ではStart Line
+ header
+ body
で構成されていましたが、H2 ではHEADER Frame
と複数のDATA Frame
で構成されています。
Frame#
通常、いくつかの共通フィールドがあります。例えばLength
,Type
,Flags
およびStream Id
;また、各タイプに固有のフィールドもあります。
分類は以下の通りです:
- DATA: HTTP メッセージボディの伝送に使用されます。
- HEADERS: ヘッダーフィールドの伝送に使用されます。
- PRIORITY: リソースの優先度を指定または再指定するために使用されます。
- RST_STREAM: ストリームの異常終了を通知するために使用されます。
- SETTINGS: クライアントとサーバーの設定データを合意するために使用されます。例えば、初期の双方向フロー制御ウィンドウサイズを設定します。
- PUSH_PROMISE: サーバーからのプッシュ許可。
- PING: 往復時間を計算し、「アクティブ」チェックを実行するために使用されます。
- GOAWAY: 対向に現在の接続でストリームを作成しないよう通知するために使用されます。
- WINDOW_UPDATE: 特定のストリームまたは接続のフローを調整するために使用されます。
- CONTINUATION: 大きな HTTP ヘッダーを伝送する際の継続フレーム専用です。
なぜ H2 は HTTPS を使用する必要があるのか?#
これは H2 標準では規定されていませんが、主に HTTP プロトコルのアップグレード / ネゴシエーションをより便利に行うためです。Web サーバーが H2 をサポートしているかどうかを確認する方法は主に 2 つあります。
- リクエストヘッダーに
Upgrade: HTTP/2.0
およびConnection: Upgrade,HTTP2-Settings
などを設定し、Websocket
へのアップグレードに似ています。 TLS
のALPN
(Application Layer Protocol Negotiation、アプリケーション層プロトコルネゴシエーション)のALPN Next Protocol
フィールドを使用し、Client Hello
とServer Hello
の段階で確定します。
現在のブラウザは基本的に方法二を実装しており、つまりHTTPS に結びついています。ただし、ブラウザを使用せずにアクセスする場合は、もちろん HTTPS を使用しなくても構いません。
詳細は参考してください。
なぜ H2 は並行レスポンスリクエストを実現できるのか?#
HTTP/1.1 では、リクエストとレスポンスは一対一で対応しており、同じ接続内でクライアントが 2 つのリクエストを順次送信し、しばらく後にサーバーから 1 つのレスポンスを受け取ります。このレスポンスは必ず最初に送信されたリクエストに対応します。
なぜなら、どのレスポンスがどのリクエストに対応しているかを示すマークがないからです。
一方、H2 ではStream
とFrame
の設計に基づき、各Frame
にはStream Id
が付与され、同じStream
内のデータかどうかを識別します。各Stream
は互いに影響を与えず、これにより 1 つの TCP 接続内で複数のリクエスト / レスポンスを伝送できるようになります。
H2 の新機能#
H2 の HTTP/1.1 に対する最適化の核心は、可能な限り少ない接続数を使用することです。
- マルチプレクシング: 1 つの TCP 接続で複数のリクエスト / レスポンスを処理でき、別の TCP 接続を開く必要がありません。これは
Stream
とFrame
を通じて実現されます。 - バイナリ分割:
Frame
を最小単位として通信し、バイナリエンコーディングを使用します。 - ヘッダー圧縮:
HPACK
アルゴリズムを使用して最適化します。- 一般的なリクエストヘッダーの KV 組み合わせを含む静的辞書を維持します。
- 動的辞書を持ち、動的に拡張可能です(各接続が個別に管理)。
- ハフマンエンコーディングをサポートします(静的ハフマンコード表)。
HTTP/1 ではメッセージボディを gzip で圧縮できますが、リクエストヘッダーは通常圧縮されていません。時にはリクエストヘッダーのデータがメッセージボディのデータよりも多くなることがあります。
- リクエスト優先度:通常、
HEADERS
フレームとPRIORITY
フレームに含まれ、通常はサーバーのサポートレベルに依存します。
ツール#
テスト署名の生成#
go run $GOROOT/src/crypto/tls/generate_cert.go --host localhost
curl を使用して HTTPS をデバッグ#
curl https://zcygov.cn -vv