Real World HTTPを読んだので備忘録がわりにまとめた。
本の概要
本書の軸になっているのは
- HTTPの変遷とその機能の説明
- HTTPを取り巻く技術の紹介
- それらをGO言語のサンプルコードを用いて実装レベルでの説明
になっていた。
本書が実現したことは二つあります。一つ目はHTTPについてのイメージを読者に掴んでもらい、今後のHTTPやHTMLの新しい技術が出てきてもキャッチアップできる基礎を学ぶことです。(中略)もう一つが、外部のウェブサービスを利用するコードや、ウェブサービスをプログラミングしたり、それらのコードのテストを行えるようになることです。言い換えれば「手を動かせるようになる」ことです。
ここにあるように、HTTPの進化を辿ることで幹となる部分を捉えることができ、またサンプルコードを試しながら実装レベルで理解ができた。
以下に、印象に残った箇所をまとめる。
HTTPの歴史
HTTPとはWebブラウザとWebサーバが通信する時の手順とフォーマットをルール化したもの。
バージョンアップし続けている。
- HTTP/0.9: 1990年
- HTTP/1.0: 1996年
- HTTP/1.1: 1997年
- HTTP/2.0: 2015年
HTTP/0.9
- 検索のリクエスト(GET)しかできない
- ヘッダー、ステータスコード無し
- レスポンスはHTMLドキュメントのみ
HTTP/1.0
- リクエスト時にメソッドが追加された(GET)
- リクエスト時にHTTPバージョンが追加された(HTTP/1.0)
- ヘッダーが追加された(Host, User-Agent, Accept)
HTTP/1.1
- keep-alive
- Serverとのコネクションを接続したままにして再利用する
- TLSへの対応
- PUTとDELETEメソッドの標準化
HTTP/2.0
- ストリームによる通信の並列化
- ヘッダの圧縮
ヘッダー
ヘッダーは、「フィールド名:値」という形式で本文の前に付加され、本文のメタ情報を表現する。
リクエスト、レスポンスで代表的なヘッダーフィールドは以下のようなものがある。
- リクエストのヘッダーフィールド
- User-Agent: クライアントのアプリケーション名
- Referer: クライアントがリクエストを送る時に見ていたURL
- Authorization: 認証情報(Basic/Digest/Barear など)
- レスポンスのヘッダーフィールド
- Content-Type: ファイルの種類でMIMEタイプという識別子を記述する(MIMEタイプによってOSが挙動を変える)
- Content-Length: ボディのサイズ
- Date: ドキュメントの日時
ボディ
- HTTP/1.(0|1)
- クライアントが指定したコンテンツがそのまま格納される。
- レスポンスではボディを指定サイズ(Content-Length)分読み込むだけ。
- データの送信形式
- x-www-form-urlencoded
- Content-Type: application/x-www-form-urlencoded
- キーと値がイコール(=)で結ばれた(&)文字列になる(キーに対して値が1:1の情報しか持てない)
- RFCの形式に応じて=, &などはエスケープされる
- multipart/form-data
- Content-Type: multpart/form-data
- 一度のリクエストで複数のファイルを送信する
- 境界文字列で区切られる
- ファイルごとに追加のメタ情報をタグとして持てる
- x-www-form-urlencoded
認証とセッション
basic認証とdigest認証
- basic認証
- ユーザ名とパスワードをbase64エンコーディングして送信、サーバサイドで復元する
- base64は可逆変換
- digest認証
- クライアントとサーバでそれぞれユーザ名とパスワードを組み合わせた文字列のハッシュ値を取って一致するか確認する
- ハッシュ関数の一方向性を利用
- basic認証とdigest認証があまり使われてない理由
- ログインせずに同じページを閲覧する(ゲストアクセス)ができない
- リクエストごとにユーザ名とパスワードを送信し、認証する必要が多く計算量がかかる
- ブラウザの専用ログインフォームを使う必要があり、ログイン画面をカスタマイズできない
クッキーを使ったセッション管理
- 手順
- 最初のログインでユーザ名とパスワードをHTTPSで送信
- サーバ側で認証し、問題がなければセッショントークンを発行
- DBやKVSにユーザレコードのIDとセッショントークンを関連付けておく
- クライアントはそれをレスポンスで受け取ってそれをクッキーに保存する
- 以降のリクエストはセッショントークンを付与して認証する
TLS
- 共通鍵方式
- 暗号化と複合の鍵が同じ
- 鍵をクライアントとサーバでのみ共有する必要がある(鍵配送時に第三者に対して秘匿する必要がある)
- 相対的に計算量がかなり少ない
- 公開鍵方式
- 暗号化と復号の鍵が異なる
- 暗号化用の鍵は秘匿する必要がない
- 相対的に計算量がかなり多い
- 手順
- 鍵の配送先の信頼性の確認: サーバ証明書
- クライアントがサーバの持つサーバ証明書を取得
- サーバの公開鍵でサーバ証明書を暗号化しハッシュ値をとり、一致するか確認する
- 鍵配送の安全性の確認: 公開鍵方式
- 通信ごとにクライアントが共通鍵を生成
- サーバの公開鍵を使って暗号化し、共通鍵を秘匿して配送
- 配送されると共通鍵で通信を暗号化、復号する
- 鍵の配送先の信頼性の確認: サーバ証明書
チャンク
- チャンク方式
- HTTP/1.1からサポート
- データを小分けにして送信する方式
- ストリーミングダウンロード/アップロードとも呼ばれる
- ヘッダー
- 末尾にもヘッダーを追加する
- ボディ
- 16進数のファイルサイズと指定サイズ分のデータが続く
- 最後に0を送信すると転送が全て終わったことを示す
HTTP/1.1の応用技術
- ファイルのダウンロード
- XMLHttpRequest
- RPC
XMLHttpRequest
- XMLHttpRequest
- クライアントからJavascriptを使ってHTTP通信を可能にするブラウザ組み込みのオブジェクト
- ブラウザのHTTPリクエストの違い
- 送受信時にHTMLの画面がリロードされない
- 画面のクリアをせずに表示を更新するアーキテクチャーであるAjaxに応用された
- メソッドとしてGETとPOST以外も送信できる
- プレーンテキスト、JSON、バイナリデータ、XMLのフォーマットが送受信できる
- 送受信時にHTMLの画面がリロードされない
RPC
- リモートプロシージャコール
- 別のサーバにある機能をあたかも自分のサーバ内の処理のように呼び出し、実行する仕組み
- JSON-RPC
- JSONを使ったリモートプロシージャコール
- 呼び出しメソッド名は文字列で、パラメータも記述できる
- 返り値は入力に使用したidと値そのものがresultとして格納される
RESTとRPC
- REST(REpresentational State Transfer) とは
- 別のサーバにあるプログラムの呼び出しのインターフェースの設計原則の一つ
- リクエストのセマンティクス(意味の持たせ方)をHTTPに習うようにするという設計原則
- クライアント視点から見たREST
- URLはリソースの階層を表したパスになっている
- URLは名詞のみで構成されている
- リソースに対してHTTPメソッドを送ることでリソースの取得、更新、追加などの操作を行う
- ステータスコードを見ればリクエストが正しく処理されたどうかが分かる
- RPCの設計原則
- URLでオブジェクトのインスタンスを発見し、指定したメソッドと引数で呼び出すというアナロジーになっている
- RPCのRESTとの違い
- リソース一つに対して、呼び出しの受け口はURL一つ
- HTTPメソッドは全てPOST
- ボディでメソッドと引数を指定するため
HTTP/2.0の応用技術
- ストリーム
- fetchAPI
ストリーム
- HTTP1.1まで
- 一つのリクエストがTCPのソケットを専有してしまうので、一つのオリジンサーバに対して2~6本のTCP接続を行って並列化していた
- HTTP2.0
- 1本のTCP接続の内部にストリームという仮想のTCPソケットを作って通信するので、TCP接続のハンドシェイクをまとめられる
-
通信の多重化の概要
- ストリーム
- 従来のHTTPでは、リクエストとレスポンスの組を1つずつしか同時に送受信できない
- HTTP/2では1つの接続上にストリームと呼ばれる仮想的なリクエストとレスポンスの双方向シーケンスを作る
-
理論的なリクエストとレスポンスであるメッセージをまとめて優先順位をつけることができる
- 各ストリームには、1~256 の整数の重みを割り当てることができる
- 各ストリームには、別のストリームに対して明示的な依存関係を指定できる
- メッセージ
- 論理的なリクエストまたはレスポンスがマッピングされたデータ
- 複数のフレームによって構成される
- フレーム
- 従来のHTTPではリクエストやレスポンスのフォーマットとしてテキストを使用してきた
- HTTP/2.0では「フレーム」と呼ばれるバイナリ形式のフォーマットを採用している
- フレームには従来のHTTPのリクエストやレスポンスの内容は、全てフレームにマッピングされる
- ヘッダー => HEADERSフレーム
- ボディ => DATAフレーム
- コネクション > ストリーム > メッセージ > フレーム の入れ子構造になっている
- コネクション:ストリーム = 1:N
- ストリーム:メッセージ = 1:N (複数のリクエストとレスポンス)
- メッセージ:フレーム = 1:N (リクエストもしくはレスポンスごとの構成要素)
- ストリーム
これがわかりやすい
ストリーム: 確立した接続におけるバイト単位のデータの双方向フロー。1 つ以上のメッセージを伝送する場合があります。
メッセージ: 論理的なリクエストまたはレスポンス メッセージにマッピングする、フレームの完全なシーケンス。
フレーム: HTTP/2 での通信の最小単位。それぞれフレーム ヘッダーが含まれ、少なくともフレームが属するストリームを識別します。
これらの用語の関係は、次のようにまとめることができます。
すべての通信は、任意の数の双方向ストリームを伝送できる 1 つの TCP 接続で実行されます。
各ストリームには、双方向メッセージの伝送に使用する一意の識別子とオプションの優先順位情報が存在します。
各メッセージは、リクエスト、レスポンスなどの論理 HTTP メッセージであり、1 つ以上のフレームで構成されます。
フレームは、HTTP ヘッダー、メッセージ ペイロードなど、特定のタイプのデータを伝送する通信の最小ユニットです。 さまざまなストリームのフレームが混在し、各フレームのヘッダーに組み込まれたストリーム識別子を使って再構築される場合があります。
-
フロー制御
- コネクション単位の送信できるデータサイズ(WINDOW SIZE)のなかでストリームのデータサイズが割り当てられる
- そのサイズの一杯までデータを詰め込めるのでコネクションのオーバヘッドが減りパフォーマンスが改善する
fetchAPI
- FetchAPIとは
- XMLHTTPRequestをより柔軟で拡張したクライアントからHTTP通信を行う関数
- 主な特徴
- Javascriptのモダンな非同期処理のPromiseに準拠している
- キャッシュを制御できる
- Service Worker 内から利用できる
ブラウザが Web ページとは別にバックグラウンドで実行するスクリプト
オフラインのアプリを実現・サポートするために作られたもの
- Service Workerの使い方
- register関数でコールバックを定義したファイルを登録する
- それが受け取ったイベントごとのコールバックをaddEventListener関数で登録する
navigator.serviceWorker.register('/service_worker.js')
// service_worker.js
self.addEventListener('install', (event) => {
// 処理
});