IISのリバースプロキシを介してwebsocketsサーバー(websockify)に接続しようとしています。 IISとwebsocketsサーバーは同じ物理サーバーにあります(Windows Server 2012 R2、IIS 8.5、ARR 3、Websockets有効)。これに関するいくつかの質問と、これはIIS 8およびARR 3で動作するはずですが、実際の解決策はまだありません。IISでhttp/httpsリバースプロキシを使用した経験がありますが、これは私の最初にWebSocketを操作します。
例えば:
元のURL:ws://10.2.1.10/websockify
リバースプロキシはこれを次のように変換する必要があります:ws://10.2.1.10:5901/websockify
Web.configの非常に一般的なサンプルルール:
<rewrite>
<rules>
<rule name="WS reverse proxy" stopProcessing="true">
<match url="(.*)" />
<conditions> <add input="{CACHE_URL}" pattern="^(.+)://" />
</conditions>
<action type="Rewrite" url="{C:1}://10.2.1.10:5901/websockify"/>
</rule>
</rules>
</rewrite>
Failed Request Traceによると、URLは翻訳されたように見えますが、何らかの理由で、10.2.1.10:5901のWebSocketサーバーに到達しません。
最終目標は、ネットワーク上の複数のVNCサーバーへのブラウザーベースのクライアントアクセスを提供するためにnoVNC/websockifyを組み込むことです。 WebSocketをリバースプロキシする方法を理解するための助けがあれば、高く評価されます。
私はIIS 8.5でARR 3.0を使用してこれと同じことを達成しようとしていましたが、最終的に問題が見つかりました。 MicrosoftのErez Benari氏によれば、 これは可能です :
WebSocketサポートには、IISにWebSocket機能をインストールする必要がありますが、他の構成やアクションは必要ありません。サーバーマネージャーの役割と機能の追加を使用して機能をインストールすると、ARR 3.0は要求を適切に処理します。
テストとして、Node.jsサーバーをWebSocket用に設定しました。
const WebSocketServer = require( 'ws'); const wss = new WebSocketServer({port:3011}); function sendWSMessage(msg){ wss .clients.forEach((client)=> { client.send(msg); }); } setInterval(function(){ sendWSMessage( 'hello client'); }、3000);
簡単なテストページに加えて:
var websock = new WebSocket( 'ws:// localhost:3011'); websock.onmessage = function(event){ console.log(event.data); }; websock.onopen = function(event){ websock.send( "hello server"); };
次に、ローカルホスト上の "wstest"ディレクトリのweb.configファイルに次のようにして、ローカルマシンにARRリバースプロキシを設定します。
_<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="WebSocketTestRule" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{CACHE_URL}" pattern="^(.+)://" />
</conditions>
<action type="Rewrite" url="{C:1}://localhost:3011/" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
_
これにより、_//localhost/wstest
_のすべてのトラフィックがポート3011のNode.jsサーバーに転送されます。Nodeサーバーは、_ws://localhost:3011
_を介して直接接続すると機能します。 _ws://localhost/wstest
_を介してプロキシ経由で接続しようとすると、リクエストがNode.jsサーバーに到達し、アップグレードが行われて、接続が確立されます。
Chromeからの送信:
GET ws:// localhost/wstest HTTP/1.1 ホスト:localhost 接続:アップグレード プラグマ:no-cache キャッシュ制御:no-cache アップグレード:websocket 元:file:// Sec-WebSocket-Version:13 ユーザーエージェント:Mozilla/5.0(Windows NT 6.3; WOW64)AppleWebKit/537.36(KHTML、like Gecko)Chrome/51.0.2704.84 Safari/537.36 Accept-Encoding:gzip、deflate、sdch Accept-Language:en-US、en; q = 0.8 Sec-WebSocket-Key:4ufu8nAOj7cKndASs4EX9w == Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Node.jsサーバーは以下を受け取ります。
cache-control:no-cache connection:upgrade pragma:no-cache upgrade:Websocket accept-encoding:gzip、deflate 、sdch accept-language:en-US、en; q = 0.8 Host:localhost:3011 max-forwards:10 user-agent:Mozilla /5.0(Windows NT 6.3; WOW64)AppleWebKit/537.36(KHTML、like Gecko)Chrome/51.0.2704.84 Safari/537.36 起源:file:// sec-websocket-version:13 sec-websocket-key:fBkTwAS9d/unXYKDE3 + Jjg == sec-websocket-extensions:permessage-deflate; client_max_window_bits x-original-url:/wstest x-forwarded-for:[:: 1]:54499 x-arr-log-id:a0b27458-9231-491d -b74b-07ae5a01c300
Node.jsサーバーは次のように応答します。
HTTP/1.1 101スイッチングプロトコル アップグレード:websocket 接続:アップグレード Sec-Websocket-Accept:yep8mgQACAc93oGIk8Azde4WSXk = Sec-WebSocket-Extensions :permessage-deflate
そして最後にChromeが受け取ります:
HTTP/1.1 101スイッチングプロトコル アップグレード:Websocket Sec-WebSocket-Accept:CBSM8dzuDoDG0OrJC28nIqaw/sI = Sec-WebSocket-Extensions:permessage-deflate X-Powered-By:ARR/3.0 Connection:Upgrade X-Powered-By:ASP.NET Date:Fri、10 Jun 2016 21:16: 16 GMT EndTime:17:16:16.148 ReceivedBytes:0 SentBytes:0
これで接続されました。これはすべて正常に見えますが、唯一の顕著な違いは、Sec-WebSocket-KeyとSec-WebSocket-AcceptがIISまたはARRプロキシのいずれかによって双方向で変更されることです。
しかし...プロキシを介してWebSocketフレームが作成されることはありません! Chromeは、アップグレードリクエストについて肯定的なフィードバックを受け取ると、WebSocketメッセージフレームを送信し、座ってサーバーからのメッセージを待機します。 Node.jsサーバーがフレームを送信し、エラーは発生しませんが、Chromeでフレームが受信されることはありません。 Chromeが送信したメッセージがNode.jsで受信されることはありません。 ARR/IISがWebSocketフレームを両方向にドロップしているようです。
Chromeがサーバーに、メッセージごとの圧縮用のWebSocket拡張機能であるpermessage-deflate拡張機能をサポートしていることを通知していることに注意してください。サーバーはpermessage-deflateもサポートしていると応答しているため、ブラウザーとサーバーが互いにメッセージを送信するときに、この圧縮拡張機能を使用します。しかし、真ん中の男、ARRは明らかにこの圧縮をサポートしていません!サーバーでpermessage-deflateのサポートをオフにすることで、実際のWebSocketフレームがプロキシを完全に通過できるようになります。
const wss = new WebSocketServer({port:3011、perMessageDeflate:false});
問題は、ARR 3.0が_Sec-Websocket-Extensions
_ヘッダーをサポートしていないため、ヘッダーが単純に通過できるようになっていることだと思います。ただし、ARRはネゴシエーションに関与しておらず、圧縮されたメッセージの受け渡しをサポートしていないことを2つのパーティに通知する方法がないため、このヘッダーをクライアントとサーバー間でネゴシエートできるようにするのは誤りです。うまくいけば、いつかARRがクライアントとの間でネゴシエートし、それからサーバーとの間で別のネゴシエーションを行うことで、拡張機能を適切に処理できるようになるでしょう。現在のところ、クライアントとサーバーが互いにネゴシエートしているだけなので、このエラーが発生します。
@jowoが述べたように、IIS8/8.5はSec-Websocket-Extensionsをサポートしていないようです。そうは言っても、私が適用した回避策は、問題のサーバー変数を単に書き換えることです。最初にHTTP_SEC_WEBSOCKET_EXTENSIONSを許可サーバー変数リストに追加してから、それをルールに追加します。
<serverVariables><set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="" /></serverVariables>
そうすれば、宛先は面倒なpermessage-deflateを受け取らないでしょう:)
TLDRの目的は次のとおりです。
perMessageDeflate
を渡して、ノードサーバーソケットioの圧縮を無効にします。 IISプロキシは圧縮をサポートしていないため、APR 3で圧縮を無効にしますconst io = require("socket.io")(server, {
transports: ["websocket", "polling"],
perMessageDeflate: false
});