naoki86star

インターネットの片隅でなにかしら書いてみる

WebSocket connection to 'wss://abc.balus.xyz/ws' failed:

 I love Tornadoである、それは置いておいて。tornado.websocket.WebSocketHandelerのおかげで数メソッド定義するだけでwebsocketサービスを動かし始めることができる。

 でもって、websocketクライアントからの接続を制限する仕掛けを考えてみたとき、Sec-WebSocket-Protocolヘッダを利用してみようやってみた。Sec-WebSocket-Protocol:を試してみようと考えた理由は、javascriptのWebSocket()を使ったときにヘッダに何か入れようとするとこれしかなさそうに見えているので。
 pythonとかjavaとかのクライアントであれば、任意のヘッダ定義しちゃって、tornado-server側でそれを取り出して101応答なり403応答なり選択動作できそうだけども、javascriptも想定しておこうとするならSec-WebSocket-Protocol:しか選択肢がなさそうと考えたからである。*1
 でもって、pythonとかjavaでは、接続時にSec-WebSocket-Protocol:をサーバーに渡すwebsocketのクライアント書いて、動いてくれた。空とか所望でない値が来たら403応答をクライアント側に戻す実装が可能である。


 同じようなことをjavascriptで書いてブラウザからつないだ時、開発者ツールコンソールに冒頭のエラーがでた。MDNのサンプルにあるような初歩的なスクリプト、ただしprotocols引数付き。

var ws = new WebSocket('wss://abc.balus.xyz/ws', [ 'abc', 'xyz' ]);
ws.onopen = function(e) {}
ws.onclose = function(e) {}
ws.onmessage = function(e) {}

 サーバは101応答を戻したというログを吐いてるのだけども、ブラウザjavascript側では onopen()が呼ばれず onclose()は呼ばれていた。ブラウザの開発者ツールnetworkのrequest/responseにはresponseが出力されていないしstatusは'finish'になっていた。
 普通は、failed:の後ろに説明が入ってくれてるはずがでてこない。いつもお世話になってるstackoverflowで、Edgeだと少し詳細にでてくれるよってあったのでそっちで試してみたら、ビッグヒントがでてきた。

Error during WebSocket handshake: Sent non-empty 'Sec-WebSocket-Protocol' header but no response was received

 結論としては、(今今なブラウザ向けには)レスポンスに"選択したプロトコル"をSec-WebSocket-Protocol:で戻してあげるといいらしい。

 Sec-WebSocket-Protocol:は用途としては、"プロトコル名"*2みたいなのを入れる・WebScoketで複数提案して応答で一つ戻ってきたのに追従した処理にする、のように想定してるのだろう。ここにtokenみたいなのを入れるのはちょっと筋違いか。

 でも、WebSocket()だと、なにかヘッダ追加したい場合には、第二引数のprotocolでしか渡せないようだし。かといって、今、動くからと、横着な使い方していると、数年するとなんかしら破綻してたりするんですよね。。。

 ちなみに、証明書ちゃんとやってるSSL接続で試していて、最初wss:(not ws:)なにかあるかは疑ったけどもそういうことはなし。*3

*1:WebSocket()からは、Cookie:を入れてきてくれるのでそれでうまいこと考えてみる選択肢もありそう。

*2:Chat-v1, Chat-v2みたいな感じ?

*3:グローバルで試してると、ガンガンやんちゃな接続来る。。