C# Tokyo LT 大会 2020/02 - connpass で話したやつです。
参照 RFC7230 6.7
- Upgrade ヘッダーは、1つの HTTP/1.1 コネクション上で他のプロトコルへ移行するのに用いられる
- HTTP/2 にはこの仕組みはなく、CONNECT メソッド拡張の protocol 擬似ヘッダーにて実現される [RFC8441 4]
- クライアントは、Upgrade ヘッダーに優先度の高い順にプロトコル名のリストを設定し、送信する
- サーバーは、リストから移行するプロトコルを選択して Upgrade ヘッダーに設定し、101 Switching Protocols レスポンスを返す
-
サーバー側からプロトコルの移行を指示する場合、426 Upgrade Required レスポンスを用いる
-
Upgrade ヘッダーは hop-by-hop ヘッダーであり、指定されたプロトコルに対応していない中継者による回送を防ぐために、Connection ヘッダーに
upgrade
を指定しなければならない - サーバーが Upgrade と Expect を同時に受信した場合、101 Switching Protocols より先に 100 Continue を送信しなければならない
- 利用できるプロトコルのリストは、IANA に登録されなければならない
- Hypertext Transfer Protocol (HTTP) Upgrade Token Registry
- 事実上 WebSocket にしか用いられていないのでは
HTTP フォワードプロキシが介在する場合、単に通信を中継するだけでなく通信される内容も変化する。
もちろんプロキシの機能として HTTP メッセージの改変が行われる場合はあるが、それ以外の場合でも変化する部分はある。
- request-target の形式
- クライアントが送信するリクエストラインが変更される
- サーバー直アクセス:
GET /hoge HTTP/1.1
- プロキシ経由アクセス:
GET http://example.com/hoge HTTP/1.1
- Transfer Codings の変更
- chunked から Content-Length 形式に変更される、圧縮が解除されるといったことはあり得る
- HTTP コネクションの持続的接続の有無
- client-proxy 間と proxy-server 間では TCP コネクションの持続方法が異なる場合がある
いずれも HTTP の意味論的な変更ではなく、通信上の都合により変更が許されている部分である。
参照 RFC7230 5.3
- HTTP リクエストメッセージのリクエストラインは、
method request-target HTTP-version
で構成されるGET / HTTP/1.1
- この内の request-target にはいくつかの形式が存在する
型式名 | 例 | 用途 |
---|---|---|
origin-form | GET /where?q=now HTTP/1.1 |
最もよくある形式 |
absolute-form | GET http://www.example.org/pub/WWW/TheProject.html HTTP/1.1 |
プロキシ向けリクエストはこの形式でなければならない |
authority-form | CONNECT www.example.com:80 HTTP/1.1 |
CONNECT メソッドでは必須。CONNECT メソッド限定。 |
asterisk-form | OPTIONS * HTTP/1.1 |
OPTIONS メソッドでは必須。OPTIONS メソッド限定。リソースのワイルドカード的に用いられる。 |
このうち、absolute-form は原則プロキシ向けのリクエストにのみ用いられるが、サーバーは absolute-form を受容可能でなければならない。
理由は将来の HTTP バージョンでは全て absolute-form への移行を可能にするためとされている。
実際に HTTP/2 では、リクエストラインに相当する機能は擬似ヘッダーフィールドに変更されているものの、absolute-form に相当するだけの情報を持つようになっている。
しかし実際に absolute-form を送信すると正しく動作しないサーバーが非常に多い。
恐らくはリバースプロキシでのリライトルールあたりとの相性が悪いせいではないかと考えられる。
目次
概要
参照 RFC7230 4, 3.3.1
- HTTP メッセージボディーの転送は、HTTP/1.0 までは Content-Length で指定した長さのデータを転送する方式しかなかった
- 転送が終わるまでボディーデータの全てをメモリ上にとどめておく必要があるなどした
- HTTP/1.1 にて chunked transfer coding (chunked) ができるようになり、ボディーデータを分割して転送することができるようになった
- 事前に最終的な長さが分からなくても転送可能
- HTTP/2 には Transfer Codings の仕組み自体がない
- Stream に取って代わられたため
- Transfer Codings の方式は、TE および Transfer-Encoding ヘッダーにて指定する
- TE ヘッダーでクライアントが受入可能な方式を指定
- Transfer-Encoding ヘッダーで送信するメッセージの符号化方式を指定
- 受信者は chunked を復号化できなければならない [RFC7230 4.1]
- Transfer-Encoding は hop-by-hop ヘッダーであり、隣接ノード間にのみ適用される [RFC7230 3.3.1]
- 故に、プロキシなどの中継者は Transfer-Encoding を復号化し、Content-Length 方式や別の Transfer-Encoding に変換するなどしても良い
- Content-Encoding ヘッダーは end-to-end ヘッダーである点に注意
目次
概要
参照 RFC7230 6
- HTTP は、リクエスト・レスポンスのセットを TCP コネクション上で送受信する
- HTTP/1.0 の既定では、1 つのリクエスト・レスポンスが完了したら TCP コネクションは閉じられる
Connection: keep-alive
リクエストヘッダーにより TCP コネクションを閉じず再利用する持続的接続 (Persistent Connection) ができるようになった
- HTTP/1.1 では持続的接続は既定で有効であり、
Connection: keep-alive
は必要ない- HTTP/1.0 サーバーとの持続的接続を望む場合は送信される
- HTTP/1.1 における「コネクション」とは、メッセージ経路全体ではなく隣接ノード間 (クライアントとプロキシ間など) のコネクションのことを指す
自作プロキシ (Nekoxy2) の WebSocket (RFC6455) 対応にあたっての私なりの理解をまとめました。
概要
WebSocket 自体はかなり単純で、TCP 上で投げっぱなし全二重通信をする方法と、HTTP/1.1 からのプロトコル アップグレード方法 (WebSocket の開始方法) が定義されているだけです。
実際に何を通信するかはサブプロトコル等のアプリケーション側にお任せとなります。
WebSocket over HTTP/2 (RFC8441) では、この内アップグレード方法が HTTP/2 向けに新しく定義されただけで、通信方法は同一となります。
※ 詳細は HTTP/2 理解メモ 参照。
注意点としては、接続・切断については到達保証がありますが、それ以外のメッセージには到達保証がないため、必要に応じてアプリケーション等で実装する必要があります。
また HTTP が利用するコネクションを再利用する形となるため、経路上に非対応の透過プロキシなどが存在する場合に通信が失敗する可能性があります。
HTTP/2 理解メモ同様に、自作プロキシ (Nekoxy2) で実装した際の HPACK (RFC7541) への自分なりの理解をまとめたものです。
解説とかは他サイトのほうが良いかと。
HPACK は RFC の Appendix に具体例がたくさん載っているので、分からなくなったらそこを眺めると良いでしょう。
※RFC7541 ではバイト数は厳密に「オクテット数」と表現されているが、ここではバイト数とオクテット数は同じ意味とする