『Real World HTTP』という本を読んでいるが、とても面白い。
学んだことをここにメモする。
1.7 URL
- URL表記
スキーマ://ユーザ:パスワード@ホスト名:ポート/パス#フラグメント?クエリー
- URLを構成する文字はASCII文字列とされている
- 日本語などはUTF-8を使ってURLエンコードする
- ブラウザ上で日本語のURLが表示されるのはデコードしてくれているから
- HTTPの仕様上ではURL長の制限はないが、ブラウザ側で制限がかかっていることがある。
- 当初、URLのドメイン名は半角英数字とハイフンしか使えなかった
- Punycodeというエンコードルールに従えば日本語なども使える
- ただし、実際に送信されるドメイン名はPunycodeによって半角英数字(xn--から始まる文字列)に変換される
1.8 ボディ
- Content-Lengthヘッダにボディのバイト数が含まれている。ボディが圧縮されていた場合は圧縮後のバイト数
- GET,HEAD,DELETE,OPTIONS,CONNECTメソッドにボディを持たせること自体は可能だが非推奨
- サーバ側はメッセージボディを読み込めるようにしておくべきだがリクエストの処理時にはボディを無視すべき
- ボディを持つ場合、サーバから拒否される可能性もある
- TRACEメソッドは「ボディを含めてはいけない」と明記されている。
2.1 x-www-form-urlencoded
- シンプルなフォーム送信で利用
- リクエストヘッダに"Content-Type: application/x-www-form-urlencoded"を指定
- ボディは以下のように「キー=値」の形式で表現され、複数ある場合は「&」で連結される
name=k-noya&comment=hoge
- ブラウザで送信される場合、「アルファベット、数値、一部記号(*-._)」以外の文字はエスケープされる(RFC1866)
2.2 multipart/form-data
- ファイルも送信できるフォーム
- Content-Typeヘッダに以下を指定
Content-Type: multipart/form-data; boundary=境界文字列
- ボディは以下のように境界文字列で区切られた形式となる
- boundary=----WebKitFormBoundaryXXXXと仮定
- "-- + 境界文字列"で始まり、"-- + 境界文字列 + --"で終わる
------WebKitFormBoundaryXXXX
Content-Disposition: form-data; name="name"
k-noya
------WebKitFormBoundaryXXXX
Content-Disposition: form-data; name="comment"
hoge
----WebKitFormBoundaryXXXX--
- ファイル送信した場合は以下のような形式となる
------WebKitFormBoundaryXXXX
Content-Disposition: form-data; name="attachment-file"; filename="test.txt"
Content-Type: text/plain
Hello HTTP
------WebKitFormBoundaryXXXX--
2.4 コンテントネゴシエーション
- 1リクエストの中で、言語や圧縮方式などの設定をネゴシエートする仕組み
- クライアントはAccept系のヘッダを使って要望を伝える
- サーバはContent系のヘッダを使って結果を伝える
ネゴシエーション対象 | リクエストヘッダ | レスポンスヘッダ |
---|---|---|
MIMEタイプ | Accept | Content-Type |
表示言語 | Accept-Language | Content-Language or html内のタグ |
キャラクターセット | Accept-Charset | Content-Type |
ボディの圧縮方式 | Accept-Encoding | Content-Encoding |
2.8 キャッシュ
Last-Modified
- 最初はLast-Modifiedヘッダを使ってキャッシュの有効性を確認していた
- 毎回、If-Modified-Sinceで更新有無を確認しないといけない
Exipre
- 毎回確認しなくて良いように、Expiresヘッダが登場
- Expireするまでは更新確認不要になった
- ExpireしたらIf-Modified-Sinceで確認
Etag
- 動的なコンテンツ向けにEtagヘッダが登場
- ハッシュ値を利用するので、「同一URLでもユーザによってコンテンツが異なる場合」に効果的
- If-None-MatchヘッダにEtag値を付けて有効性を確認する。
Cache-Control
- Etagと同時期にCache-Controlヘッダが登場
- max-age=n, no-cache, no-storeなどキャッシュを制御する仕組みがある。
- no-cacheは毎回キャッシュが有効か確認させるもの。「キャッシュさせない」ではない。
- If-Modified-Since or If-None-Matchで毎回確認させる。
- キャッシュしないのはno-store
- Cache-Controlはクライアントからプロキシサーバへの要求時にも付加できる(一部のみ)。
Vary
- Etagで記載した「同一URLでもユーザによってコンテンツが異なる場合」などに使う
- キャッシュサーバはVaryヘッダを見てキャシュを使い分ける必要がある。
例えば、「スマホとPCでコンテンツが異なる」、かつ「ユーザの使用言語によってコンテンツが異なる」という場合には、サーバは以下のVaryヘッダをレスポンスに付加する。
Vary: User-Agent, Accept-Language
4.4 OPTIONS, TRACE, CONNECTメソッド
- OPTIONSメソッドはサーバが受付けるメソッド一覧を返す
- OPTIONSメソッドは拒否されることが多い(405 Method Not Allowed)
- TRACEメソッドは脆弱性があり、ほぼ使われない
- CONNECTはHTTPのプロトコル上に、他のプロトコルを流せるようにする
- https通信を中継する用途で使われることが多い
4.5 プロトコルのアップグレード
-
ヘッダを使ってHTTP以外のプロトコルにスイッチすることができる
-
WebSocketへの切り替えで使われることが多い
-
UpgradeヘッダおよびConnectionヘッダを使う
"Upgrade: websocket" "Connection: Upgrade"
-
アップグレードが可能であれば以下のようなレスポンスが返ってくる
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade
-
サーバからアップグレードを要求する場合は以下のレスポンスを返す
この場合もクライアントが改めてUpgradeのリクエストを送信するHTTP/1.1 426 Upgrade Required Upgrade: TLS/1.1 Connection: Upgrade
4.7 チャンク
-
データを分割して送信する方式
-
動画などサイズの大きいファイルを送信する場合にメモリを節約可能
-
クライアント側ではダウンロードした部分のみを表示するなどの対応が取れる
-
以下のようなレスポンスとなる
-
ボディは16進数のデータサイズから始まる
-
データサイズの後に実際のデータを送信
-
データサイズに0を指定することでデータ終了を通知
Content-Type: video/webm Transfer-Encoding: chunked Trailer: Content-Type 186a0 (1000KBデータ) 186a0 (1000KBデータ) 0
-
チャンク形式の場合のみ末尾(ボディの後)にヘッダを指定可能
-
Trailerヘッダで末尾に付与するヘッダを指定
Trailer: Content-Type
4.8 ボディ送信の確認
-
ボディ送信の前に受け入れ確認を行う2段階送信が可能になった
-
受け入れ確認には下記ヘッダを付与する必要がある
Expect: 100-continue
-
ボディは送信しないが、送信した場合のContent-Lengthヘッダは付与する
-
サーバから100 Continueが返ってきたらボディ込みで送信する
5.1 ファイルをダウンロードした後でローカルに保存
-
ブラウザがファイル種別を判定する際にはMIMEタイプを使う。
-
レスポンスにContent-Dispositionヘッダがあるとファイル保存する
Content-Disposition: attachment; filename=filenam.xlsx
-
RFC6266にしたがってエンコーディングするとUTF-8エンコードされたファイル名も使える。
"filename=filename.xlsx"は後方互換性のためContent-Disposition: attachment; filename*=utf-8''ファイル名.xlsx; filename=filename.xlsx
5.2 ダウンロードの中断、再開
- コンテンツの範囲指定を使うとダウンロードの中断と再開に対応できる
- サーバが範囲指定をサポートしているかはAccept-Rangesヘッダ(レスポンス)を見れば分かる
Accept-Rangeヘッダ | 説明 |
---|---|
Accept-Ranges: bytes | バイト単位で範囲指定可能 |
Accept-Ranges: none | 範囲指定はサポートしない |
-
ダウンロードを中断して、再開するまでにコンテンツが変わる可能性もあるのでEtagヘッダ(レスポンス)を併用できると良い
-
範囲指定を行う場合はRangeヘッダを付与する(範囲は0オリジン)
- Rangeヘッダが含まれる場合、レスポンスコードは206 Partial Contentとなる
- Rangeヘッダが無効な場合、レスポンスコードは416 Range Not Satisfiableとなる
- コンテンツが圧縮されている場合は圧縮後の範囲指定と捉えられる
Range: bytes=1000-1999
-
Rangeヘッダで複数の範囲指定も可能
-
レスポンスのContent-Typeは
multipart/byteranges
-
マルチパートフォームと同様のレスポンス
Range: bytes=500-900,7000-7999
Content-Type: multipart/byteranges; boundary=THIS_STRING_SEPARATES --THIS_STRING_SEPARATES Content-Type: apllication/pdf Content-Range: bytes 500-900/8000 ...some data... --THIS_STRING_SEPARATES Content-Type: apllication/pdf Content-Range: bytes 7000-7999/8000 ...some data... --THIS_STRING_SEPARATES--
-
If-Rangeヘッダ(リクエスト)にEtagか日時を設定すると条件付き範囲指定となる
-
コンテンツが更新されていない場合は、指定された範囲のコンテンツを返す
-
コンテンツが更新されている場合は、コンテンツ全体を返す
-
Rangeヘッダと一緒に送る
5.3 XMLHttpRequest
- curlコマンドのような機能をJavaScriptから使えるようにしたもの
- Javascript内で送受信が完結するので画面をクリアしなくても最新情報を取得可能
- 画面クリアせずにページを更新したり、時間/タイミングをずらして何度も更新できるアーキテクチャをAjaxと呼ぶ
- CometとはXMLHttpRequestを使ってほぼリアルタイムの双方向通信を可能にするテクニック
- ロングポーリングを使っており、サーバはイベント発生までレスポンスを返さない
- XMLHttpRequestのセキュリティにはアクセス制限と送信制限がある
制限種別 | 制限内容 |
---|---|
アクセス制限 | httponly属性がついたCookieにはアクセスできない |
送信制限 | ドメイン:基本的には同一ドメインのみアクセス可能 |
メソッド:CONNECT, TRACE, TRACK禁止 | |
ヘッダ:ブラウザ未サポートの圧縮フォーマット指定禁止など |
5.7 WebDav
- HTTPを拡張した分散ファイルシステム
- WebDavの構成要素の一部を以下に記載
- リソース: ファイルのこと
- コレクション: フォルダやディレクトリのこと
- プロパティ: リソースやコレクションのメタデータ(作成日時など)
- ロック: ファイル操作の排他制御用
- GET, POST, PUT, DELETEではファイル操作機能が足りないので下記メソッドが追加されている
- COPY
- MOVE
- MKCOL (コレクションの作成)
- LOCK/UNLOCK
参考文献
渋川よしき 著『Real World HTTP』(オライリー・ジャパン発行、ISBN978-4-87311-804-8)
https://www.oreilly.co.jp/books/9784873118048/