HTTPの比較
HTTPの仕様についてわかりやすくまとまっている記事やブログはたくさんあるけど、
読んでも実感がわかない。
ということで、
HTTP/1.0とHTTP/1.1をWSLからcurlで叩いてWiresharkでキャプチャし、その様子を見比べてみる。
HTTP/1.0とHTTP/1.1それぞれhttpとhttps両方やったが、気が向いたのでおまけにHTTP/2もチラ見。
今回は、バージョンごとの仕様の違いをわかりやすくするため以下のような感じに二個連続でリクエストを送る。
curl http://example.com/ http://example.com -v --http1.0
pcapやログ等は下記のリポジトリ。
ちなみに、筆者はHTTPの仕様などは全く知らないので悪しからず・・・
HTTP/1.0 (http)
curl http://example.com/ http://example.com -v --http1.0
全体はこんな感じ。
DNSから始まってTCPやHTTPで通信している。
細かく見ていこう。
DNS
まずはDNS。
WSL(172.17.32.52)からホストPC(172.17.32.1)へDNS問い合わせをしている。
A
がIPv4、AAAA
がIPv6でそれぞれリクエストとレスポンスで4行。
3ウェイハンドシェイク
DNSでIPアドレスがわかったので、次は3ウェイハンドシェイクでコネクションの確立。
SYN
, SYN/ACK
, ACK
がしっかり見える。(ちょっと感動した)
データ転送
コネクションの確立ができたので、次は実際にデータ転送。
しっかりとリクエストとレスポンスが確認できる。
コネクション切断
データ転送がすんだのでコネクションを接続する。
FIN/ACK
, ACK
, FIN/ACK
, ACK
でコネクションが終了。*1
これで1つのリクエストが終わった。
二個目
curlでは2つURLを指定したのでもう1回同じのがある。
DNSのやり取りがないだけで、3ウェイハンドシェイクとデータ転送とコネクションの切断と、
一回目とほぼ同じやりとりをしていることがわかる。
HTTP/1.1 (http)
1.0より効率良くやり取りするらしい。
curl http://example.com/ http://example.com -v --http1.1
全体はこんな感じ
HTTP/1.0より少し行数が少ない。
詳細
DNSと3ウェイハンドシェイク、データ転送部分はHTTP/1.0と同じ。
ここからHTTP/1.0と違ってくる。
HTTP/1.0ではここでFIN/ACK
とかしてコネクション切断したあとSYN/ACK
とかして次のコネクションを確立していたが、
HTTP/1.1では「レスポンスを受け取ったよ」というACK
を送信した後、
コネクションを切断せずにそのまま2個目をやり取りする。
これより後はまたHTTP/1.0と同じようにコネクションの切断をする。
HTTP/1.1の良い点
この比較において、HTTP/1.1が1.0より高速化してると確認できる要素は以下。
連続したリクエストに対して、前回のコネクションを再利用する
Keep-Alive
がデフォルトで有効になったおかげ。
一個目のリクエストのFINと二個目のリクエストのSYNが無いのが確認できる
HTTP/1.0 (https)
HTTPだけでは物足りなかったので、HTTP/1.0をHTTPSでも見てみる。
(HTTPSの暗号化云々とかはわからないので流します)
SSLKEYLOGFILE=/tmp/secret.log curl https://example.com/ https://example.com -v --http1.0
SSLKEYLOGFILEを
設定>Protocols>TLS>Master-Secret log filename
からインポートすればHTTPSの中身が見える
パケットの量がめっちゃ増えた。スクショに写りきってないが、48行ある。
でも、暗号化云々のパケットが増えただけでやってることはあまり変わってない。
DNS
まずはDNS問い合わせ。
3ウェイハンドシェイク
さっきまでと色が違う。
あと、HTTPSなので宛先ポートが443。
でもやってることは同じで、SYN
, SYN/ACK
, ACK
。
SSL/TLSハンドシェイク
Client Hello
, Hello Retry Request, Change Cipher Spec
, Change Cipher Spec, Client Hello
, Server Hello, Encrypted Extensions
, Certificate, Certificate Verify, Finished
, Finished
などの通信でTLS 1.3のハンドシェイクをしてる。
このハンドシェイクはClientHelloのkey_shareが不正らしく、
Hello Retry Request
等が入ってきた。
本来のハンドシェイクはもうちょっとシンプル。
データ転送
GETしてNew Session Ticket
とかの後にレスポンスが返ってくる。
コネクション切断
Alert
とかFIN
とか、コネクションの切断周りの物がある。
二個目
これで一個目のリクエストは終わりで、二個目はやはりDNSが無いだけで同じことをしている。
HTTP/1.1 (https)
34行。やってることはあまり変わらないので、要点だけ見る。
データ転送
HTTP/1.1はコネクションの再利用をするので、一個目のFINと二個目のTLSv1.3ハンドシェイクが不要になる。
結果、HTTP/1.0より14行も少なくなっている。凄い。
おまけ
HTTP/2 (http)
curl http://example.com/ http://example.com -v --http2
HTTP/2をhttpでやってみようとしたところ、良く見るとHTTP/1.1になっている。
少し調べたところ「HTTP/2はTLSのみ」みたいなことが書いてあったので、HTTPSでやってみる。
HTTP/2 (https)
DNSとTLSv1.3ハンドシェイクは同じ。
データ転送
「Magic
?SETTINGS
?WINDOW_UPDATE
?なんだこれ・・・」
今までとは全然違う、見たことないような通信をしている。
今回はHTTP/2の流れを理解するのは目標ではないので、とりあえずチラ見だけ。
HEADERS
と[TLS segment of a reassembled PDU]
、そしてDATA
は見覚えのある内容があった。
ヘッダとボディがバラバラになっているのは面白い。
他のストリーム設定用の通信等はあんまり理解できなかった。
まだ自分はHTTP/1.1で感動するレベルだったので、そのうちHTTP/2も勉強してみたい。