WebSockets et proxy Apache: comment configurer mod_proxy_wstunnel?

98

J'ai :

  1. Apache(v2.4) sur le port 80 de mon serveur pour www.domain1.com, avec mod_proxy et mod_proxy_wstunnel activés

  2. node.js + socket.io sur le port 3001 du même serveur.

L'accès www.domain2.com(avec le port 80) redirige vers 2. grâce à la méthode décrite ici . J'ai défini ceci dans la configuration Apache:

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

Cela fonctionne pour tout, sauf pour la partie websocket: ws://...ne sont pas transmis comme il se doit par le proxy.

Lorsque j'accède à la page sur www.domain2.com, j'ai:

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

Question: Comment créer un proxy Apache pour les WebSockets également?

Basj
la source

Réponses:

160

J'ai finalement réussi à le faire, grâce à ce sujet .

FAIRE:

1) Installez Apache 2.4 (ne fonctionne pas avec 2.2), et faites:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Avoir en nodejscours d'exécution sur le port 3001

3) Faites cela dans la configuration 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>

Remarque: si vous avez plus d'un service sur le même serveur qui utilise des Websockets, vous souhaiterez peut-être le faire pour les séparer.

Basj
la source
FWIW, Apache 2.4 dans CentOS 7.1 a ce bogue donc la réécriture ne reconnaîtra pas le protocole ws: //, et ajoute le domaine avant d'envoyer la sous-requête. Vous pouvez vous tester en changeant l'indicateur [P] roxy en [R] edirect.
Andor
3
J'obtiens toujours 502 mauvaise passerelle pour mes routes ws: // en faisant cela. Exécution d'Apache 2.4 sur Ubuntu 14.04
Alex Muro
1
Cela fonctionne car tout le trafic HTTP est également transféré, mais si vous souhaitez uniquement transférer le trafic de votre socket, veuillez noter que Socket.io démarre les communications avec une requête d'interrogation HTTP. Plus d'infos ici .
Erik Koopmans
4
Comment transférer de 443 wss vers ws? est-ce que les changements de réécriture?
Hernán Eche
1
La condition RewriteCond %{QUERY_STRING} transport=websocket [NC]ne m'a pas fonctionné correctement. Suggérant d'utiliser à la RewriteCond %{HTTP:Upgrade} =websocket [NC]place.
Martin
99

Au lieu de filtrer par URL, vous pouvez également filtrer par en-tête HTTP. Cette configuration fonctionnera pour toutes les applications Web qui utilisent des websockets, même si elles n'utilisent pas 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
la source
Cela fonctionne très bien pour moi, mais je mets en proxy un sous-chemin et j'ai trouvé qu'il est important d'ajouter les ancres ^ puisque l'expression régulière recherche une sous-chaîne. Par exemple, RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (sinon / bar / foo sera également dirigé vers le même endroit que / foo)
Steve Lilly
Cette configuration fonctionne mieux sur le cloud alibaba. En outre, il corrigera l'erreur net :: ERR_RESPONSE_HEADERS_TRUNCATED "J'espère que quelqu'un trouvera cela utile.
Mike Musni
En utilisant SignalR, je peux dire que cela a fonctionné mieux pour moi +1 Merci
Vojtěch Mráz
18

Peut être sera utile. Toutes les requêtes sont envoyées via ws au nœud

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

  <Location "/">
    ProxyPass "ws://localhost:3001/"
  </Location>
</VirtualHost>
Sergey
la source
Merci @Sergey. Cela m'a aidé lorsque je n'utilisais que le chemin direct référencé par ws au lieu de tout acheminer vers la racine du document. J'ai montré ce que j'ai fait dans une réponse ci-dessous pour le bénéfice des autres.
Anwaarullah le
Cette réponse fonctionne pour les sockets Web simples (pas de socket.io), donc pour moi, c'est une meilleure réponse
Tomas
Impressionnant! Cela fonctionnait instantanément pour moi tandis que les autres tentatives de réécriture et de proxy ne résolvaient pas mon problème.
Stephan
Je vous remercie!! Parfait pour les cas d'utilisation de base, juste un simple socket Web utilisant ws: //
Antonia Blair
16

Depuis Socket.IO 1.0 (mai 2014), toutes les connexions commencent par une requête d'interrogation HTTP (plus d'informations ici ). Cela signifie qu'en plus du transfert du trafic WebSocket, vous devez transférer toutes transport=pollingles requêtes HTTP.

La solution ci-dessous doit rediriger correctement tout le trafic de socket, sans rediriger aucun autre trafic.

  1. Activez les mods Apache2 suivants:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
  2. Utilisez ces paramètres dans votre fichier * .conf (par exemple /etc/apache2/sites-available/mysite.com.conf). J'ai inclus des commentaires pour expliquer chaque pièce:

    <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. J'ai inclus une section supplémentaire pour le routage du /nodetrafic que je trouve pratique, voir ici pour plus d'informations.

Erik Koopmans
la source
1
Cela fonctionne parfaitement pour moi. Notez que si vos demandes arrivent à une URL autre que root, vous pouvez faire par exempleRewriteRule /path/(.*)
Rob Gwynn-Jones
8

Avec l'aide de ces réponses, j'ai finalement obtenu un proxy inverse pour Node-RED fonctionnant sur un Raspberry Pi avec Ubuntu Mate et Apache2 fonctionnant, en utilisant cette configuration 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>

J'ai également dû activer des modules comme celui-ci:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
Otto Paulsen
la source
7

Pour moi, cela fonctionne après avoir ajouté une seule ligne dans httpd.conf comme ci-dessous (ligne en gras).


<VirtualHost *:80>
    ServerName: xxxxx

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

La version Apache est 2.4.6 sur CentOS.

Léon
la source
5

Ma configuration:

  • Apache 2.4.10 (sous Debian)
  • Node.js (version 4.1.1) Application exécutée sur le port 3000 qui accepte les WebSockets au chemin /api/ws

Comme mentionné ci-dessus par @Basj, assurez-vous que le proxy a2enmod et ws_tunnel sont activés.

Voici une capture d'écran du fichier de configuration Apache qui a résolu mon problème:

Config Apache

La partie pertinente sous forme de texte:

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

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

J'espère que cela pourra aider.

Anwaarullah
la source
J'ai des difficultés à configurer mon application avec une URL préférée plutôt que la valeur par défaut, pourriez-vous m'aider? (Je suis assis sur un serveur de cache de vernis)
Josh
6
Pourriez-vous copier / coller au lieu de la capture d'écran? Merci d'avance, cela améliorerait la lisibilité.
Basj
3

A fait ce qui suit pour une application Spring exécutant du contenu statique, repos et websocket.

Apache est utilisé comme proxy et point de terminaison SSL pour les URI suivants:

  • / app → contenu statique
  • / api → API REST
  • / api / ws → websocket

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

J'utilise la même configuration vHost pour la configuration SSL, pas besoin de changer quoi que ce soit lié au proxy.

Configuration du ressort

server.use-forward-headers: true
Ortwin Angermeier
la source
Utilisez une balise Location pour séparer les emplacements, par exemple: <Location / app> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </Location>
CrazyMerlin
2

Utilisez ce lien pour la solution parfaite pour ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Vous devez simplement faire ci-dessous l'étape ..

Aller à /etc/apache2/mods-available

Étape 1

Activer mode proxy_wstunnel.loaden utilisant la commande ci-dessous

$a2enmod proxy_wstunnel.load

Étape 2

Aller à /etc/apache2/sites-available

et ajoutez la ligne ci-dessous dans votre fichier .conf à l'intérieur de l'hôte virtuel

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

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

Remarque: 8080 signifie que vous utilisez votre port tomcat car nous voulons nous connecter wslà où notre fichier War mis dans tomcat et tomcat sert apache pour ws. Merci

Ma configuration

ws://localhost/ws2/ALLCAD-Unifiedcommunication-1.0/chatserver?userid=4 =Connected
Arvind Madhukar
la source
Désolé je ne comprends pas vraiment (surtout votre dernière phrase). Pouvez-vous améliorer la mise en forme de la réponse et ajouter des détails pour les personnes qui ne savent pas tout ce que vous mentionnez?
Basj
Qu'est-ce que Tomcat @ArvindMadhukar?
Basj
1

Pour le transport "polling".

Côté 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>

Côté client:

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

En plus de la réponse principale: si vous avez plus d'un service sur le même serveur qui utilise des websockets, vous pouvez le faire pour les séparer, en utilisant un chemin personnalisé (*):

Serveur de nœud:

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

HTML du client:

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

Config 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]

(*) Remarque: l'utilisation de la valeur par défaut RewriteCond %{REQUEST_URI} ^/socket.ione serait pas spécifique à un site Web, et les requêtes Websockets seraient mélangées entre différents sites Web!

Basj
la source
0

FAIRE:

  1. Avoir Apache 2.4 installé (ne fonctionne pas avec 2.2), a2enmod proxyeta2enmod proxy_wstunnel.load

  2. Faites cela dans la configuration Apache,
    ajoutez simplement deux lignes dans votre fichier où 8080 est votre port en cours d'exécution tomcat

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