「HTTPとは何か？」を完全に理解する〜分かったフリの脱却〜

# HTTPを完全に理解する
「エンジニアとしてHTTPは扱ったことがあるけど、何となくしか分かっていない。。」
状態だったので、完全に理解するために調べてまとめた記事です。

## 本記事の参考書籍
下記書籍のHTTPについての記述のアウトラインを参考に、本には載っていない説明の拡充及び整理をしています。
**<a target="_blank" href="https://www.amazon.co.jp/gp/product/4774142042/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4774142042&linkCode=as2&tag=hirocode-22&linkId=8c684fff0272ca567d88750a24e32367">Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)</a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=hirocode-22&l=am2&o=9&a=4774142042" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />**
HTTPに止まらずWebの基礎知識が広く書かれているので、気になる人は読んでみて下さい。
# HTTPとは
一言で言うなら、**TCP/IPをベースとしたプロトコル**。
htmlやxmlに限らず静止画、動画、javascrpitプログラムなど、**コンピューターで扱えるデータであればなんでも転送できるWebの基盤となるプロトコル**。
webエンジニアがwebサービスを作って通信するとなった場合、ほとんどの場合HTTPを使って通信するよう実装することになる。



# 「HTTPとは、TCP/IPをベースとしたプロトコル」の意味を理解する
HTTPを一言で言うと
**「TCP/IPをベースとしたプロトコル」**
と説明されることが多い。
この一言が意味するところを理解する。

## プロトコルって何だ
プロトコルとは、**守るべき規約やルール**のこと。

### プロトコルがあるから、Webという「歴史上最大の分散システム」が成立する
「Web」とは世界中の無数のサーバーからなる歴史上最大の分散システムである。
その無数のサーバー達が通信のプロトコルを守ってやり取りをしているため、任意のサーバーとやり取り出来る。


## TCP/IPとは何だ
TCPはネットワークのプロトコルの1つ。
TCP、IP、HTTPの関係性を理解するに当たって、**階層型プロトコル**の理解が必要になってくる。
### まずは階層型プロトコルを必要最小限理解する
一口に「通信する」と言っても、やっていることは多岐に渡る。
各工程を4階層に分けたものがTCP/IP 4階層モデルである。
**一番下のネットワークインターフェース層からアプリケーション層に上がって見ていく**とイメージが付きやすい。


<img width="709" alt="スクリーンショット 2019-10-20 22.10.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78136/4cb6315c-2d72-2be5-a860-e364b771e28e.png">


## ネットワークインターフェース層
通信のための**物理的なことをする層**。
イーサネット、無線LANなど、ネットワークハードウェアが通信するための階層。
webエンジニアが滅多に意識することがない階層。
Ethernetというプロトコルを用いる。

## インターネット層
**ネットワークでデータをやり取りする部分**。
TCP/IPのIPの部分。
IPはデータの基本的な通信単位である**パケット**を単位として、指定されたIPアドレスにパケット単位でやり取りしている。
**IPが責任を持つのはデータを送り出すところだけ**。到着は保証しない。

## トランスポート層
IPが保証しなかった**データの転送を保証する層**。
TCP/IPのTCPの部分。
TCPは接続相手に対してコネクションを張る。
このコネクションを使ってデータの抜け漏れを確認し、データが到着したことを保証している。
アプリケーション層のwebアプリからすれば、トランスポート層がデータのやり取りの信頼性（パケットが欠損して、データが壊れているなど）は担保してくれているので、やり取りしたいデータの授受のみに集注出来る。

### ポートとは
**どのアプリケーションに渡るかを決定する**のがポート番号。
1-65535(2の16乗-1)で指定されている。デフォルトは80。

### アプリケーション層
**具体的なアプリケーション、メールやDNS、HTTPを実現する層**。
SSHもアプリケーション層のプロトコル。
TCPでプログラムを作るときはSocketというライブラリを使う。
Socketはネットワークでのデータのやり取りを抽象化したAPIで、接続、受信、送信、切断などの基本的な機能を備えている。
HTTPサーバーやブラウザはソケットを用いて実装している。
大抵のプログラミング言語はHTTPを実装しているため、意識することはない。

## HTTPは宅急便で輸送する際の積荷のルール

### みんながプロトコルに従うから宅配便は成立する
宅急便での配送を例に考えてみる。
様々な宅急便業者が時速100km以上の自動車でモノを目的地に輸送出来るのは、**各階層でプロトコルを守っているから**である。
### 宅急便に置けるプロトコルを4階層に分けてみる

#### 道路のプロトコル(ネットワークインターフェース層に相当)
**物理的な**プロトコル。
日本全国、所定のプロトコルに従って道路が建築されていることにより、任意の自動車が任意の場所で通行出来る。

#### 住所,道路通行方法のプロトコル（IPに相当）
ルールに従って制定された郵便番号を用いて、所定の順番で記載する必要のある住所というプロトコルと、
道路交通法によって制定された、自動車の通行方法にまつわるプロトコルに沿って自動車が動くことにより、任意の場所に任意の自動車が行くことが出来る。

#### 宅配業者のプロトコル(TCPに相当)
宅配便は発送すれば終わりではなく、不在であれば再配達するなど、宅配物が届け先に届くことを保証する必要がある。
宅配業者が宅配物の配達を担保するやり取りを規定指定いるからこそ、利用者は配達の依頼と受け取りだけに集中できる。

#### 積荷のフォーマット(HTTPに相当)
利用者は所定のダンボールに収まるように荷物を収める必要がある。
また、発送に必要な情報を記載して宅配業者に渡す必要があるが、**宅配業者が提供する伝票というライブラリ**(アプリケーション層に置けるSocket)を使うことで、宅配業者と簡単にやり取り出来る。
なお、会社で発送する際は**バックオフィスの方に発送を頼む**（言語のフレームワークを使う）ことで、伝票の記載項目を意識せずに発送することも出来る。

#### HTTPとTCP/IPの関係まとめ
- IPがネットワークでとデータをやり取りのプロトコル
- TCPがデータの転送保証のためのプロトコル
- HTTPがTCP/IPを使ってデータの授受をするためのプロトコル

だと言える。

## ネットワーク上でやり取りするパケットの中身
- インターネット層ではIP
- トランスポート層ではTCP
- アプリケーション層ではHTTP

のプロトコルに従ってデータを輸送するとき、
HTTPパケットの中身は下記のようになる
<img width="711" alt="スクリーンショット 2019-10-20 22.46.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78136/6b7cd572-6330-00e4-454d-41a694461ca6.png">
IP、TCP、HTTPの情報が乗っかっている。

# HTTPのリクエスト/レスポンス
## HTTPはリクエスト/レスポンス型のプロトコルである
HTTPではクライアントが出したリクエストをサーバーで処理してレスポンスを返す。
HTTPにおけるリクエスト/レスポンスは指定されたフォーマットがある。
リクエストメッセージとレスポンスメッセージをまとめて**HTTPメッセージ**と呼ぶ。
## HTTPメッセージの構成
下記図のように、HTTPメソッドは

- スタートライン
- ヘッダ
- ボディ

の3つに分けられる。
<img width="1334" alt="スクリーンショット 2019-10-20 23.14.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78136/348dca19-c958-804d-6a65-4c9766fe386c.png">


## スタートラインメッセージ
HTTPメッセージの一番最初にくるのがスタートラインメッセージである。
### リクエストの場合はリクエストラインと呼ぶ
```
GET /test HTTP/1.1
``` 
のようなメッセージ。

- メソッド
- リクエストURI
- プロトコルバージョン
の3つからなる。

リクエストURIが絶対パスの時HOSTは省略可能 

### レスポンスの場合はステータスラインと呼ぶ
ステータスラインは

```
HTTP/1.1 200 OK
```
のように、

- プロトコルバージョン
- ステータスコード
- テキストフレーズ

3つからなる。

## ヘッダ
```
Content-Type:application/json; charset=utf-8
```
のように、key:value、形式でメタデータが記述されている。
代表的なヘッダとして下記を紹介する。
### Content-Type
メッセージのボディの内容の種類なのかメディアタイプを示す。
上記例ではレスポンスがjsonであることを示している。この他text,image,audioなどがある。
参考:選択できるContent-Typeの一覧
https://qiita.com/AkihiroTakamura/items/b93fbe511465f52bffaa
#### メディアタイプのパラメータ、charsetパラメータ
メディアタイプはcharsetパラメータを持つことができる。
特にタイプがtextの場合はクライアントで文字化けが起きないように、文字エンコーディングの指定が望ましい。

### 認証
HTTP認証方式の１つに**Basic認証**がある。
HTTP認証方式に置いてリソースにアクセス制御がかかっており認証されていなかった場合、ステータスコードが401(Unauthorized)となり、WWW-Authenticateヘッダでクライアントで必要な認証情報をクライアントに通知する。

Basic認証はユーザー名とパスワードによる認証方式で、ユーザー名とパスワードを:で連結しBase64エンコードした文字列をAuthorizationヘッダで送信する。

```
Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
```

## ボディ
ヘッダとボディの間には空行がある。
ボディの形式は先述のContent-Typeにより様々であり、ボディがない場合もある。

# HTTPメソッド
HTTPメソッドとは、サーバーに対する**リクエストの種類**を表すもの。
`example.com`に対して、

- `GET`メソッドを使えば、`example.com`に対するリソースの取得のリクエスト
- `DELETE`メソッドを使えば、`example.com`に対するリソースの取得のリクエスト

となる。
HTTPメソッドを見ればサーバーに対して何の要求をしているか分かる。

## HTTPメソッドは8つしかなく、よく使うのは4つだけ。
HTTPメソッドは下記8つであり、その中でもよく使うのは上4つである。

| メソッド | 役割 |
|:--|:--|
| GET | リソースの取得 | 
| POST | 子リソースの作成、リソースへのデータ追加、その他処理 |
| PUT | リソースの更新、リソースの作成 |
| DELETE | リソースの削除 |
| HEAD | リソースのヘッダの取得 |
| OPTIONS | リソースがサポートしているメソッドの取得 |
| TRACE | プロキシ動作の確認 |
| CONNECT | プロキシ動作のトンネル接続への変更 |

## CRUD操作に対応するHTTPメソッド
**データ操作の基本**となる処理は、
作成(Create)、読み込み(Read)、更新(Update)、削除(Delete)
の頭文字を取って**CRUD操作**と呼ばれる。
GET,POST,PUT,DELTEはこのCRUD操作に該当するため、HTTPメソッドの中でも特に使われる操作になっている。

| CRUD | HTTPメソッド |
|:--|:--|
| Create | POST/PUT | 
| Read | GET |
| Uodate | PUT |
| Delete | DELETE |
## GET
**情報を取得**するメソッド。
Webページや画像、動画の取得など、もっとも利用頻度の高いメソッドである。
データの参照のみであり、データの更新は行わない。
### リクエストでbodyは使ってはならない
**bodyの使用は非推奨である**。
The GET method means retrieve whatever information ([...]) is identified by the Request-URI.
https://stackoverflow.com/questions/978061/http-get-with-request-body

### GETでリソースを指定する場合
GETではbodyを使わないため、検索条件等サーバーに送る情報がある場合は、**クエリストリング**で送る。
下記のようにkey-valueの組み合わせで記述する。

```
http://book.com/search?tag=java&author=growsic
```

**クエリストリングの内容は不特定多数の目に晒される前提**で考えた方が良い。
サーバーのアクセスログやブラウザのキャッシュ、プロキシサーバのログなどに平文で保存される。
セキュリティ観点から、機密情報は決してクエリストリングで送ってはならない。

参考:
クエリストリングから情報が漏れる
https://www.ipa.go.jp/security/awareness/vendor/programmingv1/a01_04.html

## POST
- リソースの更新、作成
- 他のメソッドで出来ない処理(出来るがPOSTでやった方が可用性が高い場合)

を担うメソッドである。
最も汎用的なメソッドであるとも言える。

### リソースの更新、削除
**データの作成または更新**のためにサーバーに**データを送る**役割である。
送信するデータはbodyにJSON, XMLなどの形式で記載する。

### 他のメソッドで出来ない処理(出来るがPOSTでやった方が可用性が高い場合)
CRUD操作でいうとPOSTはCreateのみを担当する位置付けだが、**実際の使用場面でいうとCRUD全てを担当させる場合**もある。

#### GETのクエリストリングには文字列長の制限がある場合がある
2000文字制限など、クエリストリングで送れる文字数に限度がある場合がある。
複雑な検索条件を指定する場合などに、POSTでbodyに検索条件をkey-valueで指定するようサーバー側で実装する場合がある。

#### PUT/DELETEを使わず、データ更新を全てPOSTでやる場合もある
- bodyを使わないデータ取得のみのGET
- その他bodyを使う全ての操作をするPOST

だけで、webアプリケーションにおけるクライアント-サーバー間の通信は完結させることは出来る。
実際**GET,POSTだけで作られているWebアプリケーションも多い**。
PUTの代わりにPOSTでデータ更新を行うAPIを実装したり、
DELETEの代わりにPOSTでデータ削除を行うAPIを実装したとしても、
PUT,DELETEを使わないことによる都合は開発者観点ではあまりない。
（ではなぜPUTとDELETEが存在するかは後述）

## PUT
**リソースの更新または作成**を担うメソッド。
bodyでデータを送信し、既にあれば更新、なければ作成する。
更新または作成を担うメソッドであることから、[リクエストに対する結果は不変](https://assertible.com/blog/7-http-methods-every-web-developer-should-know-and-how-to-test-them#put)である。

### POSTとPUTの違い
先述の通り**実装観点では全てPOSTでも問題ない**が、**HTTPメソッドの設計思想では違いがある**。

[RFC](https://www.nic.ad.jp/ja/newsletter/No24/090.html)というインターネット技術の標準的な仕様を記した文書に置いて、PUTとPOSTの違いについて下記のように記載されている。

> The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource. If the server desires that the request be applied to a different URI, it MUST send a 301 (Moved Permanently) response; the user agent MAY then make its own decision regarding whether or not to redirect the request.

[原文](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)
POSTとPUTの違いは、**POSTとPUTそれぞれにおけるURIの意味の違い**に起因する。
#### PUTを使って特定のデータのみ作成/更新を行い、それ以外の操作は一切行わない
**PUTにおいて、URIはリクエストに含まれるデータそのもの**を表す。
（すなわち作成/更新するデータそのもの）
HTTPリクエストを投げるクライアントが作成/更新したいデータあり、更新したいリソースのURIを知っている場合、PUTメソッドを使って作成/更新を行う。
この**PUTメソッドにおいて行われるべきは特定のリソースの作成/更新のみ**であり、それ以外のことは行ってはならない。（関連するそれ以外のリソースを更新するなど）
特定のリソースの作成または更新のみするという特性ゆえに、PUTリクエストにおいて[リクエストに対する結果は不変](https://assertible.com/blog/7-http-methods-every-web-developer-should-know-and-how-to-test-them#put)である。

#### POSTはリクエストのデータを元に様々な処理を行う
**POSTにおいて、URIはデータを取り扱うリソース**を表す。
受け取ったデータを元に他の関連データを更新したり、他の処理系統にデータを渡したりなどをしうる。
デザインパターンにおける[Facade](https://www.techscore.com/tech/DesignPattern/Facade.html/)のようなものである。
様々な処理を行いうる特性ゆえに、リクエストに対する結果は不変ではない。

#### 実際の使用場面において、POSTよりPUTが良い場合はあるのか
Webアプリケーションの特性を考えると、**データの作成/更新の場合でもPOSTになることがほとんど**だと考えられる。
**クライアントが指定した特定のリソースのみを作成/更新することはほとんどない**。
ユーザー情報の更新だとしても他の関連情報を関連して更新することもあるし、ユーザー間で相互作用するサービスであれば他のユーザー情報にも影響が出る。
PUTの特性を満たしているものは、せいぜいプロフィール画像の更新などくらいではないだろうか。

参考:
[What's the difference between a POST and a PUT HTTP REQUEST?
](https://stackoverflow.com/questions/107390/whats-the-difference-between-a-post-and-a-put-http-request)
[PUT vs POST - Comparing HTTP Methods](https://www.keycdn.com/support/put-vs-post#when-to-use-put-vs-post)

## DELETE
**DELETEはリソースの削除を担うメソッド**。
### POSTとDELETEとの違い
先述のPOSTとPUTの関係とほぼ同じである。
**PUTメソッドにおいて行われるべきは特定のリソースの作成/更新のみ**であったように、
**DELETEメソッドにおいて行われるべきは特定のリソースの削除のみ**である。

## GET,POSTだけで良いのではないか
#### 実用的にはGET,POSTだけでも問題ない
- bodyを使わないデータ取得のみのGET
- その他bodyを使う全ての操作をするPOST

だけで、Webアプリケーションにおけるクライアント-サーバー間の通信は完結させることは出来る。
**実際GET,POSTだけで作られているWebアプリケーションも多い**。

PUTとDELETEは特定のリソースのみに対する操作を担うが、**現実問題特定のデータのみを操作するケースはほとんどない。**
**リクエストのデータを元に様々な処理を行うPOST**が、**RFCにおけるPOST/PUT/DELETEの定義と照らし合わせても妥当**だろう。

## ステータスコード
ステータスコードは、**スタートラインに含まれるリクエストの実行結果を表す数字**である。
### クライアントはステータスコードをみて、処理の大枠を決める
ステータスコードが200であれば処理が正常終了したと見なして、レスポンス内容に応じた処理を実行し、301であれば別のURIにリダイレクトするなどが挙げられる。

## ステータスコードの分類
百の位を見れば分類が分かるようになっている。
ステータスコードは無数にあるので全て覚えるのではなく、**分類を理解した上で、発生したステータスコードを調べる**のが良いと思われる。

| ステータスコード | 内容 |
|:--|:--|
| 1xx | 処理中 | 
| 2xx | 成功 |
| 3xx | リダイレクト |
| 4xx | クライアントエラー |
| 5xx | サーバーエラー |

2xx、4xx、5xxが出会うことが多い。
開発において対処が必要になることが多い4xx系、5xx系にここでは触れる。

### 4xx
エラーが発生し、**エラーの原因がクライアント**にある場合のステータスコード。
#### 400 Bad Request
リクエストの構文やパラメータが間違っていたことを示す。

#### 401 Unauthorized 
認証が必要なURIに対して適切な認証情報を与えずにリクエストをしたことを示す。
レスポンスのwww-Authenticateヘッダでクライアントに認証情報を伝える。

#### 404 Not Found
指定したリソースが見つからなかったことを示す。

### 5xx
エラーが発生し、**エラーの原因がサーバー**にある場合のステータスコード。
#### 500 Internal Server Error
サーバー側に何らかの異常が発生しておりレスポンスを返せないことを示す。

#### 503 Service Unavailable
サーバーメンテナンスなどで一時的にアクセスできないことを示す。

# 最後に
改めて、本記事は
**<a target="_blank" href="https://www.amazon.co.jp/gp/product/4774142042/ref=as_li_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4774142042&linkCode=as2&tag=hirocode-22&linkId=8c684fff0272ca567d88750a24e32367">Webを支える技術 -HTTP、URI、HTML、そしてREST (WEB+DB PRESS plus)</a><img src="//ir-jp.amazon-adsystem.com/e/ir?t=hirocode-22&l=am2&o=9&a=4774142042" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />**
に大変お世話になりました。
生半可な理解しかなかったHTTPを学び直すために読み、関連した情報を調べながら理解したまとめとして本記事を書きました。

HTTPに止まらずWebの基礎知識が広く書かれているので、気になる人は読んでみて下さい。