2010年11月11日木曜日

WebSocketとproxy

毎度のWebSocketネタ
proxyが通らないよ!と言うtweetやら何やらが多く出回っていますが、それに関して。

結論から言うと、大抵の場合、通ります。

hybiでの仕様では、SPEC-02位からちゃんと記述がありますが、それ以前もchrome(chromium)では、ちゃんとproxyを考慮した作りになっていました。
パケットの流れとしては、以下の通り

ブラウザ->proxy
CONNECT hoge.com:80 HTTP/1.1
Host: hoge.com
Proxy-Connection: keep-alive

proxy->ブラウザ

HTTP/1.1 200 Connection established

ブラウザ->proxy->WebSocket Server

GET /fuga HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: hoge.com
Origin: http://hoge.com
Sec-WebSocket-Key1: 4 29 " 43b Q8 9 30 9
Sec-WebSocket-Key2: 3n  110913  6 25y

.?4.Q...

WebSocket Server->proxy->ブラウザ

HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Location: ws://hoge.com/fuga
Sec-WebSocket-Origin: http://hoge.com

....Vz.3L ...r...
ただ、proxyによっては通らないものがあるのも事実です。
initial handshakeのGET methodについているbodyが原因です。(イケてない仕様だと思うのですが…)
見ての通り、initial handshake requestには、bodyに8byteのdata(Sec-WebSocket-Key3相当)が付加されていますが、ヘッダにContent-Length及びTransfer-Encodingは付いていません。
この場合、RFC2616に対して、明らかに不正なパケットとなります。(参照: http://www.studyinghttp.net/header#Content-Length)

また、Upgrade headerについての扱いをどう見るか、ですが、(参照: http://www.studyinghttp.net/header#Upgrade)

initial handshake時のrequest packetにおけるUpgrade headerについては、まだUpgradeされていないと考えるべきだと思っています。
つまり、

GET /fuga HTTP/1.1
Upgrade: WebSocket
は、まだWebSocket ProtocolにUpgradeされておらず、HTTPの仕様に縛られるべきだと思うのです。

その為、Content-Length headerすら付けず、bodyに、8byteのdataを付加するようなpacketは、httpdとしても、proxyとしても扱いに困ります。
(例えば、Upgrade: WebSocketが付いたpacketは絶対にWebSocket Handshakeだ、と仮定したとすると、httpdおよびproxyは、bodyの8byteを読めるまでしばらく待っている必要があります。この場合、、bodyを付加せずheaderだけ投げつければ簡単にDoSが可能となります。例え、httpdおよびproxyがpoll timeoutなどを設けていたとしても)

以上のような理由で、proxy、httpdの設定によっては、handshake packetが捨てられる場合があります。

また、Windows版のブラウザでは、initial handshake requestが2 packetに分割して投げられます。
(Chrome, Safari, FireFox 4βで確認済)

ブラウザ->WebSocket Server

GET /fuga HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: hoge.com
Origin: http://hoge.com
Sec-WebSocket-Key1: 4 29 " 43b Q8 9 30 9
Sec-WebSocket-Key2: 3n  110913  6 25y

ブラウザ->WebSocket Server
.?4.Q...
まず、ヘッダ部分が送信され、数msおいてbody部分が送信される形です。

この場合は特に、headerにContent-Lengthが付いていないこと、methodがGETであることからproxyやhttpdでは、2 packet目の8byteのdataは、本来どのようなpacketなのか判断が付かないことになります。(捨てるのが普通でしょう)
実際は、proxyに対して、CONNECT、keep-aliveしているので、proxyはpass-throughしてくれて、ちゃんとWebSocket Serverまで届きますが。

と、言うことで、基本的にはproxy透過だよ~っと覚えていてもらえば良いかと思います。
このあたりはSPEC-03以降でちゃんと定義されていると思いますが、現状のご報告まで。

ではでは。

あぁ、追記として。
Safariには、proxy設定とWebSocketの利用に関してBUGが存在します。(一応、レポートは送付しましたが、最近のSafariでは直ってるのかしら?)
企業内のネットワークに参加しているNote PC(持ち運ぶPC)などではよく起こると思うのですが、

  1. そのPCでproxyを利用する設定になっている(自動設定スクリプトなどを含む)
  2. proxyの無い環境に持っていく。(自動設定スクリプトだと、proxyを利用しない状況になる)
  3. WebSocketを利用したsiteへ赴く
  4. ブラウザクラーッシュ
こんなときは、proxy設定を外してあげてください。

0 件のコメント:

コメントを投稿