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 における「コネクション」とは、メッセージ経路全体ではなく隣接ノード間 (クライアントとプロキシ間など) のコネクションのことを指す
HTTP/2 理解メモ同様に、自作プロキシ (Nekoxy2) で実装した際の HPACK (RFC7541) への自分なりの理解をまとめたものです。
解説とかは他サイトのほうが良いかと。
HPACK は RFC の Appendix に具体例がたくさん載っているので、分からなくなったらそこを眺めると良いでしょう。
※RFC7541 ではバイト数は厳密に「オクテット数」と表現されているが、ここではバイト数とオクテット数は同じ意味とする
自作プロキシ (Nekoxy2) の HTTP/2 (RFC7540) 対応にあたっての私なりの理解をまとめました。
HTTP/2 の解説自体は他サイトのほうがわかりやすかったり正確だったりすると思うので、あくまでも参考程度に。
RFC においては SHOULD とか MUST とかの違いは重要ですが、ここに書くの面倒なので必要に応じて調べましょう。
HPACK(RFC7541) については RFC も別だし長いし記事を分けます。
→ HPACK 理解メモ – CAT EARS
.NET Core 3.0 Preview 4 で HttpClient
の HTTP/2 サポートが追加されたようですね。
Announcing .NET Core 3 Preview 4 | .NET Blog
今までの .NET Standard では SslStream 自体が HTTP/2 の事実上必須な機能(ALPN)に対応しておらず、.NET Standard 2.1 でようやくサポートされる見込みだったため、どうやらちゃんとくるようで一安心というところです。
(HttpClient
についてはこれのせいかは不明ですが…… SocketsHttpHandler
が HTTP/2 対応していなかったのは確か)
しかしながら .NET Framework 4.8 の方は .NET Standard 2.0 止まりになることになっているので、HTTP/2 サポートは今後あるのかどうかすら怪しいですね……
Given many of the API additions in .NET Standard 2.1 require runtime changes in order to be meaningful, .NET Framework 4.8 will remain on .NET Standard 2.0 rather than implement .NET Standard 2.1.
Announcing .NET Standard 2.1 | .NET Blog
とはいえ .NET Standard はあくまでも API 標準を定めただけで実装は各プラットフォーム依存ですから、動くようになる可能性はあるかも?
とりあえず .NET Framework は 4.8 の時点では HttpClient
+ HttpClientHandler
では動作しそうにありません。
BCL 以外でなら WinHttpHandler を使い、HttpRequestMessage
のバージョンを明示的に指定することで、利用できます(Win10 1607↑ + .NET FW 4.6↑ に限る)。
参考: How to make the .net HttpClient use http 2.0?
実は Core 2.0 までも Windows 環境に限れば WinHttpHandler に丸投げしていたようで、HttpClient
で HTTP/2 が動作します。
Core 2.1 以降 SocketsHttpHandler に移行し、環境依存を減らした為、HTTP/2 に一時的に非対応となっていたようです。
HttpClient の HTTP/2 対応
- .NET Framework
- HttpClientHandler では利用不可
- WinHttpHandler (要NuGet & Win10 1607)を利用すれば利用可
- .NET Core
- ~2.0 : Win10 1607~のみ利用可?
- WinHttpHandler を使ってる模様
- 2.1~2.2 : 利用不可
- SocketsHttpHandler に移行したため
- ただし WinHttpHandler を利用するよう構成すれば Windows では利用可
- 3.0~ : 利用可
- SocketsHttpHandler の HTTP/2 対応?
- ~2.0 : Win10 1607~のみ利用可?