Ответ 1
Я пытался выполнить то же самое на IIS 8.5 с ARR 3.0, и в конце концов обнаружил проблему. Согласно Microsoft Erez Benari, это возможно:
Для поддержки WebSocket в IIS должна быть установлена функция WebSocket, но не требуется никаких других настроек или действий. Установите функцию с помощью диспетчера серверов, добавьте роли и компоненты, и после ее завершения ARR 3.0 будет обрабатывать запросы соответствующим образом.
В качестве теста я настроил сервер Node.js для WebSocket:
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 на своем локальном компьютере, добавив в файл web.config каталог "wstest" на локальном хосте:
<?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
на сервер Node.js через порт 3011. Сервер Node работает, когда я напрямую подключаюсь к нему через 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. Сообщение, отправленное Chrome, никогда не принимается Node.js. Похоже, что 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 сможет правильно обрабатывать расширения, договариваясь между собой и клиентом, а затем выполняя отдельное согласование между собой и сервером. В его нынешнем виде клиент и сервер просто договариваются друг с другом, что приводит к этой ошибке.