HTTPの基本
HTTPとはなにか
HTTPはTCP/IPをベースにしている。HTTPは階層型になっていて、
- アプリケーション層
- トランスポート層
- インターネット層
- ネットワークインターフェース層
に分かれている。
現在はHTTPのバージョンは1.1。
HTTPはリクエスト/レスポンス型のプロトコル。
HTTPメッセージ
リクエストメッセージはリクエストラインから始まる。Host情報を表すHostヘッダは必ず必要。ボディにはそのメッセージを表す本質的な情報を入れる。ボディは必要なときと、必要ではないときがある。
レスポンスメッセージはステータスラインから始まる。ステータスラインはプロトコルバージョン、ステータスコード、テキストフレーズから成る。それ以降はリクエストメッセージと同様にヘッダ、ボディから構成される。
HTTPのステートレス性
HTTPはステートレスなプロトコル。
ステートフルであるとサーバーにデータが保存される形になり、不特定多数のクライアントを相手にする場合には接続するサーバーを特定できないため、サーバー間で情報を同期するなどの処理が必要になり、とてもスケールアップが難しい。
ステートレスである場合にはクライアントが情報を保持しているため、サーバー側はリクエストに対して処理することだけに集中できるので、スケールアップする際にとても容易になる。
ステートレスにした際に、クライアントは毎回データを送らなければいけないため処理が重くなる、毎回データベースアクセスが発生するため処理が重くなる、等のパフォーマンス低下や通信エラーへの対処が難しい等のデメリットがある。
HTTPメソッド
8つしかないHTTPメソッド
HTTPのメソッドはGET、POST、PUT、DELETE、HEAD、OPTIONS、TRACE、CONNECTの8つ。8つしかないけれども、シンプルであるがゆえに成功したという観点からは正解になる。
GET、POST、PUT、DELETEは4つで「CRUD」の性質を満たす。
POSTでPUT、DELETEの代用
HTMLの制限によりGETとPOSTしか使えない問題がある。その場合に
<form method="POST" action="/list/item1">
<input type="hidden" id="_method" name="_method" value="PUT" />
</form>
のようにして、PUTの処理をPOSTで行うこともできる。
条件付きリクエスト
HTTPメソッドをリクエストを、サーバー側で実行するかどうかを制御できる。
メソッドの誤用
GETの用いる際に、WebAPI側のURIに
/resouces/1/delete
のようなものがあるとする。しかし、GETを行った際にはリソースの取得という明確な役割がある。このようにURIの中に 動詞 を入れてしまうと、思わぬ結果になってしまう。よって、URIはリソースを提供する役割に徹し、それらをどうするのかという部分はHTTPメソッドに任せるべきである。
# ステータスコード
よく見るステータスコードはファイルが見つからなかった際の404、HTTPではリクエストが完了した200などがある。
ステータスコードの分類と意味
1☓☓: 処理中。処理が継続して行われていることを表す。
2☓☓: 成功。リクエストが成功したことを示す。
3☓☓: リダイレクト。他のリソースへのリダイレクトを示す。
4☓☓: クライアントエラー。クライアント側で問題があることを示す。
5☓☓: サーバーエラー。サーバー側で問題があることを示す。
未知のステータスコードがあったとしても、最初の数があっていれば処理ができる。499というステータスコードが来た際に、それに該当するステータスコードがなくても400のステータスコードとして処理することができる。
HTTPヘッダ
HTTPヘッダの重要性
ヘッダはメッセージのボディに対する付加的な情報。メタデータである。クライアントやサーバーはヘッダを見てメッセージに対する挙動を決める。
HTTPヘッダの生い立ち。
メールやHTTPはインターネットの成長とともに規定されてきたため、HTTPには電子メールから拝借する形で追加されたヘッダがある。
コンテントネゴシエーション
サーバーがメディアタイプや文字コーディング、言語タグ等を一方的に決めるのではなく、クライアントと交渉してきめることもできる。これをコンテントネゴシエーションという。
コンテントネゴシエーションでは、クライアントはサーバー側に処理してほしいものに優先順位をつけてリクエストを送ることができる。
Content-Lengthとチャンク転送
メッセージがボディを持っている場合には基本的にContent-Lengthヘッダを利用して、そのサイズを10進数のバイトで示す。動的に画像を生成する際にはファイルサイズが確定するまでレスポンスが返せなくなってしまう。そのようなケースに少しずつデータを転送し応答性能が低下しないようにする。これがチャンク転送。
認証
WWW-Authenticateヘッダでクライアントがサーバーが提供する認証方式を理解し、認証情報を送る。
認証方式にはBasic認証とDigest認証がある。どちらも最初にリクエストをし、レスポンスで認証方式を知ることになる。
Basic認証
Base64でユーザー名とパスワードでエンコードをする。このBase64は簡単にデコードが可能であるため、平文でユーザー名とパスワードが流れているのと等しい。
digest認証
WWW-Authenticateヘッダの値をチャレンジといい、ヘッダであるnonceなどのリクエストごとに変わる値を用いて複雑にダイジェストを生成。セキュリティ的にはとても優れている。
digestはサーバー上にパスワードのハッシュ値を保管しておけばよいとうの理由もあり、とてもセキュリティリスクが低い。しかし、Basic認証と違い毎回チャレンジをしなければならないため、あまり普及していない。
その他、digest認証などが使えないけれどもどうしてもセキュリティを高めたいときに使えるWSSE認証もある。
キャッシュ
毎回サーバーからデータを取得していると非常に時間がかかってしまうため、クライアントのローカルストレージにデータを保存できるキャッシュがある。
キャッシュの抑制をするpragma、キャッシュの期限を表すexpires(データの変更の予定がない場合でも最長で1年間とすることを推奨している)、どちらの実装もできるcache-controlヘッダなどがある。キャッシュをなしにする場合はpragmaとcache-controlの値をno-cacheにし、有効期限の指定する際に絶対値を指定したい場合にはexpires、相対的な指定をしたい場合にはcache-controlを用いる。また、これらのヘッダでno-cacheを指定した場合でも、条件付きGETを用いることでキャッシュを再利用できる場合がある。その際にはリソースの内容が変わっていないステータスコード、リソースの更新状態を確認するEtagを利用してリクエスト側と一致していれば使用する機能を用いて実現する。
持続的接続
HTTP1.0ではTCPコネクションをサーバーからのレスポンスがあるたびに切っていた。これらの問題を解決するために持続的な接続をする仕組みとしてkeep-aliveヘッダを使用していた。HTTP1.1ではこの機能がデフォルトで実装されている。これをパイプライン化と呼ぶ。
接続を切りたい場合にはconnectionヘッダにcloseという値を指定する。
HTTPヘッダの活用
HTTPヘッダは電子メールや言語タグ、文字エンコーディングなど他の標準を積極的に活用している。HTTPヘッダを上手く扱うためには、これらの歴史と実際のサーバーやブラウザの実装を調査する能力が必要となる。