WebSockets và proxy Apache: làm thế nào để định cấu hình mod_proxy_wstunnel?


98

Tôi có :

  1. Apache(v2.4) trên cổng 80 của máy chủ của tôi www.domain1.com, với mod_proxymod_proxy_wstunnel được bật

  2. node.js + socket.io trên cổng 3001 của cùng một máy chủ.

Việc truy cập www.domain2.com(với cổng 80) chuyển hướng đến 2. nhờ phương pháp được mô tả ở đây . Tôi đã đặt điều này trong cấu hình Apache:

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

Nó hoạt động cho mọi thứ, ngoại trừ phần websocket: ws://...không được proxy truyền như vậy.

Khi tôi truy cập trang trên www.domain2.com, tôi có:

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

Câu hỏi: Làm cách nào để đặt proxy Apache cũng là WebSockets?

Câu trả lời:


160

Cuối cùng tôi đã làm được, nhờ chủ đề này .

LÀM:

1) Đã cài đặt Apache 2.4 (không hoạt động với 2.2) và thực hiện:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) Đã nodejschạy trên cổng 3001

3) Làm điều này trong cấu hình 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>

Lưu ý: nếu bạn có nhiều dịch vụ trên cùng một máy chủ sử dụng websockets, bạn có thể muốn thực hiện việc này để tách chúng.


FWIW, Apache 2.4 trong CentOS 7.1 có lỗi này, do đó việc viết lại sẽ không nhận ra giao thức ws: // và thêm miền trước khi gửi yêu cầu con. Bạn có thể tự kiểm tra bằng cách thay đổi cờ [P] roxy thành [R] dựng đứng.
Andor

3
Tôi vẫn nhận được cổng lỗi 502 cho các tuyến đường ws: // của mình khi thực hiện việc này. Chạy Apache 2.4 trên Ubuntu 14.04
Alex Muro

1
Điều này hoạt động vì tất cả lưu lượng HTTP cũng đang được chuyển tiếp, nhưng nếu bạn chỉ muốn chuyển tiếp lưu lượng socket của mình, xin lưu ý rằng Socket.io bắt đầu giao tiếp với một yêu cầu thăm dò HTTP. Thêm thông tin ở đây .
Erik Koopmans

4
Làm cách nào để chuyển tiếp từ 443 wss sang ws? rewritecond có thay đổi không?
Hernán Eche

1
Điều kiện RewriteCond %{QUERY_STRING} transport=websocket [NC]không hoạt động đúng với tôi. Đề nghị sử dụng RewriteCond %{HTTP:Upgrade} =websocket [NC]thay thế.
Martin

99

Thay vì lọc theo URL, bạn cũng có thể lọc theo tiêu đề HTTP. Cấu hình này sẽ hoạt động đối với bất kỳ ứng dụng web nào sử dụng websockets, cũng như nếu chúng không sử dụng 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>

Điều này đang hoạt động tốt đối với tôi nhưng tôi đang proxy một đường dẫn con và tôi thấy điều quan trọng là phải thêm các ký tự ^ vì regex đang tìm kiếm một chuỗi con. Ví dụ: RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (nếu không / bar / foo cũng sẽ được chuyển hướng đến cùng một nơi với / foo)
Steve Lilly

Cấu hình này hoạt động tốt nhất trên đám mây alibaba. Ngoài ra, nó sẽ sửa lỗi net :: ERR_RESPONSE_HEADERS_TRUNCATED "Hy vọng ai đó thấy điều này hữu ích.
Mike Musni

Sử dụng SignalR, tôi có thể nói, điều này hiệu quả nhất đối với tôi +1 Cảm ơn
Vojtěch Mráz

18

Có thể sẽ hữu ích. Chỉ cần tất cả các truy vấn gửi qua ws tới nút

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

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

Cảm ơn @Sergey. Điều này đã giúp tôi khi tôi chỉ sử dụng đường dẫn trực tiếp được tham chiếu bởi ws thay vì định tuyến mọi thứ đến tài liệu gốc. Tôi đã chỉ ra những gì tôi đã làm trong câu trả lời bên dưới vì lợi ích của những người khác.
Anwaarullah

Câu trả lời này làm việc cho cổng web đơn giản (không socket.io) nên đối với tôi đây là câu trả lời tốt hơn
Tomas

Tuyệt vời! Điều này đã ngay lập tức làm việc cho tôi trong khi các nỗ lực viết lại và proxy khác không giải quyết được vấn đề của tôi.
Stephan

Cảm ơn bạn!! Hoàn hảo cho trường hợp sử dụng cơ bản, chỉ cần một ổ cắm web đơn giản sử dụng ws: //
Antonia Blair

16

Kể từ Socket.IO 1.0 (tháng 5 năm 2014), tất cả các kết nối bắt đầu bằng một yêu cầu thăm dò HTTP (thêm thông tin tại đây ). Điều đó có nghĩa là ngoài việc chuyển tiếp lưu lượng WebSocket, bạn cần chuyển tiếp bất kỳ transport=pollingyêu cầu HTTP nào .

Giải pháp bên dưới sẽ chuyển hướng tất cả lưu lượng socket một cách chính xác, mà không chuyển hướng bất kỳ lưu lượng nào khác.

  1. Bật các mod Apache2 sau:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
  2. Sử dụng các cài đặt này trong tệp * .conf của bạn (ví dụ /etc/apache2/sites-available/mysite.com.conf). Tôi đã bao gồm các nhận xét để giải thích từng phần:

    <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. Tôi đã bao gồm một phần bổ sung để định tuyến /nodelưu lượng truy cập mà tôi thấy hữu ích, hãy xem tại đây để biết thêm thông tin.


1
Điều này hoàn toàn phù hợp với tôi. Lưu ý rằng nếu yêu cầu của bạn đến vào một URL khác ngoài thư mục gốc, bạn có thể làm ví dụRewriteRule /path/(.*)
Rob Gwynn-Jones

8

Với sự trợ giúp từ những câu trả lời này, cuối cùng tôi đã nhận được proxy ngược cho Node-RED chạy trên Raspberry Pi với Ubuntu Mate và Apache2 đang hoạt động, sử dụng cấu hình trang Apache2 này:

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

Tôi cũng phải kích hoạt các mô-đun như thế này:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel

7

Đối với tôi, nó hoạt động sau khi chỉ thêm một dòng trong httpd.conf như bên dưới (dòng in đậm).


<VirtualHost *:80>
    ServerName: xxxxx

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

Phiên bản Apache là 2.4.6 trên CentOS.


5

Thiết lập của tôi:

  • Apache 2.4.10 (chạy khỏi Debian)
  • Node.js (phiên bản 4.1.1) Ứng dụng chạy trên cổng 3000 chấp nhận WebSockets tại đường dẫn /api/ws

Như đã đề cập ở trên bởi @Basj, hãy đảm bảo rằng proxy a2enmod và ws_tunnel được bật.

Đây là ảnh chụp màn hình của tệp cấu hình Apache đã giải quyết vấn đề của tôi:

Cấu hình Apache

Phần có liên quan dưới dạng văn bản:

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

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

Hy vọng rằng sẽ giúp.


Tôi đang gặp sự cố khi thiết lập ứng dụng của mình với một URL ưa thích hơn là mặc định, bạn có phiền giúp tôi không? (Tôi đang ngồi trên máy chủ bộ đệm véc-ni)
Josh

6
Bạn có thể sao chép / dán thay vì ảnh chụp màn hình không? Cảm ơn bạn trước, nó sẽ cải thiện khả năng đọc.
Basj

3

Đã làm như sau cho một ứng dụng mùa xuân chạy nội dung tĩnh, phần còn lại và websocket.

Apache được sử dụng làm Proxy và SSL Endpoint cho các URI sau:

  • / app → nội dung tĩnh
  • / api → REST API
  • / api / ws → websocket

Cấu hình 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>

Tôi sử dụng cùng một cấu hình vHost cho cấu hình SSL, không cần thay đổi bất kỳ thứ gì liên quan đến proxy.

Cấu hình mùa xuân

server.use-forward-headers: true

Sử dụng một thẻ Location đến các địa điểm riêng biệt, ví dụ: <Location / app> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </ Location>
CrazyMerlin

2

Sử dụng liên kết này cho giải pháp perfact cho ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

Bạn chỉ cần thực hiện bước dưới đây ..

Đi đến /etc/apache2/mods-available

Bước 1

Kích hoạt mode proxy_wstunnel.loadbằng cách sử dụng lệnh dưới đây

$a2enmod proxy_wstunnel.load

Bước 2

Đi đến /etc/apache2/sites-available

và thêm dòng bên dưới vào tệp .conf của bạn bên trong máy chủ ảo

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

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

Lưu ý: 8080 có nghĩa là cổng chạy tomcat của bạn vì chúng tôi muốn kết nối wsnơi tệp War của chúng tôi được đặt trong tomcat và apache phục vụ tomcat cho ws. cảm ơn bạn

Cấu hình của tôi

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

Xin lỗi tôi không thực sự hiểu (đặc biệt là câu cuối cùng của bạn). Bạn có thể cải thiện định dạng của câu trả lời và thêm chi tiết cho những người không biết tất cả những gì bạn đề cập không?
Basj

Tomcat @ArvindMadhukar là gì?
Basj

1

Đối với vận chuyển "bỏ phiếu".

Phía 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>

Phía khách hàng:

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

1

Ngoài câu trả lời chính: nếu bạn có nhiều dịch vụ trên cùng một máy chủ sử dụng websockets, bạn có thể muốn thực hiện việc này để tách chúng ra, bằng cách sử dụng đường dẫn tùy chỉnh (*):

Máy chủ nút:

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

HTML máy khách:

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

Cấu hình 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]

(*) Lưu ý: sử dụng mặc định RewriteCond %{REQUEST_URI} ^/socket.iosẽ không dành riêng cho một trang web và các yêu cầu websockets sẽ được trộn lẫn giữa các trang web khác nhau!


0

LÀM:

  1. Đã cài đặt Apache 2.4 (không hoạt động với 2.2) a2enmod proxya2enmod proxy_wstunnel.load

  2. Thực hiện việc này trong cấu hình Apache
    chỉ cần thêm hai dòng trong tệp của bạn, nơi 8080 là cổng chạy tomcat của bạn

    <VirtualHost *:80>
    ProxyPass "/ws2/" "ws://localhost:8080/" 
    ProxyPass "/wss2/" "wss://localhost:8080/"
    
    </VirtualHost *:80>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.