WebSockets i Apache proxy: jak skonfigurować mod_proxy_wstunnel?


99

Mam :

  1. Apache(v2.4) na porcie 80 mojego serwera dla www.domain1.com, z włączonymi mod_proxy i mod_proxy_wstunnel

  2. node.js + socket.io na porcie 3001 tego samego serwera.

Dostęp www.domain2.com(przez port 80) przekierowuje do 2. dzięki opisanej tu metodzie . Ustawiłem to w konfiguracji Apache:

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

Działa na wszystko, z wyjątkiem części websocket: ws://...nie są przesyłane tak, jak powinno, przez proxy.

Kiedy wchodzę na tę stronę www.domain2.com, mam:

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

Pytanie: Jak sprawić, by serwer proxy Apache był również używany przez WebSockets?

Odpowiedzi:


161

W końcu udało mi się to zrobić dzięki temu tematowi .

DO ZROBIENIA:

1) Zainstaluj Apache 2.4 (nie działa z 2.2) i wykonaj:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Uruchom nodejsna porcie 3001

3) Zrób to w konfiguracji 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>

Uwaga: jeśli masz więcej niż jedną usługę na tym samym serwerze, który korzysta z gniazd sieciowych, możesz to zrobić, aby je oddzielić.


FWIW, Apache 2.4 w CentOS 7.1 ma ten błąd, dlatego przepisywanie nie rozpoznaje protokołu ws: // i dołącza domenę przed wysłaniem żądania podrzędnego. Możesz sprawdzić się, zmieniając flagę [P] roxy na [R] edirect.
Andor

3
Podczas wykonywania tej czynności nadal otrzymuję złą bramę 502 dla moich tras ws: //. Uruchamianie Apache 2.4 na Ubuntu 14.04
Alex Muro

1
Działa to, ponieważ cały ruch HTTP jest również przekazywany, ale jeśli chcesz przekazywać tylko ruch z gniazda, pamiętaj, że Socket.io rozpoczyna komunikację z żądaniem odpytywania HTTP. Więcej informacji tutaj .
Erik Koopmans

4
Jak przekierować z 443 WSS do WS? czy rewritecond zmienia?
Hernán Eche

1
Stan RewriteCond %{QUERY_STRING} transport=websocket [NC]nie działa poprawnie. Sugeruję użycie RewriteCond %{HTTP:Upgrade} =websocket [NC]zamiast tego.
Martin,

99

Zamiast filtrować według adresu URL, możesz również filtrować według nagłówka HTTP. Ta konfiguracja będzie działać dla wszystkich aplikacji internetowych, które używają websockets, również jeśli nie używają one 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>

To działa dobrze, ale przekazuję ścieżkę podrzędną i odkryłem, że ważne jest, aby dodać kotwice ^, ponieważ wyrażenie regularne szuka podciągu. Np. RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (w przeciwnym razie / bar / foo zostanie również przekierowany w to samo miejsce co / foo)
Steve Lilly,

Ta konfiguracja działa najlepiej w chmurze alibaba. Ponadto naprawi błąd net :: ERR_RESPONSE_HEADERS_TRUNCATED „Mam nadzieję, że ktoś uzna to za przydatne.
Mike Musni

Używając SignalR, mogę powiedzieć, że to zadziałało najlepiej dla mnie +1 Dzięki
Vojtěch Mráz

18

Może się przyda. Po prostu wszystkie zapytania wysyłane przez WS do węzła

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

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

Dzięki @Sergey. Pomogło mi to, gdy korzystałem tylko z bezpośredniej ścieżki, do której odwołuje się ws, zamiast kierować wszystko do katalogu głównego dokumentu. Pokazałem, co zrobiłem w odpowiedzi poniżej dla dobra innych.
Anwaarullah

Ta odpowiedź działa dla prostych gniazd sieciowych (bez gniazda.io), więc dla mnie jest to lepsza odpowiedź
Tomas

Niesamowite! To od razu zadziałało, podczas gdy inne próby przepisywania i proxy nie rozwiązały mojego problemu.
Stephan

Dziękuję Ci!! Idealny do podstawowego zastosowania, po prostu proste gniazdo sieciowe przy użyciu ws: //
Antonia Blair

17

Od wersji Socket.IO 1.0 (maj 2014) wszystkie połączenia rozpoczynają się od żądania odpytywania HTTP (więcej informacji tutaj ). Oznacza to, że oprócz przekazywania ruchu WebSocket musisz przekazywać dalej wszelkie transport=pollingżądania HTTP.

Poniższe rozwiązanie powinno poprawnie przekierować cały ruch związany z gniazdami, bez przekierowywania żadnego innego ruchu.

  1. Włącz następujące mody Apache2:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
  2. Użyj tych ustawień w swoim pliku * .conf (np /etc/apache2/sites-available/mysite.com.conf.). Dodałem komentarze, aby wyjaśnić każdy element:

    <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. Dodałem dodatkową sekcję dotyczącą routingu /noderuchu, która jest przydatna, zobacz tutaj, aby uzyskać więcej informacji.


1
U mnie to działa idealnie. Zwróć uwagę, że jeśli Twoje żądania przychodzą pod adres URL inny niż root, możesz to zrobić, na przykładRewriteRule /path/(.*)
Rob Gwynn-Jones

8

Z pomocą tych odpowiedzi w końcu otrzymałem zwrotne proxy dla Node-RED działające na Raspberry Pi z działającymi Ubuntu Mate i Apache2, używając tej konfiguracji witryny 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>

Musiałem też włączyć takie moduły:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel

7

U mnie działa po dodaniu tylko jednej linii w httpd.conf jak poniżej (pogrubiona linia).


<VirtualHost *:80>
    ServerName: xxxxx

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

Wersja Apache to 2.4.6 na CentOS.


5

Moja konfiguracja:

  • Apache 2.4.10 (na Debianie)
  • Node.js (wersja 4.1.1) Aplikacja działająca na porcie 3000, która akceptuje WebSockets na ścieżce /api/ws

Jak wspomniano powyżej @Basj, upewnij się, że proxy a2enmod i ws_tunnel są włączone.

Oto zrzut ekranu pliku konfiguracyjnego Apache, który rozwiązał mój problem:

Konfiguracja Apache

Odpowiednia część jako tekst:

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

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

Mam nadzieję, że to pomoże.


Mam problem ze skonfigurowaniem aplikacji z preferowanym adresem URL zamiast domyślnego. Czy możesz mi pomóc? (Siedzę na serwerze pamięci podręcznej lakieru)
Josh

6
Czy możesz skopiować / wkleić zamiast zrzutu ekranu? Z góry dziękuję, poprawiłoby to czytelność.
Basj

3

Wykonałem następujące czynności dla aplikacji Spring z zawartością statyczną, resztową i websocket.

Apache jest używany jako serwer proxy i punkt końcowy SSL dla następujących identyfikatorów URI:

  • / app → zawartość statyczna
  • / api → REST API
  • / api / ws → websocket

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

Używam tej samej konfiguracji vHost do konfiguracji SSL, nie muszę zmieniać niczego związanego z proxy.

Konfiguracja sprężynowa

server.use-forward-headers: true

Użyj znacznika lokalizacji, aby oddzielić lokalizacje, np .: <Lokalizacja / aplikacja> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </Location>
CrazyMerlin

2

Użyj tego łącza, aby uzyskać doskonałe rozwiązanie dla ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Musisz po prostu wykonać poniższy krok ..

Iść do /etc/apache2/mods-available

Krok 1

Włącz mode proxy_wstunnel.loadza pomocą poniższego polecenia

$a2enmod proxy_wstunnel.load

Krok 2

Iść do /etc/apache2/sites-available

i dodaj poniższą linię w pliku .conf wewnątrz wirtualnego hosta

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

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

Uwaga: 8080 oznacza twój port z uruchomionym tomcat, ponieważ chcemy połączyć się wstam, gdzie nasz plik War umieszczony w tomcat i tomcat służy do apache ws. Dziękuję Ci

Moja konfiguracja

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

Przepraszam, naprawdę nie rozumiem (szczególnie twoje ostatnie zdanie). Czy możesz poprawić formatowanie odpowiedzi i dodać szczegóły dla osób, które nie wiedzą wszystkiego, o czym wspominasz?
Basj

Czym jest Tomcat @ArvindMadhukar?
Basj

1

Do transportu „odpytywania”.

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

Strona klienta:

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

1

Oprócz głównej odpowiedzi: jeśli masz więcej niż jedną usługę na tym samym serwerze, który korzysta z gniazd sieciowych, możesz to zrobić, aby je oddzielić, używając niestandardowej ścieżki (*):

Serwer węzłów:

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

Klient HTML:

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

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

(*) Uwaga: użycie wartości domyślnej RewriteCond %{REQUEST_URI} ^/socket.ionie byłoby specyficzne dla witryny internetowej, a żądania gniazd sieci Web byłyby mieszane między różnymi witrynami!


0

DO ZROBIENIA:

  1. Mieć zainstalowany Apache 2.4 (nie działa z 2.2) a2enmod proxyia2enmod proxy_wstunnel.load

  2. Zrób to w konfiguracji Apache,
    po prostu dodaj dwie linie w swoim pliku, gdzie 8080 to twój uruchomiony port Tomcat

    <VirtualHost *:80>
    ProxyPass "/ws2/" "ws://localhost:8080/" 
    ProxyPass "/wss2/" "wss://localhost:8080/"
    
    </VirtualHost *:80>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.