HTTPプロトコル


# 初めに

いま、とあることを理由に、**python** で **proxy** を書いてます。
そのついでに、これまでに覚えたことをを共有したいと思います。

つきましては、本稿では、まず、HTTPプロトコルの全様について、端々の内容も含め、私自身が理解した内容を述べるとともに、特にリクエストメッセージの全体のサイズはどうやって決定しているのかという点についてフォーカスして説明致します。

なお、若干、言動が **おじさん** ですが許してやってください（m-_-;m)

## URLとURI
一番最初に出て来るのはこれ、**URL**と**URI**。
* **URL** というのは、ブラウザでアドレスバーに指定するやつ
* **U**niform **R**esource **L**ocator、統一資源位置特定子とでもいうべきか？
    * **L**ocator は、位置を特定するという意味。
* **URI** と言うのは、**URL** を含めた総称。
* **U**niform **R**esource **I**dentifier、統一資源識別子。
* ネットワーク上のリソースの位置を特定する場合は全て**URL**である
* 位置を特定しない物、例えば ``mailto:robin@crousou.com`` は **URI** か？

## URLの形式

:::note
URLの形式：
    スキーマー://ホスト名/パス[?クエリ]
:::
<table>
    <tr>
        <th>名前　　　　</th>
        <th>英名　　　　　</th>
        <th>説明</th>
    </tr>
    <tr>
        <td>スキーマ</td>
        <td>schema</td>
        <td>
            <b>http</b>とか<b>https</b>、<b>file</b>、<b>ftp</b>、<b>gopher</b>等々、プロトコル種類を表す
        </td>
    </tr>
    <tr>
        <td>ホスト名</td>
        <td>hostname</td>
        <td>
            ホスト名(hostname)は、基本は以下の形式を持つ
            <div><code><b>ホストアドレス</b>[:<b>ポート番号</b>][@<b>ユーザ名</b>[:<b>パスワード</b>]]</code></div>
            <b>http</b> などでは、ユーザー名・パスワードとかはセキュリティ上ほぼ使わない<br/>
            <b>Apache-httpd</b> では制限を解除した場合には、ユーザ名・パスワードを含めた形式でアクセスすることが可能である
        </td>
    </tr>
    <tr>
        <td>パス</td>
        <td>path</td>
        <td><code>/index.html</code>とか<br/>
        URLのうち、境目の <code>/</code> より前がホスト名<br/>
        <code>/</code> を含め
        <code>?</code> の直前までがパス<br/>
        （<code>?</code> は区切り文字であり、パスにもクエリにも含まない）</td>
    </tr>
    <tr>
        <td>クエリ</td>
        <td>query</td>
                <td><b>query_string</b>（クエリ・ストリング、クエリ文字列）とも呼ばれるもので、省略可能で、<b>GET</b> メソッドでリクエストするときに、リクエストパラメータを送るときに使う。<br/>また、<b>POST</b>メソッドでは、リクエストボディを送ることができるため、<b>Content-Type</b> の MIME に <code>application/x-www-form-urlencoded</code>を指定し、リクエストボディに同様の形式で、このクエリ文字列を指定することもできる。<br/>リクエストパラメータは、<code>name=value</code>の形式をアンパサンド（<code>&</code>）で区切り複数指定できる。名前(name)も値(value)も、基本、全て英数字と特定の記号（<code>[-_.~0-9a-zA-Z]</code>）のみで構成し、それらに含まれない文字（半角空白や漢字などほかの文字全て）は全てURLエンコーディングによってパーセントエンコーディング（１文字が <code>%XX</code> の形式。XXは16進数。16進数の英字部分は英大文字）される。また、同じ<code>name=value</code>が重なっても構わない。受け手のサーバ側のアプリケーションは、重複したデータをどのように扱うかはアプリーケーションに任される。さらに、クエリ文字列に使われる漢字のエンコーディングは、HTTPプロトコルの範疇ではなく、ブラウザ上のアプリと、サーバウェアの仕様で決まる。<br> 例）空白＝<code>%20</code>、アンパサンド<code>&</code>＝<code>%26</code>
        </td>
    </tr>
</table>


# クッキーとは何か？
* HTTPは、TCPセッションで開設される
    * TCPセッションは、開いたら閉じるまで開きっぱなしのやつ :heart:
* 基本、送って（リクエスト）・受けて（レスポンス）で、1セッションがクローズする
* この手続きによって通信は一度で分断されるので、Webアプリケーションでは、ステートレス（状態が続かない状態）になる
```plantuml:
group 1セッション
ブラウザ->Httpサーバ:接続を開く
activate Httpサーバ
ブラウザ->Httpサーバ:リクエスト・メッセージ
ブラウザ<--Httpサーバ:レスポンス・メッセージ
deactivate Httpサーバ
ブラウザ->Httpサーバ:接続を閉じる
destroy Httpサーバ
end
note over ブラウザ,  Httpサーバ: TCP接続に永続性が無くステートレスになり状態が分断する
```

* Webアプリケーションでは、ステートフル（状態を保つ）にするために、クッキーを活用する
    * クッキーはブラウザ側のアプリがクレクレして、サーバがくれる情報。
    * 予め、クッキーをクレクレするときには、クッキーにユーザーIDなど留めておきたい情報とか、有効期間を書いてリクエスト・メッセージに便乗させる
    * サーバが返したクッキーは有効期間の間は、ブラウザで覚えておくことができる仕組みなので、クッキーの識別子を送れば、同じ人だってのが分かるという仕組み。
```plantuml:
group セッションフル
クッキー->x ブラウザ:最初は持ってない
ブラウザ->Httpサーバ:接続を開く
activate Httpサーバ
note right of ブラウザ
    クッキーの嘆願書を書く
    * 私はだあれ？（ユーザID）
    * この期間だけほしいの❤❤❤
end note
ブラウザ->Httpサーバ:リクエスト・メッセージ、一緒にクッキークレクレ！
note left of Httpサーバ
    サーバは「ちょっとだけよ」と言い
    レスポンス時にクッキーをあげる
end note
ブラウザ<--Httpサーバ:レスポンス・メッセージ、クッキーを貰う
ブラウザ->Httpサーバ:接続は閉じてしまうけれども....
deactivate Httpサーバ
クッキー<-ブラウザ:クッキーはポケットにしまう
activate クッキー
note over of クッキー
    クッキーが一定期の間は有効
    「ちょっと」だけなので...
    * 実はそんなことはない。要求した間だけ持ってられる
    * クッキーはサイズが最大4096バイトに限定されている
    （クッキーぐらいの大きさまで）
end note
destroy Httpサーバ
note over クッキー, ブラウザ: セッションに永続性が生まれステートフルになる
ブラウザ->Httpサーバ:接続を開く
activate Httpサーバ
クッキー->ブラウザ:リクエスト時にクッキーの情報も混ぜる
note right of ブラウザ
    ねぇ～覚えてるぅ❤？❤わ・た・し・よん❤
    リクエストメッセージにクッキーの情報を載せる
end note
ブラウザ->Httpサーバ:リクエスト・メッセージ
note left of Httpサーバ
    サーバは「あぁ、先日お会いしましたね(汗)」
end note
ブラウザ<--Httpサーバ:レスポンス・メッセージ
deactivate Httpサーバ
ブラウザ->Httpサーバ:接続を閉じる
destroy Httpサーバ
end

```

> どうでもいいことだけども、この**クッキー**は、**セサミストリート** に出てたクッキーをあげるとおとなしくなるあの青い**クッキーモンスター**をイメージしているそうだ。みんな見てた？**セサミストリート**！見てないか...。



## リクエストメッセージ
* リクエストメッセージとは、HTTPサーバに送るメッセージ
* リクエストメッセージは、以下の構造を持つ
```txt:
リクエストライン
リクエストヘッダ
区切り（ヘッダとボディの区切り）
[リクエストボディ]
```
※ ``[...]`` は省略可能なことを示す

#### :trophy:リクエストライン

* リクエストラインは最初の１行

:::note
*&lt;メソッド&gt;* _ *&lt;リクエストターゲット&gt;* _ *&lt;HTTPバージョン&gt;*\r\n
:::

* 各要素は空白（0x20）1文字（ここでは``_``表記）で区切る
<table>
    <tr>
        <td>メソッド</td>
        <td><p>メソッドは、何をしたいかをサーバに伝える一番重要なメッセージ</p>
            <table>
            <tr>
                <th colspan="4">HTTP/1.1</th>
            </tr>
            <tr>
                <th><p>メソッド　　　　<br/>
                <font color="yellow"><b>黄色</b></font>＝よく使う　　　　　　　</p></th>
                <th>〇仕様　　　<br/>△付録</th>
                <th>意味</th>
            </tr>
                <td><font color="yellow"><b>GET</b></font></td>
                <td>○</td>
                <td>文書を取得したいです</td>
            <tr/>
            <tr>
                <td>HEAD</td>
                <td>○</td>
                <td>GETと同じだけど、HTTPヘッダだけください...
                お試しで...みたいな？</td>
            </tr>
            <tr>
                <td><font color="yellow"><b>POST</b></font></td>
                <td>○</td>
                <td>細かいデータを送りますね！</td>
            </tr>
            <tr>
                <td>OPTIONS</td>
                <td>○</td>
                <td>通知など</td>
            </tr>
            <tr>
                <td><font color="yellow"><b>PUT</b></font></td>
                <td>○</td>
                <td>サーバ上のファイルを置換</td>
            </tr>
            <tr>
                <td>DELETE</td>
                <td>○</td>
                <td>サーバ上のファイルを削除</td>
            </tr>
            <tr>
                <td>TRACE</td>
                <td>○</td>
                <td>リクエストをそのまま送り返す</td>
            </tr>
            <tr>
                <td>PATCH</td>
                <td>△</td>
                <td>PUTと同じだが、差分のみ置換</td>
            </tr>
            <tr>
                <td>LINK</td>
                <td>△</td>
                <td>他の情報との関連付け</td>
            </tr>
            <tr>
                <td>UNLINK</td>
                <td>△</td>
                <td>LINKの解除</td>
            </tr>
            </table>
        </td>
    </tr>
    <tr>
        <td>リクエスターゲット</td>
        <td>
        <b>URL</b> のうち、パスの部分
        </td>
    </tr>
    <tr>
        <td>HTTPバージョン</td>
        <td>
        ブラウザが対応しているHTTPバージョン<br/>
        &nbsp;&nbsp;&nbsp;<code>HTTP/1.1</code> ...　こんな文字列<br/>
        &nbsp;&nbsp;&nbsp;<code>HTTP/1.0</code> もあるが、今はほぼ無いので割愛<br/>
        レスポンスメッセージのステータスラインに記載のHTTPバージョンは
        ここで指定したHTTPバージョンが返ってくる
        </td>
    </tr>
</table>

 
#### \:trophy:リクエストヘッダ
* 各要素の区切りはコロン(:)＋空白(0x20)1文字

```text:
リクエストヘッダ1: 値\r\n
リクエストヘッダ2: 値\r\n
    :
```
* 本稿での <font color="cyan">``\r\n``</font> は説明上の表記（復帰文字=``\r``、改行文字=``\n``）のため、実際は折り返すだけで表示されないので注意
<table>
    <thead>
        <tr>
            <th>ヘッダ名　　　　　　　　</th>
            <th>概要</th>
        </tr>
    </thead>
    <tr><td colspan="2"><a>リクエストとレスポンスの両方で利用される共通のヘッダ</a></td></tr>
    <tr>
        <td>Content-Type</td>
        <td>レスポンスのコンテンツタイプを示すヘッダ。</td>
    </tr>
    <tr>
        <td>Transfer-Encoding</td>
        <td>コンテンツの転送方法を示すヘッダ。</td>
    </tr>
    <tr>
        <td>Content-Length</td>
        <td>コンテンツのサイズを示すヘッダ。</td>
    </tr>
    <tr><td colspan="2"><a>リクエストヘッダに特有のヘッダ（一部）</a></td></tr>
    <tr>
        <td>Host</td>
        <td>接続先のホスト、または、ホストとポート番号を表すヘッダ。</td>
    </tr>
    <tr>
        <td>Accept</td>
        <td>クライアントが受け入れ可能なコンテンツタイプを指定するヘッダ。</td>
    </tr>
    <tr>
        <td>Authorization</td>
        <td>認証情報を指定するヘッダ。</td>
    </tr>
    <tr>
        <td>User-Agent</td>
        <td>クライアントの情報を指定するヘッダ。</td>
    </tr>
    <tr>
        <td>Cookie</td><td>クッキー情報を指定するヘッダ。</td>
    </tr>
</table>

##### クライアント側の仕様の通知

* リクエストヘッダ **``Accpet``** または **``Accept-*``** などは、クライアントで受け取ることができる仕様の通知を表す

##### Host リクエストヘッダについて
* リクエストメッセージには必ず、**Host** ヘッダがある
* **Host** ヘッダは、**URL** の<a>ホスト名部分</a>
ポート番号が付いていればポート番号の記述も付く
* 通常ブラウザが、リクエストヘッダを送る頃には、既にTCPセッションが始まっていて、ホストは開かれているから、普通のブラウザは関係ないのだけど、間を仲介するプロクシサーバとかは、誰が宛先かわからない。
そのためという訳じゃないと思うが、必ず指定されるリクエストヘッダとして **Host** ヘッダがあり、ここには、**URL** のうちの **ホスト名** が書かれている

#### \:trophy:区切り
* 区切りは空行（\r\n）で、ヘッダとボディの区切りを表す
* ボディが無くても必ず存在する
 
#### \:trophy:リクエストボディ
* リクエストボディは、省略可能である
* リクエストボディには、必要に応じて、 **POST**/**PUT**/**PATCH**/**DELETE** の4つのメソッドで送信するコンテンツを指定する
* **PUT**/**PATCH** メソッドでは、アップロードする文書のコンテンツをここに指定する
* **DELETE** メソッドでも同様にコンテンツを送ることができる
* リクエストボディの解釈は、サーバサイドのアプリケーションに任されておりHTTPサーバ自体は関与しない。 基本的なプロトコルの実装では、すべてのメソッドで、リクエストボディを送信することができる。つまり、例えば **GET** メソッドでリクエストボディを送ってもエラーにはならない。しかし、メソッドと言う考え方を導入したからには、４つのメソッド **POST**/**PUT**/**PATCH**/**DELETE** 以外ではリクエストボディは送信しないでね、という約束の下に、ルール付けが行われているに過ぎない

* リクエストボディが送信可能な各メソッドにて送れるリクエスト内容は以下の通り

|**Content-Type** の MIME　　　　　　　　　　　　　　　　　|リクエストボディの内容|
|--|--|
|application/x-www-form-urlencoded|**GET**メソッドで指定した場合とまったく同じクエリパラメータを指定する。URLエンコードも行う|
|multipart/form-data|マルチパート形式でデータを送る。ファイルのアップロードもこの形式で送信される。フォームの各要素（**GET** メソッドでの名前と値のペア）についても、マルチパートで分割されるため、大きなデータを送ることができる。マルチパート送信時には、**Content-Type**の第２引数にバウンダリを指定する。本例については次の節「マルチパートの例」を参照|
|application/json|json形式のまま指定する|
|application/xml|xml形式のまま指定する|
|そのほか|上位機以外にも、MIMEに準拠した形式のデータを送ることができる|

#### \:trophy:マルチパートの例
* マルチパートでデータを送信する際にはバウンダリの生成が必要となる
* バウンダリの値はその時々で一意になることが望ましい
* 生成したバウンダリは **Content-Type** の第2パラメータに指定する
```html:マルチパートのMIMEとバウンダリ指定
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
```
* 以下の例に挙げるように、マルチパートの各パーティションでは、個々に、**Content-Type** が指定できるようになっているため、個々のコンテンツに適した内容でデータを送ることができる
* マルチパートのコンテンツが、リクエストヘッダ作成時に確定できていない場合は、後述「メッセージ全体の長さ」で述べる方式に則り、マルチパートの各個別にではなく、マルチパート全体を１つの大きな塊として、**Content-Length** は指定せず、**Transfer-Encoding** に **chunked** を指定し、チャンク方式で送ることができる
* このマルチパートはレスポンスメッセージのレスポンスボディの際にも同様の形式で使用が可能である。すなわち、ファイルのダウンロードは、レスポンスメッセージにおけるこのマルチパートで行われる。
```html:マルチパートのコンテンツの内容
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="fieldName"

fieldValue
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain

(ファイルの内容)
----WebKitFormBoundary7MA4YWxkTrZu0gW
```


# レスポンスメッセージ
* レスポンスメッセージとは、リクエストメッセージを送った結果、HTTPサーバから戻ってくるメッセージ
* レスポンスメッセージは、サーバからクライアント（通常はブラウザ）へと、方向性が異なる点と、レスポンスヘッダのヘッダ名や値の解釈が一部異なるのみで（レスポンスだけに含まれるレスポンスヘッダなどがある）、ヘッダの指定形式、ボディとの境界線、マルチパートの受信方法、コンテンツの長さの取り方など、おおよその仕様は全て共通である。
* レスポンスメッセージは、以下の構造を持つ
```txt:
ステータストライン
レスポンスヘッダ
区切り（ヘッダとボディの区切り）
[レスポンスボディ]
```
※ ``[...]`` は省略可能なことを示す

#### \:trophy:ステータスライン


:::note
*&lt;HTTPバージョン&gt;* _ *&lt;ステータス番号&gt;* _ *&lt;理由&gt;*\r\n
:::




#### \:trophy:レスポンスヘッダ
* 基本、送り（リクエスト）と帰り（レスポンス）は同じ
* 各要素の区切りはコロン(:)＋空白(0x20)1文字

```text:
リクエストヘッダ1: 値\r\n
リクエストヘッダ2: 値\r\n
    :
```

<table>
    <thead>
        <tr>
            <th>ヘッダ名　　　　　　　　</th>
            <th>概要</th>
        </tr>
    </thead>
    <tr><td colspan="2"><a>リクエストとレスポンスの両方で利用される共通のヘッダ</a></td></tr>
    <tr>
        <td>Content-Type</td>
        <td>レスポンスのコンテンツタイプを示すヘッダ。</td>
    </tr>
    <tr>
        <td>Transfer-Encoding</td>
        <td>コンテンツの転送方法を示すヘッダ。</td>
    </tr>
    <tr>
        <td>Content-Length</td>
        <td>コンテンツのサイズを示すヘッダ。</td>
    </tr>
    <tr>
        <td colspan="2"><a>レスポンスヘッダに特有のヘッダ（一部）</a></td>
    </tr>
    <tr><td>Location</td>
    <td>リダイレクト先URLを指定するヘッダ。</td>
    </tr>
    <tr><td>Set-Cookie</td>
    <td>クッキーを設定するヘッダ。</td>
    </tr>
    <tr><td>Server</td>
    <td>サーバーの情報を指定するヘッダ。</td>
    </tr>
</table>

### \:trophy:区切り（ヘッダとボディの区切り）

* これもまた、送り（リクエスト）と帰り（レスポンス）は同じ
* 区切りはレスポンスボディが無くても必ず付く

#### \:trophy:レスポンスボディ

* これもまた、送り（リクエスト）と帰り（レスポンス）は同じ



# メッセージ全体の長さ
* 電文の長さに関係するリクエストヘッダは以下の3つ
    1. ``Content-Length``
    2. ``Transfer-Encoding``
    3. ``Content-Type``
* 本節ではリクエストヘッダを例に挙げ述べているが、
レスポンスヘッダについても基本同じ

## Content-Length、Transfer-Encoding 共にないケース
* リクエストボディが存在しない場合は、``Content-Length`` も、``Transfer-Encoding`` も無い（``GET``、``HEAD``、``DELETE``、``OPTIONS``メソッドなど）

## Content-Length、Transfer-Encoding 共にあるケース
* サイズの計算方法の基本は、``Content-Length`` のみのケースと同じ
* ``Transfer-Encoding`` には転送方法などが記述される
* ``Transfer-Encoding`` に、``chunked`` の記述はない

## Content-Length のみのケース
* ``Content-Length`` には、文字エンコーディングでエンコードした後の、ボディの長さが指定されている（10進数）
* サイズの１単位は1オクテット（8ビット=1バイト)
* その文字エンコーディングは、省略可能な ``Content-Type`` に書かれている
* ``Content-Type`` の値は、セミコロン（;）区切り
    * 1つ目がMIME（ほかの呼び方をする人がいるが、MIME（マイム）で問題ない）
    ``text/plain`` がデフォルト。
    * 2つ目が文字セット
    ``;charset=xxx`` で指定し、文字エンコーディングを表す。``utf-8`` がデフォルト。
* ``Content-Type`` が無い場合は、各デフォルトが選択される
* ボディの長さは、ヘッダとボディを区切っている区切り（\r\n）の次の位置からカウントされる
* ``UTF-8`` の場合、漢字は、3～6バイトになるが、漢字1文字でカウントするのではなく、この3～6バイトの数でカウントする
* コンテンツとしての改行が、最後にあっても、RFC上の仕様に規定はなく、基本は、トリムはされず、コンテンツとしてカウントされる（各自ブラウザなどの実装による）


## Transfer-Encoding のみのケース
* ``Transfer-Encoding`` の値は、カンマ（,）区切り
* この場合、値の中に、``chunked``（チャンク） が指定される
* チャンクが指定されるのは、予め転送するデータ容量が分からない場合（``Content-Length`` が確定できないケース）が多い
* チャンクは、**チャンクヘッダ**と**チャンクデータ**に分かれ、交互に定義される
* チャンクは、ボディ（リクエストボディやレスポンスボディ）に指定され、その形式は、
```html:チャンクデータの形式
チャンクヘッダ\r\n
チャンクデータ\r\n
 ：
0\r\n
\r\n（空行） ←ここで終わり
```

* 最後の``0\r\n``は、必ず指定され、チャンクサイズが ``0`` であることを示し、続くチャンクデータは存在しない。また、最後の空行（\r\n）も必ず最後に付く
* 各要素の末尾の復帰・改行は要素の終わりを示すのではなく、次に続く要素との区切りを示す。
* チャンクヘッダは、16 進数表記のサイズを記述する
    * 1バイトなら ``1``、46バイトなら ``2e`` など
* チャンクデータは ``Transfer-Encoding`` に ``Base64`` などの指定がない限りは、 直のバイナリデータが指定される
* 容量が多い場合に圧縮転送するケースがあり、``Transfer-Encoding`` に``compress`` や ``gzip`` などが指定される
* ``Transfer-Encoding`` に ``gzip`` がある場合は、元のデータが ``gzip`` 形式で、それをチャンク単位で分割したデータが連続する
 
## ここで無駄な豆知識
* 何ゆえに1オクテットという単位を使うかと言うと、``CISC``などのプロセッサにおいては、1バイトが32ビットや64ビット、詰まり最小単位が64ビットだったりするケースがある
* すなわち、世界は、1バイト＝8ビット固定ではないのである