WebSocket e proxy Apache: come configurare mod_proxy_wstunnel?


98

Io ho :

  1. Apache(v2.4) sulla porta 80 del mio server per www.domain1.com, con mod_proxy e mod_proxy_wstunnel abilitati

  2. node.js + socket.io sulla porta 3001 dello stesso server.

L'accesso www.domain2.com(con la porta 80) reindirizza a 2. grazie al metodo qui descritto . L'ho impostato nella configurazione di Apache:

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

Funziona per tutto, tranne la parte websocket: ws://...non vengono trasmessi come dovrebbe dal proxy.

Quando accedo alla pagina su www.domain2.com, ho:

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

Domanda: come rendere Apache proxy anche i WebSocket?

Risposte:


160

Finalmente sono riuscito a farlo, grazie a questo argomento .

FARE:

1) Avere Apache 2.4 installato (non funziona con 2.2) e fare:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Avere in nodejsesecuzione sulla porta 3001

3) Fallo nella configurazione di 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 hai più di un servizio sullo stesso server che utilizza websocket, potresti volerlo fare per separarli.


FWIW, Apache 2.4 in CentOS 7.1 ha questo bug, quindi la riscrittura non riconoscerà il protocollo ws: // e antepone il dominio prima di inviare la sottorichiesta. Puoi metterti alla prova cambiando il flag [P] roxy in [R] edirect.
Andor

3
Ricevo ancora 502 bad gateway per i miei percorsi ws: // quando lo faccio. Esecuzione di Apache 2.4 su Ubuntu 14.04
Alex Muro

1
Funziona perché anche tutto il traffico HTTP viene inoltrato, ma se desideri inoltrare solo il traffico socket, tieni presente che Socket.io avvia le comunicazioni con una richiesta di polling HTTP. Maggiori info qui .
Erik Koopmans

4
Come inoltrare da 443 wss a ws? rewritecond cambia?
Hernán Eche

1
La condizione RewriteCond %{QUERY_STRING} transport=websocket [NC]non mi ha funzionato correttamente. Suggerendo di usare RewriteCond %{HTTP:Upgrade} =websocket [NC]invece.
Martin,

99

Invece di filtrare per URL, puoi anche filtrare per intestazione HTTP. Questa configurazione funzionerà per tutte le applicazioni web che utilizzano websocket, anche se non utilizzano 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>

Questo funziona bene per me, ma sto eseguendo il proxy di un sottopercorso e ho scoperto che è importante aggiungere gli ^ anchor poiché l'espressione regolare cerca una sottostringa. Es. RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (altrimenti / bar / foo verrà indirizzato allo stesso posto di / foo)
Steve Lilly

Questa configurazione funziona meglio su alibaba cloud. Inoltre, risolverà l'errore net :: ERR_RESPONSE_HEADERS_TRUNCATED "Spero che qualcuno lo trovi utile.
Mike Musni

Utilizzando SignalR, posso dire, ha funzionato meglio per me +1 Grazie
Vojtěch Mráz

18

Potrebbe essere utile. Solo tutte le query inviate tramite ws a node

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

  <Location "/">
    ProxyPass "ws://localhost:3001/"
  </Location>
</VirtualHost>

Grazie @Sergey. Questo mi ha aiutato quando ho usato solo il percorso diretto a cui fa riferimento ws invece di instradare tutto alla radice dei documenti. Ho mostrato cosa ho fatto in una risposta di seguito a beneficio degli altri.
Anwaarullah,

Questa risposta funziona per semplici socket web (no socket.io) quindi per me questa è la risposta migliore
Tomas

Eccezionale! Questo ha funzionato immediatamente per me mentre gli altri tentativi di riscrittura e proxy non hanno risolto il mio problema.
Stephan

Grazie!! Perfetto per il caso d'uso di base, solo un semplice socket web utilizzando WS: //
Antonia Blair

16

A partire da Socket.IO 1.0 (maggio 2014), tutte le connessioni iniziano con una richiesta di polling HTTP (maggiori informazioni qui ). Ciò significa che oltre a inoltrare il traffico WebSocket, è necessario inoltrare qualsiasi transport=pollingrichiesta HTTP.

La soluzione seguente dovrebbe reindirizzare correttamente tutto il traffico socket, senza reindirizzare altro traffico.

  1. Abilita le seguenti mod di Apache2:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
  2. Usa queste impostazioni nel tuo file * .conf (ad esempio /etc/apache2/sites-available/mysite.com.conf). Ho incluso commenti per spiegare ogni pezzo:

    <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. Ho incluso una sezione aggiuntiva per il routing del /nodetraffico che trovo utile, vedi qui per maggiori informazioni.


1
Questo funziona perfettamente per me. Tieni presente che se le tue richieste arrivano a un URL diverso da root, puoi fare ad esempioRewriteRule /path/(.*)
Rob Gwynn-Jones

8

Con l'aiuto di queste risposte, ho finalmente ottenuto il proxy inverso per Node-RED in esecuzione su un Raspberry Pi con Ubuntu Mate e Apache2 funzionanti, utilizzando questa configurazione del sito 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>

Ho anche dovuto abilitare moduli come questo:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel

7

Per me funziona dopo aver aggiunto solo una riga in httpd.conf come sotto (linea in grassetto).


<VirtualHost *:80>
    ServerName: xxxxx

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

La versione di Apache è 2.4.6 su CentOS.


5

La mia configurazione:

  • Apache 2.4.10 (eseguendo Debian)
  • Node.js (versione 4.1.1) App in esecuzione sulla porta 3000 che accetta WebSocket nel percorso /api/ws

Come accennato in precedenza da @Basj, assicurati che a2enmod proxy e ws_tunnel siano abilitati.

Questo è uno screenshot del file di configurazione di Apache che ha risolto il mio problema:

Configurazione di Apache

La parte rilevante come testo:

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

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

Spero che aiuti.


Ho problemi a configurare la mia app con un URL preferito piuttosto che quello predefinito, ti dispiacerebbe aiutarmi? (Sono seduto su un server cache di vernice)
Josh

6
Potresti copiare / incollare invece di screenshot? Grazie in anticipo, migliorerebbe la leggibilità.
Basj

3

Ha fatto quanto segue per un'applicazione spring che esegue contenuto statico, rest e websocket.

Apache viene utilizzato come proxy e endpoint SSL per i seguenti URI:

  • / app → contenuto statico
  • / api → API REST
  • / api / ws → websocket

Configurazione di 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>

Uso la stessa configurazione di vHost per la configurazione SSL, non è necessario modificare nulla relativo al proxy.

Configurazione a molla

server.use-forward-headers: true

Utilizza un tag Location per separare le posizioni, ad esempio: <Location / app> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </Location>
CrazyMerlin

2

Utilizza questo collegamento per la soluzione perfact per ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Devi solo fare il passaggio sotto ..

Vai a /etc/apache2/mods-available

Passo 1

Abilita mode proxy_wstunnel.loadutilizzando il comando seguente

$a2enmod proxy_wstunnel.load

Passo 2

Vai a /etc/apache2/sites-available

e aggiungi sotto la riga nel tuo file .conf all'interno dell'host virtuale

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

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

Nota: 8080 significa che sei la porta in esecuzione di tomcat perché vogliamo connetterci wsdove il nostro file War inserito in tomcat e tomcat serve apache per ws. grazie

La mia configurazione

ws://localhost/ws2/ALLCAD-Unifiedcommunication-1.0/chatserver?userid=4 =Connected

Scusa, non ho davvero capito (soprattutto la tua ultima frase). Puoi migliorare la formattazione della risposta e aggiungere dettagli per le persone che non sanno tutto ciò che dici?
Basj

Cos'è Tomcat @ArvindMadhukar?
Basj

1

Per il trasporto "polling".

Lato 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>

Dalla parte del cliente:

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

1

Oltre alla risposta principale: se hai più di un servizio sullo stesso server che utilizza websocket, potresti volerlo fare per separarli, utilizzando un percorso personalizzato (*):

Server nodo:

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

HTML client:

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

Configurazione di 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: l'utilizzo del valore predefinito RewriteCond %{REQUEST_URI} ^/socket.ionon sarebbe specifico per un sito Web e le richieste di websocket sarebbero confuse tra diversi siti Web!


0

FARE:

  1. Avere Apache 2.4 installato (non funziona con 2.2) a2enmod proxyea2enmod proxy_wstunnel.load

  2. Fallo nella configurazione di Apache,
    aggiungi solo due righe nel tuo file dove 8080 è la tua porta di esecuzione di Tomcat

    <VirtualHost *:80>
    ProxyPass "/ws2/" "ws://localhost:8080/" 
    ProxyPass "/wss2/" "wss://localhost:8080/"
    
    </VirtualHost *:80>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.