web-dev-qa-db-ja.com

IIS 8のWebSocketsリバースプロキシ

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をリバースプロキシする方法を理解するための助けがあれば、高く評価されます。

14
cydc

私は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がクライアントとの間でネゴシエートし、それからサーバーとの間で別のネゴシエーションを行うことで、拡張機能を適切に処理できるようになるでしょう。現在のところ、クライアントとサーバーが互いにネゴシエートしているだけなので、このエラーが発生します。

23
jowo

@jowoが述べたように、IIS8/8.5はSec-Websocket-Extensionsをサポートしていないようです。そうは言っても、私が適用した回避策は、問題のサーバー変数を単に書き換えることです。最初にHTTP_SEC_WEBSOCKET_EXTENSIONSを許可サーバー変数リストに追加してから、それをルールに追加します。

<serverVariables><set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="" /></serverVariables>

そうすれば、宛先は面倒なpermessage-deflateを受け取らないでしょう:)

6
emboss64

TLDRの目的は次のとおりです。

  1. uRLがwsおよびwssプロトコルを正しくリダイレ​​クトする
  2. オプションperMessageDeflateを渡して、ノードサーバーソケットioの圧縮を無効にします。 IISプロキシは圧縮をサポートしていないため、APR 3で圧縮を無効にします
const io = require("socket.io")(server, {
  transports: ["websocket", "polling"],
  perMessageDeflate: false 
});
0
kabapy