我正在尝试通过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中的一般示例规则:
根据失败的请求跟踪,URL似乎被翻译,但由于某种原因,它没有到达10.2.1.10:5901的websocket服务器.
最终目标是合并noVNC/websockify,以便为网络上的多个VNC服务器提供基于浏览器的客户端访问.任何帮助理解如何反向代理websockets表示赞赏.
我一直在尝试使用ARR 3.0在IIS 8.5上完成同样的事情,并最终发现了问题.根据微软的Erez Benari的说法,这是可能的:
WebSocket支持要求在IIS上安装WebSocket功能,但不需要任何其他配置或操作.使用服务器管理器添加角色和功能安装该功能,一旦完成,ARR 3.0将适当地处理请求.
作为测试,我为WebSocket设置了一个Node.js服务器:
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"); };
然后,我在本地计算机上设置了ARR反向代理,并在localhost上的"wstest"目录的web.config文件中添加以下代码:
这应该将所有流量转发//localhost/wstest
到端口3011上的Node.js服务器.节点服务器在我通过它直接连接到它时工作ws://localhost:3011
.当我尝试通过代理连接时ws://localhost/wstest
,请求通过Node.js服务器,进行升级,并建立连接.
Chrome发送:
GET ws://localhost/wstest HTTP/1.1 Host: localhost Connection: Upgrade Pragma: no-cache Cache-Control: no-cache Upgrade: websocket Origin: file:// Sec-WebSocket-Version: 13 User-Agent: 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 origin: 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 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-Websocket-Accept: yep8mgQACAc93oGIk8Azde4WSXk= Sec-WebSocket-Extensions: permessage-deflate
最后Chrome收到:
HTTP/1.1 101 Switching Protocols Upgrade: 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从未接收过它们.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
标题,所以它允许标题简单地通过.但允许在客户端和服务器之间协商此标头是错误的,因为ARR不参与协商,并且无法告诉双方它不支持传递压缩消息.希望有一天,ARR能够通过在自身和客户端之间进行协商来正确处理扩展,然后在自身和服务器之间进行单独的协商.就目前而言,它只是让客户端和服务器相互协商,从而导致此错误.
如@jowo所述,IIS8 / 8.5似乎不支持Sec-Websocket-Extensions。话虽如此,我应用的解决方法是简单地重写有问题的服务器变量。首先将HTTP_SEC_WEBSOCKET_EXTENSIONS添加到允许的服务器变量列表中,然后将其添加到您的规则中:
这样,目的地就不会收到麻烦的permessage-deflate :)