У меня есть веб-приложение, которое использует SignalR. Когда я подключаюсь к веб-приложению изнутри нашего брандмауэра, оно работает нормально и устанавливает соединение через WebSockets. Однако при подключении к тому же веб-приложению из-за пределов нашего брандмауэра через сервер с использованием IIS 8.5, ARR 3, перезаписи URL-адресов и в качестве обратного прокси-сервера соединение SignalR всегда возвращается к длительному опросу. На вкладке «Сеть» я вижу в инструментах разработчика, что соединение с SignalR с использованием WebSockets успешно с кодом состояния 101, но по какой-то причине он сразу пытается подключиться с использованием SSE, а затем LongPolling.
Вот мой URL переписать правило (URL закрыт):
<rule name="Test" enabled="true" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="^xxx\.xxxxxxxx\.com$" />
<add input="{CACHE_URL}" pattern="^(.+)://" />
</conditions>
<serverVariables>
<set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
<set name="HTTP_ACCEPT_ENCODING" value="" />
<set name="HTTP_SEC_WEBSOCKET_EXTENSIONS" value="" />
</serverVariables>
<action type="Rewrite" url="{C:1}://xxx.xxxxxxxx.com/{R:1}" />
</rule>
Я добавил в серверную переменную HTTP_SEC_WEBSOCKET_EXTENSIONS
, чтобы очистить заголовок ответа, потому что я прочитал, что разрешение сжатия во время перезаписи URL может вызвать проблемы с сообщениями WebSockets. Переменная HTTP_ACCEPT_ENCODING
очищает заголовок запроса при входе, а затем у меня есть соответствующее правило вывода для его сброса. Это было необходимо для обработки внутренней ошибки сервера 500, как описано в этом сообщении в блоге .
Здесь приведена соответствующая часть клиентского кода для установления соединения SignalR (это Angular JS приложение с использованием TypeScript):
namespace app.services {
export class SignalRService {
private readonly connection: SignalR.Hub.Connection;
private readonly proxy: SignalR.Hub.Proxy;
constructor(readonly $rootScope: ng.IRootScopeService,
readonly $log: ng.ILogService) {
this.connection = $.hubConnection('/signalr', { useDefaultPath: false });
this.proxy = this.connection.createHubProxy('NotificationsHub');
this.connection
.start()
.done(() => this.$rootScope.$apply(() => this.$rootScope.$broadcast('signalRConnected', true)))
.fail(error => {
this.$rootScope.$apply(() => {
this.$rootScope.$broadcast('signalRConnected', false);
this.$log.error(error);
});
});
}
}
}
Наконец, вот некоторые скриншоты с вкладки сети инструментов разработчика. Это показывает, что попытка подключения через WebSockets вернула код состояния 101, который, как я думал, означает успешное рукопожатие, но затем он немедленно переходит к попытке подключения SSE.
Это показывает детали за линией соединения WebSockets:
Я совершенно сбит с толку, почему WebSockets не работает через Перезапись URL. Я прочитал несколько статей и постов в Интернете, но я не смог заставить его перестать возвращаться к длинному опросу. Протокол WebSockets был установлен и включен на сервере DMZ, выполняющем перезапись URL. До этого он получал неверный ответ на заголовок ответа Se c -WebSocket-Accept. Я почти смирился с необходимостью использовать Long Polling, но я надеюсь, что кто-то что-то заметит или может дать какой-нибудь совет, чтобы это работало через WebSockets.