Соединение веб-сокетов Apache: обновление заменено на keep-alive - PullRequest
0 голосов
/ 27 декабря 2018

Я пытаюсь выяснить ошеломляющую проблему apache, когда запросы проходят через два уровня обратных прокси-серверов apache, прежде чем обращаться к вышестоящим сервисам.Большая часть трафика, кажется, проходит через все нормально.Заметным исключением являются веб-сокеты.

В частности, это тестовый запрос

curl -i -H 'Connection: Upgrade' -H 'Upgrade: websocket' localhost:80/test.html

При прокси-запросах от порта 80 к порту 8080 я замечаю (используя tcpdump и Wireshark), чтозаголовок Upgrade был удален и вместо него было установлено Connection: Keep-Alive.Более того, любые попытки сброса заголовка Connection на Upgrade и Upgrade: websocket были бесполезными.

Обратите внимание, что для запуска службы webstream требуется Connection: Upgrade и Upgrade: websocket (я получаюошибка 404 без этих заголовков).

Почему Apache форсирует Connection: Keep-Alive при прокси для себя?Есть ли способ заставить его передавать заголовки Connection / Upgrade или устанавливать эти значения вручную?RequestHeader и друзья, к сожалению, не помогли.Без Connection и Upgrade, прошедших через обратный прокси-сервер, вышестоящая служба забирает и выдает 404 / Не найдено в конечной точке веб-сокета.

ServerRoot "/usr/local/apache2"

Listen 80
Listen 8080

LogLevel rewrite:trace8

LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule xml2enc_module modules/mod_xml2enc.so
LoadModule proxy_html_module modules/mod_proxy_html.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule auth_mellon_module modules/mod_auth_mellon.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule ssl_module modules/mod_ssl.so

<IfModule unixd_module>
User daemon
Group daemon
</IfModule>

<VirtualHost *:80>
  LogLevel rewrite:trace8
  ServerName localhost

  RewriteEngine On

    RewriteCond %{HTTP:Upgrade} =websocket
    RewriteRule /(.*) ws://localhost:8080/$1 [P,L]
    RewriteCond %{HTTP:Upgrade} !=websocket
    RewriteRule /(.*) http://localhost:8080/$1 [P,L]

  ProxyPass / http://localhost:8080/
  ProxyPassReverse / http://localhost:8080/
  ProxyRequests Off
</VirtualHost> 

<VirtualHost *:8080>
    LogLevel rewrite:trace8

    ServerName localhost
    UseCanonicalName On
    RewriteEngine On

    ProxyPass / http://proxy-debug:8080/
    ProxyPassReverse / http://proxy-debug:8080/
    ProxyRequests Off
</VirtualHost>


ServerAdmin you@example.com


ErrorLog /proc/self/fd/2

DocumentRoot "/"

1 Ответ

0 голосов
/ 27 декабря 2018

Я не совсем понимаю, как это работает, но самородки мудрости, которые я почерпнул, и решение, которое я разработал, заключаются в следующем:

  • Apache не понимает веб-сокеты в одном смысле (то есть когдаэто говорит само с собой).В результате вам нужно использовать mod_rewrite и установить протокол на ws:// для переадресации веб-сокетов в восходящем направлении (кажется, что никакое количество заголовков настроек вам здесь не поможет).
  • Если вы установитепротокола на ws://, тогда Apache установит соответствующие заголовки (Connection: Upgrade и Upgrade: websocket) при прокси-запросах в восходящем направлении.Однако, по какой-то причине, он, похоже, не делает этого при прокси-запросах к себе / другому VirtualHost.
  • В VirtualHost *:80 (скопировано ниже) вы можете увидеть сообщение, которое пытается определить,websocket в порядке, а затем измените протокол соответствующим образом.Это не будет работать для VirtualHost *:8080 (см. Вышеприведенный пункт).Необходимы другие методы.

    RewriteCond %{HTTP:Upgrade} =websocket
    RewriteRule /(.*) ws://localhost:8080/$1 [P,L]
    RewriteCond %{HTTP:Upgrade} !=websocket
    RewriteRule /(.*) http://localhost:8080/$1 [P,L]
    
  • Соберите все это вместе, и вы должны сообщить VirtualHost *:8080, что требуется соединение через веб-сокет в восходящем направлении.К счастью, мы контролируем VirtualHost *:80 и можем разглядеть эту информацию / передать ее.Мы должны делать это, не касаясь заголовков Connection или Upgrade, поскольку Apache делает с ними странные вещи.Протокол может быть отслеживаемым, но я не уверен, как это сделать.В результате я использую фиктивный, внутренний, настраиваемый заголовок для передачи.Вероятно, лучше назвать его так, чтобы столкновения были маловероятными.

В VirtualHost *:80 мы добавляем блок вроде:

SetEnvIf Upgrade ^websocket$ websock=true
RequestHeader set X-Is-Websocket %{websock}e

И затем мы читаемэтот заголовок в VirtualHost *:8080, при необходимости изменив протокол:

SetEnvIf X-Is-Websocket ^true$ websock=true
RequestHeader unset X-Is-Websocket

# change protocol if necessary
RewriteCond %{ENV:websock} =true
RewriteRule /(.*) ws://proxy-debug:8080/$1 [P,L]
RewriteCond %{ENV:websock} !=true
RewriteRule /(.*) http://proxy-debug:8080/$1 [P,L]

Надеюсь, это поможет!:)

...