19

我正在尝试通过 IIS 上的反向代理连接到 websockets 服务器 (websockify)。IIS 和 websockets 服务器驻留在同一物理服务器上(Windows Server 2012 R2、IIS 8.5、ARR 3、启用了 Websockets)。我已经看到了一些关于此的问题,并且建议这应该与 IIS 8 和 ARR 3 一起使用,但目前还没有实际的解决方案。我对 IIS 中的 http/https 反向代理有一些经验,但这是我第一次尝试使用 websockets。

例如:

原网址: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>

根据失败的请求跟踪,该 url 似乎已被翻译,但由于某种原因,它没有到达位于 10.2.1.10:5901 的 websocket 服务器。

最终目标是结合 noVNC/websockify 以提供基于浏览器的客户端访问网络上的多个 VNC 服务器。任何帮助理解如何反向代理 websockets 表示赞赏。

4

3 回答 3

29

我一直试图在带有 ARR 3.0 的 IIS 8.5 上完成同样的事情,最终发现了问题。根据微软的 Erez Benari 的说法,这是可能的

WebSocket 支持需要在 IIS 上安装 WebSocket 功能,但不需要任何其他配置或操作。使用服务器管理器添加角色和功能安装该功能,一旦完成,ARR 3.0 将适当地处理请求。

作为测试,我为 WebSocket 设置了一个 Node.js 服务器:

常量 WebSocketServer = 要求('ws');
const wss = new WebSocketServer({ 端口: 3011 });
函数发送WSMessage(消息){
    wss.clients.forEach((client) => {
        客户端.send(msg);
    });
}
设置间隔(函数(){
    sendWSMessage('你好客户端');
}, 3000);

连同一个简单的测试页面:

var websock = new WebSocket('ws://localhost:3011');
websock.onmessage = 功能(事件){
    控制台日志(事件数据);
};
websock.onopen = 功能(事件){
  websock.send("你好服务器");
};

然后,我在本地机器上设置了一个 ARR 反向代理,在 localhost 上的“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
主机:本地主机
连接:升级
Pragma:无缓存
缓存控制:无缓存
升级:websocket
来源:文件://
Sec-WebSocket-版本:13
用户代理:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
接受编码:gzip、deflate、sdch
接受语言:en-US,en;q=0.8
Sec-WebSocket-Key:4ufu8nAOj7cKndASs4EX9w==
Sec-WebSocket-Extensions:permessage-deflate;client_max_window_bits

Node.js 服务器接收:

缓存控制:无缓存
连接:升级
杂注:无缓存
升级:Websocket
接受编码:gzip、deflate、sdch
接受语言:en-US,en;q=0.8
主机:本地主机:3011
最大前锋:10
用户代理:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36
来源:文件://
sec-websocket 版本:13
sec-websocket-key: fBkTwAS9d/unXYKDE3+Jjg==
sec-websocket-extensions:permessage-deflate;client_max_window_bits
x-原始网址:/wstest
x-转发-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
连接:升级
X-Powered-By: ASP.NET
日期:格林威治标准时间 2016 年 6 月 10 日星期五 21:16:16
结束时间:17:16:16.148
接收字节数:0
发送字节数:0

所以现在他们已经连接了。这一切看起来都不错,唯一明显的区别是 Sec-WebSocket-Key 和 Sec-WebSocket-Accept 被 IIS 或 ARR 代理双向更改。

但是...没有任何 WebSocket 帧可以通过代理!当 Chrome 收到对其升级请求的肯定反馈时,它会发送其 WebSocket 消息帧,然后它会坐下来等待来自服务器的消息。Node.js 服务器发送它的帧,没有发生错误,但 Chrome 永远不会收到它们。Node.js 永远不会收到 Chrome 发送的消息。似乎 ARR/IIS 正在双向丢弃 WebSocket 帧。

注意 Chrome 是如何告诉服务器它支持 permessage-deflate 扩展的,这是一个用于按消息压缩的 WebSocket 扩展。服务器响应它也支持 permessage-deflate,所以当他们浏览器和服务器互相发送消息时,他们使用这个压缩扩展。然而,中间的那个人,ARR,显然不支持这种压缩!通过在服务器上关闭对 permessage-deflate 的支持,实际的 WebSocket 帧现在可以完美地通过代理:

const wss = new WebSocketServer({ port: 3011, perMessageDeflate: false });

我认为问题在于 ARR 3.0 不支持Sec-Websocket-Extensions标头,因此它允许标头简单地通过。但是允许客户端和服务端协商这个header是错误的,因为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 回答
-1

TLDR 旨在:

  1. url 正确重定向 ws 和 wss 协议
  2. 禁用节点服务器套接字 io 中的压缩,传递选项perMessageDeflate. 在 IIS 代理中禁用压缩为 APR 3 不支持它
const io = require("socket.io")(server, {
  transports: ["websocket", "polling"],
  perMessageDeflate: false 
});
于 2020-03-16T21:50:38.420 回答