NGINXとSSLとWebSocketのバグまとめ

前回のブログから大分ぶっ飛んだ工程の話になりますが、SSL設定はNGINXで行いました。
弊社のENishi環境はSECOMのSSLを使用しています。

本番環境にサーバー証明書の設定を行ったので、今日はそれについて。
基本的な設定は下記が参考になりました。

で、上記のとおりにやっても、HTTP接続では起こらなかったバグが出始めたので、バグった事と対応をまとめておきます。

①バグ:Assetsが見えなくなる


テンプレート上にこういう風に書いていた静的リソースが軒並み見えなくなりました。
このせいで、画像やCSS、JSを一気に読めなくなるような状態に。THE☆悪夢……。
対応:proxy_passで直しました。


location /assets {
proxy_pass http://my-backend/assets;
}

②バグ:WebSocketがバグる

WebSocketはJS内で下記のようにベタ打ちでパスを書く事は環境の違いやパスが変わった時にカバーしにくいので、好ましくありません。


ws = new WebSocket(“localhost:9000/sample/sockets”);

Playでは、上記の例だと@routes.Application.SocketSample.WebSocketURLのような感じで、自動でURLを生成してくれる仕組みがあるので、基本的にこれを用います。
JSからはアノテーションが使えないので、WebSocketURLの一覧を定義したJSON Actionを用意し、JSからはgetJSONで取得するようにします。


サーバー(Play-Controller):
val result =
Json.obj(
"chatSocket" -> routes.ChatController.chatSocket.webSocketURL(),
"connectsSocket" -> routes.ConnectsController.connectsSocket.webSocketURL(),
"noticeSocket" -> routes.NotificationController.noticeSocket.webSocketURL(),
)
Ok(result)
クライアント(JS):
$.getJSON(
'/userJson', // アクセス先のURL
function(data, status) {
ws = new WebSocket(data.chatSocket);
ws.onmessage = function(e) { roomCast(e);}	//roomCast←function
ws.onopen = function(e) { socketOpened(); }	//socketOpened←function
}
);

http接続のときはこれでよかったんですが、httpsにした途端JS側でエラーを吐くように。
現象はこちら。
http://stackoverflow.com/questions/9745249/html5-websocket-with-ssl

ここにもある通り、WebSocketのURLはSSL使った時はws://ではなく、wss://でないとダメなんですね。し、知らなかった。。。いや、まあそりゃそうか。
幸いPlayのWebSocketURL()には引数にフラグを渡せば、SSL接続にしてくれるので、これを活用して下記のように直しておきます。


サーバー(Play-Controller):
// implicitのrequestからドメインを取得。本番サーバーかどうかのフラグを宣言
val isProduction = request.domain contains "enishi.co"
val result =
Json.obj(
"chatSocket" -> routes.ChatController.chatSocket.webSocketURL(isProduction),
"connectsSocket" -> routes.ConnectsController.connectsSocket.webSocketURL(isProduction),
"noticeSocket" -> routes.NotificationController.noticeSocket.webSocketURL(isProduction),
)
Ok(result)

とりあえず、これでJS側でエラーは吐かなくなりました。。。ほっ。

んが、しかし。

今度はWebSocketがすぐ切断される問題が発生!!
チャットサービスでWebSocketが5分おきに切断されていたらお話にならない!!

とりあえず、JS側では、下記の処理を追加。


$.getJSON(
'/userJson', // アクセス先のURL
function(data, status) {
ws = new WebSocket(data.chatSocket);
ws.onmessage = function(e) { roomCast(e);}	//roomCast←function
ws.onopen = function(e) { socketOpened(); }	//socketOpened←function
// クローズ時に再接続を試行
ws.onclose = function(e) { ws = new WebSocket(data.chatSocket); }
}
);

乱暴ですが、JSで下記の処理もやっておきます。
bodyのホバー時に切断されていたら再接続するように。


$(‘body’).hover(function(){
if(ws.status != 1){
ws = new WebSocket(socketPass);
}
});

参考:WebSocketのステータスについて。。。
http://d.hatena.ne.jp/kazuhooku/20140221/1392965892

そもそもすぐにCloseされてしまうのは、サーバー側のタイムアウト設定の問題かなと思い、調べてみたらやっぱりそうでした。nginxで下記の設定したら切断時間を設定できました。
http://stackoverflow.com/questions/12102110/nginx-to-reverse-proxy-websockets-and-enable-ssl-wss

これでSSLとNGINXによるWebSocketの問題はとりあえず一通り解消されました。ふう~~~。

Scalaエンジニアの求人