WebSockets e proxy Apache: como configurar o mod_proxy_wstunnel?

98

Eu tenho :

  1. Apache(v2.4) na porta 80 do meu servidor para www.domain1.com, com mod_proxy e mod_proxy_wstunnel habilitado

  2. node.js + socket.io na porta 3001 do mesmo servidor.

O acesso www.domain2.com(com porta 80) redireciona para 2. graças ao método descrito aqui . Eu defini isso na configuração do Apache:

<VirtualHost *:80>
    ServerName www.domain2.com
    ProxyPass / http://localhost:3001/
    ProxyPassReverse / http://localhost:3001/
    ProxyPass / ws://localhost:3001/
    ProxyPassReverse / ws://localhost:3001/
</VirtualHost>

Funciona para tudo, exceto a parte do websocket: ws://...não são transmitidos como deveriam pelo proxy.

Quando eu acesso a página no www.domain2.com, tenho:

Impossible to connect ws://www.domain2.com/socket.io/?EIO=3&transport=websocket&sid=n30rqg9AEqZIk5c9AABN.

Pergunta: Como tornar o proxy Apache o WebSockets também?

Basj
fonte

Respostas:

160

Eu finalmente consegui fazê-lo, graças a este tema .

FAÇAM:

1) Instale o Apache 2.4 (não funciona com 2.2) e faça:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Executar nodejsna porta 3001

3) Faça isso na configuração do Apache

<VirtualHost *:80>
  ServerName www.domain2.com

  RewriteEngine On
  RewriteCond %{REQUEST_URI}  ^/socket.io            [NC]
  RewriteCond %{QUERY_STRING} transport=websocket    [NC]
  RewriteRule /(.*)           ws://localhost:3001/$1 [P,L]

  ProxyPass / http://localhost:3001/
  ProxyPassReverse / http://localhost:3001/
</VirtualHost>

Nota: se você tiver mais de um serviço no mesmo servidor que usa websockets, você pode querer fazer isso para separá-los.

Basj
fonte
FWIW, Apache 2.4 no CentOS 7.1 tem esse bug, portanto, a reescrita não reconhecerá o protocolo ws: // e adiciona o domínio antes de enviar a sub-solicitação. Você pode testar a si mesmo mudando o sinalizador [P] roxy para [R] edirect.
Andor
3
Ainda recebo 502 gateway inválido para minhas rotas ws: // ao fazer isso. Executando Apache 2.4 no Ubuntu 14.04
Alex Muro
1
Isso funciona porque todo o tráfego HTTP também está sendo encaminhado, mas se você quiser apenas encaminhar o tráfego do socket, observe que o Socket.io inicia a comunicação com uma solicitação de pesquisa HTTP. Mais informações aqui .
Erik Koopmans
4
Como encaminhar de 443 wss para ws? reescrever alterações de segundo?
Hernán Eche
1
A condição RewriteCond %{QUERY_STRING} transport=websocket [NC]não funcionou corretamente. Sugerindo usar em seu RewriteCond %{HTTP:Upgrade} =websocket [NC]lugar.
Martin
99

Em vez de filtrar por URL, você também pode filtrar por cabeçalho HTTP. Esta configuração funcionará para qualquer aplicativo da web que use websockets, também se não estiver usando socket.io:

<VirtualHost *:80>
  ServerName www.domain2.com

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

  ProxyPassReverse / http://localhost:3001/
</VirtualHost>
cdauth
fonte
Isso está funcionando bem para mim, mas estou fazendo proxy de um subcaminho e descobri que é importante adicionar as âncoras ^, pois o regex está procurando uma substring. Por exemplo, RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (caso contrário, / bar / foo também será direcionado para o mesmo lugar que / foo)
Steve Lilly,
Esta configuração funciona melhor na nuvem alibaba. Além disso, ele corrigirá o erro net :: ERR_RESPONSE_HEADERS_TRUNCATED "Espero que alguém ache isso útil.
Mike Musni
Usando SignalR, posso dizer que funcionou melhor para mim +1 Obrigado
Vojtěch Mráz
18

Pode ser útil. Apenas todas as consultas são enviadas via ws para o nó

<VirtualHost *:80>
  ServerName www.domain2.com

  <Location "/">
    ProxyPass "ws://localhost:3001/"
  </Location>
</VirtualHost>
Sergey
fonte
Obrigado @Sergey. Isso me ajudou quando usei apenas o caminho direto referenciado por ws em vez de rotear tudo para a raiz do documento. Mostrei o que fiz em uma resposta abaixo para o benefício de outras pessoas.
Anwaarullah
Esta resposta funciona para soquetes de web simples (sem socket.io), então para mim esta é a melhor resposta
Tomas
Impressionante! Isso funcionou instantaneamente para mim, enquanto as outras tentativas de reescrita e proxy não resolveram meu problema.
Stephan
Obrigado!! Perfeito para o caso de uso básico, apenas um simples soquete da web usando ws: //
Antonia Blair
16

A partir do Socket.IO 1.0 (maio de 2014), todas as conexões começam com uma solicitação de pesquisa HTTP (mais informações aqui ). Isso significa que, além de encaminhar o tráfego do WebSocket, você precisa encaminhar todas transport=pollingas solicitações HTTP.

A solução abaixo deve redirecionar todo o tráfego de soquete corretamente, sem redirecionar qualquer outro tráfego.

  1. Habilite os seguintes mods do Apache2:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
  2. Use essas configurações em seu arquivo * .conf (por exemplo /etc/apache2/sites-available/mysite.com.conf). Incluí comentários para explicar cada peça:

    <VirtualHost *:80>
        ServerName www.mydomain.com
    
        # Enable the rewrite engine
        # Requires: sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
        # In the rules/conds, [NC] means case-insensitve, [P] means proxy
        RewriteEngine On
    
        # socket.io 1.0+ starts all connections with an HTTP polling request
        RewriteCond %{QUERY_STRING} transport=polling       [NC]
        RewriteRule /(.*)           http://localhost:3001/$1 [P]
    
        # When socket.io wants to initiate a WebSocket connection, it sends an
        # "upgrade: websocket" request that should be transferred to ws://
        RewriteCond %{HTTP:Upgrade} websocket               [NC]
        RewriteRule /(.*)           ws://localhost:3001/$1  [P]
    
        # OPTIONAL: Route all HTTP traffic at /node to port 3001
        ProxyRequests Off
        ProxyPass           /node   http://localhost:3001
        ProxyPassReverse    /node   http://localhost:3001
    </VirtualHost>
  3. Incluí uma seção extra para roteamento de /nodetráfego que considero útil. Veja aqui para obter mais informações.

Erik Koopmans
fonte
1
Isso funciona perfeitamente para mim. Observe que se suas solicitações chegarem a um URL diferente de root, você pode fazer, por exemplo,RewriteRule /path/(.*)
Rob Gwynn-Jones
8

Com a ajuda dessas respostas, finalmente consegui o proxy reverso para Node-RED rodando em um Raspberry Pi com Ubuntu Mate e Apache2 funcionando, usando esta configuração de site Apache2:

<VirtualHost *:80>
    ServerName nodered.domain.com
    RewriteEngine On
    RewriteCond %{HTTP:Upgrade} =websocket [NC]
    RewriteRule /(.*)           ws://localhost:1880/$1 [P,L]
    RewriteCond %{HTTP:Upgrade} !=websocket [NC]
    RewriteRule /(.*)           http://localhost:1880/$1 [P,L]
</VirtualHost>

Eu também tive que habilitar módulos como este:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
Otto Paulsen
fonte
7

Para mim funciona depois de adicionar apenas uma linha no httpd.conf conforme abaixo (linha em negrito).


<VirtualHost *:80>
    ServerName: xxxxx

    #ProxyPassReverse is not needed
    ProxyPass /log4j ws://localhost:4711/logs
<VirtualHost *:80>

A versão do Apache é 2.4.6 no CentOS.

Leon
fonte
5

Minha configuração:

  • Apache 2.4.10 (rodando em Debian)
  • Node.js (versão 4.1.1) Aplicativo em execução na porta 3000 que aceita WebSockets no caminho /api/ws

Conforme mencionado acima por @Basj, certifique-se de que o proxy a2enmod e ws_tunnel estejam habilitados.

Esta é uma captura de tela do arquivo de configuração do Apache que resolveu meu problema:

Configuração do Apache

A parte relevante como texto:

<VirtualHost *:80>
  ServerName *******
  ServerAlias *******
  ProxyPass / http://localhost:3000/
  ProxyPassReverse / http://localhost:3000/

  <Location "/api/ws">
      ProxyPass "ws://localhost:3000/api/ws"
  </Location>
</VirtualHost>

Espero que ajude.

Anwaarullah
fonte
Estou tendo problemas para configurar meu aplicativo com um URL preferencial em vez do padrão, você se importaria de me ajudar? (Estou sentado em cima de um servidor de cache de verniz)
Josh
6
Você poderia copiar / colar em vez da captura de tela? Obrigado antecipadamente, isso melhoraria a legibilidade.
Basj
3

Fiz o seguinte para um aplicativo Spring rodando conteúdo static, rest e websocket.

O Apache é usado como proxy e ponto de extremidade SSL para os seguintes URIs:

  • / app → conteúdo estático
  • / api → API REST
  • / api / ws → websocket

Configuração Apache

<VirtualHost *:80>
    ServerName xxx.xxx.xxx    

    ProxyRequests Off
    ProxyVia Off
    ProxyPreserveHost On

    <Proxy *>
         Require all granted
    </Proxy>

    RewriteEngine On

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

    # rest
    ProxyPass /api http://localhost:8080/api
    ProxyPassReverse /api http://localhost:8080/api

    # static content    
    ProxyPass /app http://localhost:8080/app
    ProxyPassReverse /app http://localhost:8080/app 
</VirtualHost>

Eu uso a mesma configuração vHost para a configuração SSL, não há necessidade de alterar nada relacionado ao proxy.

Configuração de primavera

server.use-forward-headers: true
Ortwin Angermeier
fonte
Use uma tag de localização para separar locais, por exemplo: <Location / app> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </Location>
CrazyMerlin
2

Use este link para solução perfeita para ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Você deve apenas seguir o passo abaixo ..

Vamos para /etc/apache2/mods-available

Passo 1

Ative mode proxy_wstunnel.loadusando o comando abaixo

$a2enmod proxy_wstunnel.load

Passo 2

Vamos para /etc/apache2/sites-available

e adicione a linha abaixo em seu arquivo .conf dentro do host virtual

ProxyPass "/ws2/"  "ws://localhost:8080/"

ProxyPass "/wss2/" "wss://localhost:8080/"

Nota: 8080 significa que sua porta de execução do tomcat porque queremos conectar wsonde nosso arquivo War colocado no tomcat e tomcat servir apache paraws . obrigado

Minha Configuração

ws://localhost/ws2/ALLCAD-Unifiedcommunication-1.0/chatserver?userid=4 =Connected
Arvind Madhukar
fonte
Desculpe, eu realmente não entendo (especialmente sua última frase). Você pode melhorar a formatação da resposta e adicionar detalhes para pessoas que não sabem tudo o que você mencionou?
Basj
O que é Tomcat @ArvindMadhukar?
Basj
1

Para transporte "polling".

Lado Apache:

<VirtualHost *:80>
    ServerName mysite.com
    DocumentRoot /my/path


    ProxyRequests Off

    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>

    ProxyPass /my-connect-3001 http://127.0.0.1:3001/socket.io
    ProxyPassReverse /my-connect-3001 http://127.0.0.1:3001/socket.io   
</VirtualHost>

Lado do cliente:

var my_socket = new io.Manager(null, {
    host: 'mysite.com',
    path: '/my-connect-3001'
    transports: ['polling'],
}).socket('/');
sNICkerssss
fonte
1

Além da resposta principal: se você tiver mais de um serviço no mesmo servidor que usa websockets, você pode querer fazer isso para separá-los, usando um caminho personalizado (*):

Servidor de nó:

var io = require('socket.io')({ path: '/ws_website1'}).listen(server);

HTML do cliente:

<script src="/ws_website1/socket.io.js"></script>
...
<script>
var socket = io('', { path: '/ws_website1' });
...

Configuração do Apache:

RewriteEngine On

RewriteRule ^/website1(.*)$ http://localhost:3001$1 [P,L]

RewriteCond %{REQUEST_URI}  ^/ws_website1 [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule ^(.*)$ ws://localhost:3001$1 [P,L]

RewriteCond %{REQUEST_URI}  ^/ws_website1 [NC]
RewriteRule ^(.*)$ http://localhost:3001$1 [P,L]

(*) Nota: usar o padrão RewriteCond %{REQUEST_URI} ^/socket.ionão seria específico para um site, e as solicitações de websockets seriam misturadas entre sites diferentes!

Basj
fonte
0

FAÇAM:

  1. Ter Apache 2.4 instalado (não funciona com 2.2), a2enmod proxyea2enmod proxy_wstunnel.load

  2. Faça isso na configuração do Apache,
    basta adicionar duas linhas em seu arquivo onde 8080 é a porta de execução do tomcat

    <VirtualHost *:80>
    ProxyPass "/ws2/" "ws://localhost:8080/" 
    ProxyPass "/wss2/" "wss://localhost:8080/"
    
    </VirtualHost *:80>
Arvind Madhukar
fonte