Назад | Перейти на главную страницу

Apache 2.4.7 mod_proxy_wstunnel слишком много туннелирования (HTTP, а также WS)

Я использую Apache 2.4.7 в качестве обратного прокси на Ubuntu 14.04 LTS. Этот сервер Apache действует как точка входа для множества различных серверных приложений, доступ к которым осуществляется через различные конфигурации mod_proxy в <Location> блоки

Мне нужно предоставить обратный прокси-доступ к приложению, которое использует WebSockets. Это приложение Java Spring, которое обслуживает HTML и другие статические файлы через HTTP, а затем использует WebSocket для динамических данных после загрузки страницы.

У меня запущено приложение Nginx используя следующую конфигурацию:

location /newapp/ {
    proxy_pass http://newapp.example.com:8080/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

К сожалению, из-за того, что мне нужен модуль аутентификации Apache, которого нет на Nginx, я не могу использовать его в производственной среде.

Что я хочу сделать в псевдо-Apache-config:

<Location /newapp/>
    if not WebSockets:
        ProxyPass http://newapp.example.com:8080/
        ProxyPassReverse /
    else
        ProxyPass ws://newapp.example.com:8080/
        ProxyPassReverse /
</Location>

Апач mod_proxy_wstunnel модуль заставляет меня думать, что это должно быть возможно. Доступ к WebSocket осуществляется по URL-адресу /api/socket/..., поэтому я попытался разделить два типа ProxyPass используя отдельные <Location> блоки:

<Location /newapp/>
    ProxyPass http://newapp.example.com:8080/ disablereuse=on
    ProxyPassReverse /

    ProxyPassReverseCookieDomain newapp.example.com apps.example.com
    ProxyPassReverseCookiePath http://newapp.example.com:8080/ /newapp/
</Location>

<Location /newapp/api/socket/>
    ProxyPass ws://newapp.example.com:8080/api/socket/
    ProxyPassReverse /
</Location>

Изначально это работает - браузер запрашивает http://apps.example.com/newapp/страница загружается по HTTP, статические ресурсы загружаются, код JavaScript подключается к веб-сокету, все в порядке.

Однако, когда новый запрос делается через HTTP - скажем, для GET /newapp/static/someimage.png, что-то идет не так. Этот запрос не соответствует местоположению WebSocket, поэтому я ожидаю, что он будет прокси GET /static/someimage.png сквозь http://newapp.example.com:8080/.

Вместо этого сервер приложений получает запрос на GET /newapp/static/someimage.png и возвращает 404, поскольку это не тот URL, о котором он знает. Это нарушает работу приложения, так как HTTP-запросы, которые должны работать, не работают.

Ноты:

Я думаю, что происходит:

думаю mod_proxy_wstunnel, после активации первым запросом на соответствие /newapp/api/socket/, принимает на себя все последующие входящие запросы от клиента, независимо от того, соответствуют ли они Location или не. Я проверил это, добавив RequestHeader set Test "some_identifying_value" директива каждому Location - HTTP-запросы для статических файлов и для /api/socket/info имел Test заголовок на них, но неправильно проксированные HTTP-запросы не имели Test заголовок на них, что говорит о том, что они передаются напрямую, не обрабатываются директивами Apache.

В конечном счете, мой вопрос таков: можно ли настроить любую версию Apache (я счастлив обновить!) Для приложений на основе WebSocket с обратным прокси-сервером таким образом, чтобы HTTP-запросы также правильно выполнялись обратным проксированием после того, как WebSocket связано? Если да, то как это настроено?

Ответ Андерса помог мне добраться туда на 95%.

Базовый сценарий:

  • У нас есть сервер на newapp.example.com
  • Порт 8080 работает как HTTP, так и WebSockets.
  • URL-адрес, который отвечает на запросы WebSockets: /api/socket/
  • Мы выполняем обратное проксирование этого приложения как http://apps.example.com/newapp/

Вот как настроить WebSockets и обратное проксирование HTTP для описанного выше сценария в <Location> блок:

<Location /newapp/>
    ProxyPass http://newapp.example.com:8080/
    ProxyPassReverse /

    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
    RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
    RewriteRule /api/(.*) ws://newapp.example.com:8080/api/$1 [P]
</Location>

Последнее правило перезаписи имеет решающее значение - без него мы передадим запрос /newapp/api/socket через сервер WebSocket, который он отклонит.

Регулярное выражение разбирает все после api - может быть лучший способ захватить этот блок, но это сработало. Затем мы должны не забыть повторно добавить /api/ на конечный URL перенаправления.

Самое главное, HTTP-запросы продолжают работать после установления соединения WebSocket!

Я использую apache 2.4 в качестве прокси перед моим весенним загрузочным приложением, это приложение обслуживает некоторые остальные вызовы api, веб-сокеты (sockjs) и некоторые статические страницы. У меня были проблемы с работой веб-сокета, секрет заключался в том, чтобы добавить правила перезаписи вы видите ниже, теперь это работает для меня, моя конфигурация виртуального хоста apache выглядит так:

<VirtualHost *:443>
  SSLEngine on
  SSLCertificateFile /etc/httpd/ssl/my.crt
  SSLCertificateKeyFile /etc/httpd/ssl/my.key
  SSLCertificateChainFile /etc/httpd/ssl/intermediate.crt
  ProxyPreserveHost On
  ProxyPass / http://127.0.0.1:8080/
  ProxyPassReverse / http://127.0.0.1:8080/
  ProxyRequests Off
  RewriteEngine on
  RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
  RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
  RewriteRule .* ws://localhost:6868%{REQUEST_URI} [P]
  ServerName my.dnsname.com
</VirtualHost>