19

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 サーバーに到達していません。

最終的な目標は、noVNC/websockify を組み込み、ネットワーク上の複数の VNC サーバーへのブラウザー ベースのクライアント アクセスを提供することです。Websocket をリバース プロキシする方法を理解するための助けをいただければ幸いです。

4

3 に答える 3

29

IIS 8.5 で ARR 3.0 を使用してこれと同じことを達成しようとしていたところ、最終的に問題が見つかりました。Microsoft の Erez Benari によると、これは可能です:

WebSocket をサポートするには、IIS に WebSocket 機能をインストールする必要がありますが、その他の構成やアクションは必要ありません。サーバー マネージャーの [役割と機能の追加] を使用して機能をインストールします。それが完了すると、ARR 3.0 は要求を適切に処理します。

テストとして、WebSocket 用の Node.js サーバーをセットアップしました。

const WebSocketServer = require('ws');
const wss = new WebSocketServer({ ポート: 3011 });
関数 sendWSMessage(msg) {
    wss.clients.forEach((クライアント) => {
        client.send(メッセージ);
    });
}
setInterval(関数() {
    sendWSMessage('こんにちはクライアント');
}、3000);

簡単なテストページとともに:

var websock = new WebSocket('ws://localhost:3011');
websock.onmessage = 関数 (イベント) {
    console.log(イベント.データ);
};
websock.onopen = 関数 (イベント) {
  websock.send("こんにちはサーバー");
};

次に、ローカル マシンに ARR リバース プロキシを設定します。ローカルホストの「wstest」ディレクトリの web.config ファイルに次のように記述します。

<?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 サーバーにすべてのトラフィックが転送されますws://localhost:3011。経由でプロキシ経由ws://localhost/wstestで接続しようとすると、リクエストが Node.js サーバーに到達し、アップグレードが行われ、接続が確立されます。

Chrome が送信するもの:

ws://localhost/wstest HTTP/1.1 を取得します。
ホスト: ローカルホスト
接続: アップグレード
プラグマ: no-cache
キャッシュ制御: キャッシュなし
アップグレード: ウェブソケット
オリジン: file://
Sec-WebSocket-バージョン: 13
ユーザーエージェント: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (Gecko のような KHTML) Chrome/51.0.2704.84 Safari/537.36
Accept-Encoding: gzip、deflate、sdch
Accept-Language: en-US,en;q=0.8
Sec-WebSocket キー: 4ufu8nAOj7cKndASs4EX9w==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

Node.js サーバーは以下を受け取ります。

キャッシュ制御: キャッシュなし
接続: アップグレード
プラグマ: キャッシュなし
アップグレード: ウェブソケット
受け入れエンコード: gzip、deflate、sdch
受け入れる言語: en-US,en;q=0.8
ホスト: ローカルホスト:3011
最大転送: 10
ユーザーエージェント: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (Gecko のような KHTML) Chrome/51.0.2704.84 Safari/537.36
オリジン: ファイル://
秒-websocket-バージョン: 13
sec-websocket-key: fBkTwAS9d/unXYKDE3+Jjg==
sec-websocket-extensions: permessage-deflate; client_max_window_bits
x-original-url: /wstest
x-転送-for: [::1]:54499
x-arr-log-id: a0b27458-9231-491d-b74b-07ae5a01c300

Node.js サーバーは次のように応答します。

HTTP/1.1 101 スイッチング プロトコル
アップグレード: ウェブソケット
接続: アップグレード
Sec-Websocket-Accept: yep8mgQACAc93oGIk8Azde4WSXk=
Sec-WebSocket-Extensions: permessage-deflate

最後に、Chrome は以下を受け取ります。

HTTP/1.1 101 スイッチング プロトコル
アップグレード: ウェブソケット
Sec-WebSocket-Accept: CBSM8dzuDoDG0OrJC28nIqaw/sI=
Sec-WebSocket-Extensions: permessage-deflate
X-Powered-By: ARR/3.0
接続: アップグレード
X-Powered-By: ASP.NET
日付: 2016 年 6 月 10 日 (金) 21:16:16 GMT
終了時間: 17:16:16.148
受信バイト: 0
送信バイト: 0

だから今、彼らはつながっています。これはすべて問題ないように見えます。唯一の顕著な違いは、Sec-WebSocket-Key と Sec-WebSocket-Accept が IIS または ARR プロキシによって両方向で変更されることです。

しかし... WebSocket フレームがプロキシを通過することはありません! Chrome は、アップグレード リクエストに対して肯定的なフィードバックを受け取ると、WebSocket メッセージ フレームを送信し、サーバーからのメッセージを待っています。Node.js サーバーはそのフレームを送信し、エラーは発生しませんが、Chrome で受信されることはありません。Chrome が送信したメッセージが Node.js によって受信されることはありません。ARR/IIS が WebSocket フレームを両方向にドロップしているようです。

メッセージごとの圧縮用の WebSocket 拡張機能である permessage-deflate 拡張機能をサポートしていることを、Chrome がサーバーにどのように伝えているかに注意してください。サーバーは、permessage-deflate もサポートしていると応答しているため、ブラウザーとサーバーがメッセージを相互に送信するときに、この圧縮拡張機能を使用します。ただし、真ん中の人、ARR は、明らかにこの圧縮をサポートしていません。サーバーで permessage-deflate のサポートをオフにすることで、実際の WebSocket フレームが問題なくプロキシを通過できるようになりました。

const wss = new WebSocketServer({ ポート: 3011, perMessageDeflate: false });

Sec-Websocket-Extensions問題は、ARR 3.0 がヘッダーをサポートしていないため、ヘッダーが単純に通過できるようになっていることだと思います。しかし、ARR はネゴシエーションに関与せず、圧縮されたメッセージの受け渡しをサポートしていないことを 2 つの当事者に伝える方法がないため、クライアントとサーバーの間でこのヘッダーのネゴシエーションを許可するのは正しくありません。願わくば、いつの日か、ARR がそれ自体とクライアントの間でネゴシエーションを行い、その後、ARR とサーバーの間で別のネゴシエーションを行うことによって、拡張機能を適切に処理できるようになることを願っています。現在のところ、クライアントとサーバーが相互にネゴシエートしているだけで、このエラーが発生します。

于 2016-06-10T21:29:47.973 に答える
8

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

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

そうすれば、宛先は面倒な permessage-deflate を受け取りません:)

于 2017-11-16T12:12:21.993 に答える