我一直试图在带有 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 能够通过在自身和客户端之间进行协商,然后在自身和服务器之间进行单独的协商来正确处理扩展。就目前而言,它只是让客户端和服务器相互协商,这导致了这个错误。