WebSockets dan Apache proxy: bagaimana cara mengkonfigurasi mod_proxy_wstunnel?

99

Saya sudah :

  1. Apache(v2.4) pada port 80 server saya untuk www.domain1.com, dengan mod_proxy dan mod_proxy_wstunnel diaktifkan

  2. node.js + socket.io pada port 3001 dari server yang sama.

Mengakses www.domain2.com(dengan port 80) dialihkan ke 2. berkat metode yang dijelaskan di sini . Saya telah mengatur ini di konfigurasi Apache:

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

Ia bekerja untuk semuanya, kecuali bagian websocket: ws://...tidak dikirimkan seperti seharusnya oleh proxy.

Saat saya mengakses halaman di www.domain2.com, saya memiliki:

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

Pertanyaan: Bagaimana cara membuat proxy Apache sebagai WebSockets juga?

Basj
sumber

Jawaban:

161

Saya akhirnya berhasil melakukannya, berkat topik ini .

MELAKUKAN:

1) Instal Apache 2.4 (tidak bekerja dengan 2.2), dan lakukan:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Telah nodejsberjalan pada port 3001

3) Lakukan ini di konfigurasi 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>

Catatan: jika Anda memiliki lebih dari satu layanan di server yang sama yang menggunakan websockets, Anda mungkin ingin melakukan ini untuk memisahkannya.

Basj
sumber
FWIW, Apache 2.4 di CentOS 7.1 memiliki bug ini sehingga penulisan ulang tidak akan mengenali protokol ws: //, dan menambahkan domain sebelum mengirim subrequest. Anda dapat menguji diri Anda sendiri dengan mengubah pengalihan [P] roxy ke [R].
Andor
3
Saya masih mendapatkan 502 gateway buruk untuk rute ws: // saya saat melakukan ini. Menjalankan Apache 2.4 di Ubuntu 14.04
Alex Muro
1
Ini berfungsi karena semua lalu lintas HTTP juga diteruskan, tetapi jika Anda hanya ingin meneruskan lalu lintas soket Anda, harap perhatikan bahwa Socket.io memulai komunikasi dengan permintaan polling HTTP. Info selengkapnya di sini .
Erik Koopmans
4
Bagaimana cara meneruskan dari 443 wss ke ws? apakah rewritecond berubah?
Hernán Eche
1
Kondisi RewriteCond %{QUERY_STRING} transport=websocket [NC]tidak bekerja dengan benar. Menyarankan untuk menggunakan RewriteCond %{HTTP:Upgrade} =websocket [NC]sebagai gantinya.
Martin
99

Selain memfilter berdasarkan URL, Anda juga dapat memfilter berdasarkan header HTTP. Konfigurasi ini akan berfungsi untuk aplikasi web apa pun yang menggunakan websockets, juga jika mereka tidak menggunakan 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
sumber
Ini berfungsi dengan baik untuk saya tetapi saya membuat proxy subpath dan saya merasa penting untuk menambahkan jangkar ^ karena regex sedang mencari substring. Misalnya RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (jika tidak / bar / foo juga akan diarahkan ke tempat yang sama dengan / foo)
Steve Lilly
Konfigurasi ini berfungsi paling baik di cloud alibaba. Selain itu, ini akan memperbaiki kesalahan net :: ERR_RESPONSE_HEADERS_TRUNCATED "Semoga seseorang menemukan ini berguna.
Mike Musni
Dengan menggunakan SignalR, saya dapat mengatakan, ini bekerja paling baik untuk saya +1 Terima kasih
Vojtěch Mráz
18

Semoga bermanfaat. Semua kueri dikirim melalui ws ke node

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

  <Location "/">
    ProxyPass "ws://localhost:3001/"
  </Location>
</VirtualHost>
Sergey
sumber
Terima kasih @Sergey. Ini membantu saya ketika saya hanya menggunakan jalur langsung yang dirujuk oleh ws daripada merutekan semuanya ke root dokumen. Saya telah menunjukkan apa yang telah saya lakukan dalam jawaban di bawah ini untuk kepentingan orang lain.
Anwaarullah
Jawaban ini berfungsi untuk soket web sederhana (tanpa socket.io) jadi bagi saya ini adalah jawaban yang lebih baik
Tomas
Hebat! Ini langsung berfungsi untuk saya sementara upaya penulisan ulang dan proxy lainnya tidak menyelesaikan masalah saya.
Stephan
Terima kasih!! Sempurna untuk kasus penggunaan dasar, hanya soket web sederhana yang menggunakan ws: //
Antonia Blair
17

Mulai Socket.IO 1.0 (Mei 2014), semua koneksi dimulai dengan permintaan polling HTTP (info selengkapnya di sini ). Itu berarti selain meneruskan lalu lintas WebSocket, Anda perlu meneruskan transport=pollingpermintaan HTTP apa pun .

Solusi di bawah ini harus mengarahkan semua lalu lintas soket dengan benar, tanpa mengalihkan lalu lintas lain.

  1. Aktifkan mod Apache2 berikut:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
  2. Gunakan pengaturan ini di file * .conf Anda (misalnya /etc/apache2/sites-available/mysite.com.conf). Saya telah menyertakan komentar untuk menjelaskan setiap bagian:

    <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. Saya telah menyertakan bagian tambahan untuk merutekan /nodelalu lintas yang menurut saya berguna, lihat di sini untuk info lebih lanjut.

Erik Koopmans
sumber
1
Ini bekerja dengan sempurna untuk saya. Perhatikan bahwa jika permintaan Anda datang ke URL selain root, Anda dapat melakukannya misalnyaRewriteRule /path/(.*)
Rob Gwynn-Jones
8

Dengan bantuan dari jawaban ini, saya akhirnya mendapatkan proxy terbalik untuk Node-RED yang berjalan pada Raspberry Pi dengan Ubuntu Mate dan Apache2 berfungsi, menggunakan konfigurasi situs Apache2 ini:

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

Saya juga harus mengaktifkan modul seperti ini:

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

Bagi saya ini berfungsi setelah menambahkan hanya satu baris di httpd.conf seperti di bawah ini (garis tebal).


<VirtualHost *:80>
    ServerName: xxxxx

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

Versi Apache 2.4.6 di CentOS.

Leon
sumber
5

Setup saya:

  • Apache 2.4.10 (menjalankan Debian)
  • Node.js (versi 4.1.1) Aplikasi yang berjalan pada port 3000 yang menerima WebSockets di jalur /api/ws

Seperti yang disebutkan di atas oleh @Basj, pastikan proxy a2enmod dan ws_tunnel diaktifkan.

Ini adalah tangkapan layar dari file konfigurasi Apache yang memecahkan masalah saya:

Konfigurasi Apache

Bagian yang relevan sebagai teks:

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

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

Semoga membantu.

Anwaarullah
sumber
Saya mengalami masalah saat menyiapkan aplikasi saya dengan URL yang lebih disukai daripada default, maukah Anda membantu saya? (Saya duduk di atas server cache pernis)
Josh
6
Bisakah Anda menyalin / menempel alih-alih tangkapan layar? Terima kasih sebelumnya, ini akan meningkatkan keterbacaan.
Basj
3

Lakukan hal berikut untuk aplikasi pegas yang menjalankan konten statis, istirahat, dan websocket.

Apache digunakan sebagai Proxy dan SSL Endpoint untuk URI berikut:

  • / app → konten statis
  • / api → REST API
  • / api / ws → websocket

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

Saya menggunakan konfigurasi vHost yang sama untuk konfigurasi SSL, tidak perlu mengubah apa pun yang terkait dengan proxy.

Konfigurasi pegas

server.use-forward-headers: true
Ortwin Angermeier
sumber
Gunakan tag Lokasi untuk memisahkan lokasi, yaitu: <Location / app> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </Location>
CrazyMerlin
2

Gunakan tautan ini untuk solusi sempurna untuk ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Anda hanya perlu melakukan langkah di bawah ini ..

Pergi ke /etc/apache2/mods-available

Langkah 1

Aktifkan mode proxy_wstunnel.loaddengan menggunakan perintah di bawah ini

$a2enmod proxy_wstunnel.load

Langkah 2

Pergi ke /etc/apache2/sites-available

dan tambahkan baris di bawah ini di file .conf Anda di dalam virtual host

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

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

Catatan: 8080 berarti Anda bahwa tomcat Anda menjalankan port karena kami ingin menghubungkan wstempat file War kami dimasukkan ke dalam tomcat dan tomcat melayani apache ws. Terima kasih

Konfigurasi Saya

ws://localhost/ws2/ALLCAD-Unifiedcommunication-1.0/chatserver?userid=4 =Connected
Arvind Madhukar
sumber
Maaf, saya tidak begitu mengerti (terutama kalimat terakhir Anda). Bisakah Anda meningkatkan format jawaban dan menambahkan detail untuk orang yang tidak tahu semua yang Anda sebutkan?
Basj
Apa itu Tomcat @ArvindMadhukar?
Basj
1

Untuk transportasi "pemungutan suara".

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

Sisi klien:

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

Selain jawaban utama: jika Anda memiliki lebih dari satu layanan di server yang sama yang menggunakan websockets, Anda mungkin ingin melakukan ini untuk memisahkannya, dengan menggunakan jalur kustom (*):

Server node:

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

HTML klien:

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

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

(*) Catatan: menggunakan default RewriteCond %{REQUEST_URI} ^/socket.iotidak akan dikhususkan untuk situs web, dan permintaan websockets akan tercampur di antara situs web yang berbeda!

Basj
sumber
0

MELAKUKAN:

  1. Memiliki Apache 2.4 terinstal (tidak bekerja dengan 2.2), a2enmod proxydana2enmod proxy_wstunnel.load

  2. Lakukan ini di konfigurasi Apache
    cukup tambahkan dua baris di file Anda di mana 8080 adalah porta tomcat Anda

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