WebSockets y proxy Apache: ¿cómo configurar mod_proxy_wstunnel?

98

Yo tengo :

  1. Apache(v2.4) en el puerto 80 de mi servidor para www.domain1.com, con mod_proxy y mod_proxy_wstunnel habilitados

  2. node.js + socket.io en el puerto 3001 del mismo servidor.

Accediendo www.domain2.com(con el puerto 80) se redirecciona a 2. gracias al método aquí descrito . He configurado esto en la configuración de 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 todo, excepto para la parte websocket: ws://...el proxy no los transmite como debería.

Cuando accedo a la página en www.domain2.com, tengo:

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

Pregunta: ¿Cómo hacer que Apache también sea proxy de WebSockets?

Basj
fuente

Respuestas:

160

Finalmente logré hacerlo gracias a este tema .

QUE HACER:

1) Instale Apache 2.4 (no funciona con 2.2) y haga:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Se está nodejsejecutando en el puerto 3001

3) Haga esto en la configuración de 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: si tiene más de un servicio en el mismo servidor que usa websockets, es posible que desee hacer esto para separarlos.

Basj
fuente
FWIW, Apache 2.4 en CentOS 7.1 tiene este error, por lo tanto, la reescritura no reconocerá el protocolo ws: // y antepone el dominio antes de enviar la subquest. Puede probarse a sí mismo cambiando el indicador [P] roxy a [R] edirect.
Andor
3
Todavía obtengo una puerta de enlace 502 incorrecta para mis rutas ws: // al hacer esto. Ejecutando Apache 2.4 en Ubuntu 14.04
Alex Muro
1
Esto funciona porque todo el tráfico HTTP también se reenvía, pero si solo desea reenviar el tráfico de su socket, tenga en cuenta que Socket.io inicia las comunicaciones con una solicitud de sondeo HTTP. Más info aquí .
Erik Koopmans
4
¿Cómo reenviar de 443 wss a ws? rewritecond cambia?
Hernán Eche
1
La condición RewriteCond %{QUERY_STRING} transport=websocket [NC]no me funcionó correctamente. Sugerir usar RewriteCond %{HTTP:Upgrade} =websocket [NC]en su lugar.
Martin
99

En lugar de filtrar por URL, también puede filtrar por encabezado HTTP. Esta configuración funcionará para cualquier aplicación web que use websockets, también si no usan 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
fuente
Esto funciona bien para mí, pero estoy usando una subruta y he descubierto que es importante agregar los anclajes ^ ya que la expresión regular está buscando una subcadena. Por ejemplo, RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (de lo contrario, / bar / foo también se dirigirá al mismo lugar que / foo)
Steve Lilly
Esta configuración funciona mejor en la nube de alibaba. Además, corregirá el error net :: ERR_RESPONSE_HEADERS_TRUNCATED "Espero que alguien encuentre esto útil.
Mike Musni
Usando SignalR, puedo decir que esto funcionó mejor para mí +1 Gracias
Vojtěch Mráz
18

Puede ser útil. Solo todas las consultas se envían a través de ws al nodo

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

  <Location "/">
    ProxyPass "ws://localhost:3001/"
  </Location>
</VirtualHost>
Sergey
fuente
Gracias @Sergey. Esto me ayudó cuando solo usé la ruta directa a la que hace referencia ws en lugar de enrutar todo a la raíz del documento. He mostrado lo que he hecho en una respuesta a continuación para beneficio de otros.
Anwaarullah
Esta respuesta funciona para sockets web simples (sin socket.io), así que para mí esta es una mejor respuesta
Tomas
¡Increíble! Esto funcionó instantáneamente para mí, mientras que los otros intentos de reescritura y proxy no resolvieron mi problema.
Stephan
¡¡Gracias!! Perfecto para el caso de uso básico, solo un conector web simple usando ws: //
Antonia Blair
16

A partir de Socket.IO 1.0 (mayo de 2014), todas las conexiones comienzan con una solicitud de sondeo HTTP (más información aquí ). Eso significa que, además de reenviar el tráfico de WebSocket, debe reenviar cualquier transport=pollingsolicitud HTTP.

La siguiente solución debería redirigir todo el tráfico de socket correctamente, sin redirigir ningún otro tráfico.

  1. Habilite las siguientes modificaciones de Apache2:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
  2. Utilice esta configuración en su archivo * .conf (por ejemplo /etc/apache2/sites-available/mysite.com.conf). He incluido comentarios para explicar cada pieza:

    <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. He incluido una sección adicional para enrutar el /nodetráfico que me parece útil, consulte aquí para obtener más información.

Erik Koopmans
fuente
1
Esto funciona perfectamente para mi. Tenga en cuenta que si sus solicitudes llegan a una URL que no sea la raíz, puede hacerlo, por ejemploRewriteRule /path/(.*)
Rob Gwynn-Jones
8

Con la ayuda de estas respuestas, finalmente obtuve el proxy inverso para Node-RED ejecutándose en una Raspberry Pi con Ubuntu Mate y Apache2 funcionando, usando esta configuración del sitio 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>

También tuve que habilitar módulos como este:

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

Para mí, funciona después de agregar solo una línea en httpd.conf como se muestra a continuación (línea en negrita).


<VirtualHost *:80>
    ServerName: xxxxx

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

La versión de Apache es 2.4.6 en CentOS.

León
fuente
5

Mi configuración:

  • Apache 2.4.10 (ejecutando Debian)
  • Node.js (versión 4.1.1) Aplicación que se ejecuta en el puerto 3000 que acepta WebSockets en la ruta /api/ws

Como lo mencionó @Basj anteriormente, asegúrese de que el proxy a2enmod y ws_tunnel estén habilitados.

Esta es una captura de pantalla del archivo de configuración de Apache que resolvió mi problema:

Configuración de Apache

La 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 ayude.

Anwaarullah
fuente
Tengo problemas para configurar mi aplicación con una URL preferida en lugar de la predeterminada, ¿te importaría ayudarme? (Estoy sentado encima de un servidor de caché de barniz)
Josh
6
¿Podrías copiar / pegar en lugar de una captura de pantalla? Gracias de antemano, mejoraría la legibilidad.
Basj
3

Hice lo siguiente para una aplicación de primavera que ejecuta contenido estático, de descanso y de websocket.

Apache se utiliza como proxy y punto final SSL para los siguientes URI:

  • / app → contenido estático
  • / api → API REST
  • / api / ws → websocket

Configuración de 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>

Utilizo la misma configuración de vHost para la configuración de SSL, no es necesario cambiar nada relacionado con el proxy.

Configuración de primavera

server.use-forward-headers: true
Ortwin Angermeier
fuente
Utilice una etiqueta de ubicación para separar ubicaciones, es decir: <Location / app> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </Location>
CrazyMerlin
2

Utilice este enlace para obtener una solución perfecta para ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Solo tienes que hacer el siguiente paso.

Ir /etc/apache2/mods-available

Paso 1

Habilitar mode proxy_wstunnel.loadusando el siguiente comando

$a2enmod proxy_wstunnel.load

Paso 2

Ir /etc/apache2/sites-available

y agregue la siguiente línea en su archivo .conf dentro del host virtual

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

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

Nota: 8080 significa que su puerto de ejecución de tomcat porque queremos conectar wsdonde nuestro archivo War colocado en tomcat y tomcat sirven para apachews . gracias

Mi configuración

ws://localhost/ws2/ALLCAD-Unifiedcommunication-1.0/chatserver?userid=4 =Connected
Arvind Madhukar
fuente
Lo siento, realmente no entiendo (especialmente tu última oración). ¿Puedes mejorar el formato de la respuesta y agregar detalles para las personas que no saben todo lo que mencionas?
Basj
¿Qué es Tomcat @ArvindMadhukar?
Basj
1

Para transporte de "votación".

Lado de 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 del cliente:

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

Además de la respuesta principal: si tiene más de un servicio en el mismo servidor que usa websockets, es posible que desee hacer esto para separarlos, utilizando una ruta personalizada (*):

Servidor de nodo:

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

HTML del cliente:

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

Configuración de 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: el uso del valor predeterminado RewriteCond %{REQUEST_URI} ^/socket.iono sería específico de un sitio web y las solicitudes de websockets se mezclarían entre diferentes sitios web.

Basj
fuente
0

QUE HACER:

  1. Tener instalado Apache 2.4 (no funciona con 2.2) a2enmod proxyya2enmod proxy_wstunnel.load

  2. Haga esto en la configuración de Apache,
    simplemente agregue dos líneas en su archivo donde 8080 es su puerto de ejecución de tomcat

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