我正在尝试通过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>
根据失败的请求跟踪,网址似乎已被翻译,但由于某种原因,它无法到达10.2.1.10:5901的websocket服务器。
最终目标是合并noVNC / websockify,以便为网络上的多个VNC服务器提供基于浏览器的客户端访问。任何有关如何反向代理websockets的帮助都表示赞赏。
答案 0 :(得分:17)
我一直在尝试使用ARR 3.0在IIS 8.5上完成同样的事情,并最终找到了问题。根据微软的Erez Benari,this is possible:
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文件中添加以下内容:
<?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发送:
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能够通过在自身和客户端之间进行协商来正确处理扩展,然后在自身和服务器之间进行单独的协商。就目前而言,它只是让客户端和服务器相互协商,从而导致此错误。
答案 1 :(得分:4)
如@jowo所述,IIS8 / 8.5似乎不支持Sec-Websocket-Extensions。话虽这么说,我应用的解决方法是简单地重写有问题的服务器变量。首先将HTTP_SEC_WEBSOCKET_EXTENSIONS添加到允许的服务器变量列表中,然后将其添加到您的规则中:
<serverVariables><set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="" /></serverVariables>
这样,目的地就不会受到麻烦的通货膨胀的影响:)
答案 2 :(得分:-1)
TLDR将:
perMessageDeflate
禁用节点服务器套接字io中的压缩。禁用压缩,因为IIS Proxy中的APR 3不支持压缩const io = require("socket.io")(server, {
transports: ["websocket", "polling"],
perMessageDeflate: false
});