0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HTTPプロトコル

Posted at

初めに

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

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

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

URLとURI

一番最初に出て来るのはこれ、URLURI

  • URL というのは、ブラウザでアドレスバーに指定するやつ
  • Uniform Resource Locator、統一資源位置特定子とでもいうべきか?
    • Locator は、位置を特定するという意味。
  • URI と言うのは、URL を含めた総称。
  • Uniform Resource Identifier、統一資源識別子。
  • ネットワーク上のリソースの位置を特定する場合は全てURLである
  • 位置を特定しない物、例えば mailto:robin@crousou.comURI か?

URLの形式

URLの形式:
スキーマー://ホスト名/パス[?クエリ]

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

クッキーとは何か?

  • HTTPは、TCPセッションで開設される
    • TCPセッションは、開いたら閉じるまで開きっぱなしのやつ :heart:
  • 基本、送って(リクエスト)・受けて(レスポンス)で、1セッションがクローズする
  • この手続きによって通信は一度で分断されるので、Webアプリケーションでは、ステートレス(状態が続かない状態)になる
  • Webアプリケーションでは、ステートフル(状態を保つ)にするために、クッキーを活用する
    • クッキーはブラウザ側のアプリがクレクレして、サーバがくれる情報。
    • 予め、クッキーをクレクレするときには、クッキーにユーザーIDなど留めておきたい情報とか、有効期間を書いてリクエスト・メッセージに便乗させる
    • サーバが返したクッキーは有効期間の間は、ブラウザで覚えておくことができる仕組みなので、クッキーの識別子を送れば、同じ人だってのが分かるという仕組み。

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

リクエストメッセージ

  • リクエストメッセージとは、HTTPサーバに送るメッセージ
  • リクエストメッセージは、以下の構造を持つ
リクエストライン
リクエストヘッダ
区切り(ヘッダとボディの区切り)
[リクエストボディ]

[...] は省略可能なことを示す

:trophy:リクエストライン

  • リクエストラインは最初の1行

<メソッド> _ <リクエストターゲット> _ <HTTPバージョン>\r\n

  • 各要素は空白(0x20)1文字(ここでは_表記)で区切る
メソッド

メソッドは、何をしたいかをサーバに伝える一番重要なメッセージ

HTTP/1.1

メソッド    
黄色=よく使う       

〇仕様   
△付録
意味
GET 文書を取得したいです
HEAD GETと同じだけど、HTTPヘッダだけください... お試しで...みたいな?
POST 細かいデータを送りますね!
OPTIONS 通知など
PUT サーバ上のファイルを置換
DELETE サーバ上のファイルを削除
TRACE リクエストをそのまま送り返す
PATCH PUTと同じだが、差分のみ置換
LINK 他の情報との関連付け
UNLINK LINKの解除
リクエスターゲット URL のうち、パスの部分
HTTPバージョン ブラウザが対応しているHTTPバージョン
   HTTP/1.1 ... こんな文字列
   HTTP/1.0 もあるが、今はほぼ無いので割愛
レスポンスメッセージのステータスラインに記載のHTTPバージョンは ここで指定したHTTPバージョンが返ってくる

:trophy:リクエストヘッダ

  • 各要素の区切りはコロン(:)+空白(0x20)1文字
リクエストヘッダ1: 値\r\n
リクエストヘッダ2: 値\r\n
    :
  • 本稿での \r\n は説明上の表記(復帰文字=\r、改行文字=\n)のため、実際は折り返すだけで表示されないので注意
ヘッダ名         概要
リクエストとレスポンスの両方で利用される共通のヘッダ
Content-Type レスポンスのコンテンツタイプを示すヘッダ。
Transfer-Encoding コンテンツの転送方法を示すヘッダ。
Content-Length コンテンツのサイズを示すヘッダ。
リクエストヘッダに特有のヘッダ(一部)
Host 接続先のホスト、または、ホストとポート番号を表すヘッダ。
Accept クライアントが受け入れ可能なコンテンツタイプを指定するヘッダ。
Authorization 認証情報を指定するヘッダ。
User-Agent クライアントの情報を指定するヘッダ。
Cookie クッキー情報を指定するヘッダ。
クライアント側の仕様の通知
  • リクエストヘッダ Accpet または Accept-* などは、クライアントで受け取ることができる仕様の通知を表す
Host リクエストヘッダについて
  • リクエストメッセージには必ず、Host ヘッダがある
  • Host ヘッダは、URLホスト名部分
    ポート番号が付いていればポート番号の記述も付く
  • 通常ブラウザが、リクエストヘッダを送る頃には、既にTCPセッションが始まっていて、ホストは開かれているから、普通のブラウザは関係ないのだけど、間を仲介するプロクシサーバとかは、誰が宛先かわからない。
    そのためという訳じゃないと思うが、必ず指定されるリクエストヘッダとして Host ヘッダがあり、ここには、URL のうちの ホスト名 が書かれている

:trophy:区切り

  • 区切りは空行(\r\n)で、ヘッダとボディの区切りを表す
  • ボディが無くても必ず存在する

:trophy:リクエストボディ

  • リクエストボディは、省略可能である

  • リクエストボディには、必要に応じて、 POST/PUT/PATCH/DELETE の4つのメソッドで送信するコンテンツを指定する

  • PUT/PATCH メソッドでは、アップロードする文書のコンテンツをここに指定する

  • DELETE メソッドでも同様にコンテンツを送ることができる

  • リクエストボディの解釈は、サーバサイドのアプリケーションに任されておりHTTPサーバ自体は関与しない。 基本的なプロトコルの実装では、すべてのメソッドで、リクエストボディを送信することができる。つまり、例えば GET メソッドでリクエストボディを送ってもエラーにはならない。しかし、メソッドと言う考え方を導入したからには、4つのメソッド POST/PUT/PATCH/DELETE 以外ではリクエストボディは送信しないでね、という約束の下に、ルール付けが行われているに過ぎない

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

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

:trophy:マルチパートの例

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

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

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

レスポンスメッセージ

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

[...] は省略可能なことを示す

:trophy:ステータスライン

<HTTPバージョン> _ <ステータス番号> _ <理由>\r\n

:trophy:レスポンスヘッダ

  • 基本、送り(リクエスト)と帰り(レスポンス)は同じ
  • 各要素の区切りはコロン(:)+空白(0x20)1文字
リクエストヘッダ1: 値\r\n
リクエストヘッダ2: 値\r\n
    :
ヘッダ名         概要
リクエストとレスポンスの両方で利用される共通のヘッダ
Content-Type レスポンスのコンテンツタイプを示すヘッダ。
Transfer-Encoding コンテンツの転送方法を示すヘッダ。
Content-Length コンテンツのサイズを示すヘッダ。
レスポンスヘッダに特有のヘッダ(一部)
Location リダイレクト先URLを指定するヘッダ。
Set-Cookie クッキーを設定するヘッダ。
Server サーバーの情報を指定するヘッダ。

:trophy:区切り(ヘッダとボディの区切り)

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

:trophy:レスポンスボディ

  • これもまた、送り(リクエスト)と帰り(レスポンス)は同じ

メッセージ全体の長さ

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

Content-Length、Transfer-Encoding 共にないケース

  • リクエストボディが存在しない場合は、Content-Length も、Transfer-Encoding も無い(GETHEADDELETEOPTIONSメソッドなど)

Content-Length、Transfer-Encoding 共にあるケース

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

Content-Length のみのケース

  • Content-Length には、文字エンコーディングでエンコードした後の、ボディの長さが指定されている(10進数)
  • サイズの1単位は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 が確定できないケース)が多い
  • チャンクは、チャンクヘッダチャンクデータに分かれ、交互に定義される
  • チャンクは、ボディ(リクエストボディやレスポンスボディ)に指定され、その形式は、
チャンクデータの形式
チャンクヘッダ\r\n
チャンクデータ\r\n
 :
0\r\n
\r\n(空行) ←ここで終わり
  • 最後の0\r\nは、必ず指定され、チャンクサイズが 0 であることを示し、続くチャンクデータは存在しない。また、最後の空行(\r\n)も必ず最後に付く
  • 各要素の末尾の復帰・改行は要素の終わりを示すのではなく、次に続く要素との区切りを示す。
  • チャンクヘッダは、16 進数表記のサイズを記述する
    • 1バイトなら 1、46バイトなら 2e など
  • チャンクデータは Transfer-EncodingBase64 などの指定がない限りは、 直のバイナリデータが指定される
  • 容量が多い場合に圧縮転送するケースがあり、Transfer-Encodingcompressgzip などが指定される
  • Transfer-Encodinggzip がある場合は、元のデータが gzip 形式で、それをチャンク単位で分割したデータが連続する

ここで無駄な豆知識

  • 何ゆえに1オクテットという単位を使うかと言うと、CISCなどのプロセッサにおいては、1バイトが32ビットや64ビット、詰まり最小単位が64ビットだったりするケースがある
  • すなわち、世界は、1バイト=8ビット固定ではないのである
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?