WebSockets และ Apache Proxy: วิธีกำหนดค่า mod_proxy_wstunnel


101

ฉันมี :

  1. Apache(v2.4) บนพอร์ต 80 ของเซิร์ฟเวอร์ของฉันwww.domain1.comกับmod_proxyและmod_proxy_wstunnelเปิดใช้งาน

  2. node.js + socket.io บนพอร์ต 3001 ของเซิร์ฟเวอร์เดียวกัน

การเข้าถึงwww.domain2.com(มีพอร์ต 80) เปลี่ยนเส้นทางไป 2. ต้องขอบคุณวิธีการอธิบายที่นี่ ฉันได้ตั้งค่านี้ในการกำหนดค่า Apache:

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

ใช้งานได้กับทุกอย่างยกเว้นส่วน websocket: ws://...ไม่ได้รับการส่งผ่านพร็อกซีอย่างที่ควรจะเป็น

เมื่อฉันเข้าถึงเพจwww.domain2.comฉันมี:

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

คำถาม: จะทำให้ Apache Proxy เป็น WebSockets ได้อย่างไร?

คำตอบ:


163

ในที่สุดฉันก็ทำได้ขอบคุณหัวข้อนี้

ทำ:

1) ติดตั้ง Apache 2.4 แล้ว (ใช้ไม่ได้กับ 2.2) และทำ:

a2enmod proxy
a2enmod proxy_http
a2enmod proxy_wstunnel

2) nodejsทำงานบนพอร์ต 3001

3) ทำสิ่งนี้ในการกำหนดค่า 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>

หมายเหตุ: หากคุณมีมากกว่าหนึ่งบริการบนเซิร์ฟเวอร์เดียวกันที่ใช้ websockets คุณอาจต้องการทำสิ่งนี้เพื่อแยกบริการ


FWIW, Apache 2.4 ใน CentOS 7.1 มีข้อผิดพลาดนี้ดังนั้นการเขียนซ้ำจะไม่รู้จักโปรโตคอล ws: // และจะสั่งโดเมนล่วงหน้าก่อนที่จะส่งคำขอย่อย คุณสามารถทดสอบตัวเองได้โดยเปลี่ยนแฟล็ก [P] roxy เป็น [R] edirect
Andor

3
ฉันยังคงได้รับ 502 bad gateway สำหรับ ws: // route เมื่อทำสิ่งนี้ ใช้ Apache 2.4 บน Ubuntu 14.04
Alex Muro

1
การดำเนินการนี้ได้ผลเนื่องจากการรับส่งข้อมูล HTTP ทั้งหมดจะถูกส่งต่อเช่นกัน แต่ถ้าคุณต้องการส่งต่อการรับส่งข้อมูลซ็อกเก็ตของคุณเท่านั้นโปรดทราบว่า Socket.io เริ่มการสื่อสารด้วยคำขอการสำรวจ HTTP ข้อมูลเพิ่มเติมที่นี่
Erik Koopmans

4
วิธีการส่งต่อจาก 443 wss เป็น ws? rewritecond เปลี่ยนแปลงหรือไม่
Hernán Eche

1
เงื่อนไขRewriteCond %{QUERY_STRING} transport=websocket [NC]ไม่ทำงานฉันอย่างถูกต้อง แนะนำให้ใช้RewriteCond %{HTTP:Upgrade} =websocket [NC]แทน.
Martin

102

แทนที่จะกรองตาม URL คุณยังกรองตามส่วนหัว HTTP ได้อีกด้วย การกำหนดค่านี้จะใช้ได้กับเว็บแอปพลิเคชันใด ๆ ที่ใช้เว็บซ็อกเก็ตหากไม่ได้ใช้ 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>

มันใช้งานได้ดีสำหรับฉัน แต่ฉันกำลังพร็อกซีพา ธ ย่อยและฉันพบว่าการเพิ่ม ^ anchors เป็นสิ่งสำคัญเนื่องจาก regex กำลังมองหาสตริงย่อย เช่น RewriteRule ^ [^ /] * / foo /(.*) http: // $ {FOO_HOST} / $ 1 [P, L] (มิฉะนั้น / bar / foo จะถูกนำไปที่เดียวกับ / foo)
สตีฟ Lilly

การกำหนดค่านี้ทำงานได้ดีที่สุดบนคลาวด์ของ alibaba นอกจากนี้จะแก้ไข error net :: ERR_RESPONSE_HEADERS_TRUNCATED "หวังว่าจะมีคนพบว่าสิ่งนี้มีประโยชน์
Mike Musni

การใช้ SignalR ฉันสามารถพูดได้ว่าสิ่งนี้ได้ผลดีที่สุดสำหรับฉัน +1 ขอบคุณ
VojtěchMráz

18

อาจจะเป็นประโยชน์ เพียงแค่ข้อความค้นหาทั้งหมดส่งผ่าน ws ไปยังโหนด

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

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

ขอบคุณ @Sergey สิ่งนี้ช่วยฉันเมื่อฉันใช้เฉพาะเส้นทางโดยตรงที่อ้างอิงโดย ws แทนที่จะกำหนดเส้นทางทุกอย่างไปยังรูทเอกสาร ฉันได้แสดงสิ่งที่ฉันได้ทำในคำตอบด้านล่างเพื่อประโยชน์ของผู้อื่น
Anwaarullah

คำตอบนี้ใช้ได้กับเว็บซ็อกเก็ตธรรมดา (ไม่มี socket.io) ดังนั้นสำหรับฉันแล้วนี่คือคำตอบที่ดีกว่า
Tomas

สุดยอด! สิ่งนี้ใช้งานได้ทันทีสำหรับฉันในขณะที่ความพยายามเขียนซ้ำและพร็อกซีอื่น ๆ ไม่สามารถแก้ปัญหาของฉันได้
Stephan

ขอบคุณ!! เหมาะสำหรับกรณีการใช้งานพื้นฐานเพียงแค่เว็บซ็อกเก็ตธรรมดาโดยใช้ ws: //
Antonia Blair

18

ตั้งแต่ Socket.IO 1.0 (พฤษภาคม 2014) การเชื่อมต่อทั้งหมดเริ่มต้นด้วยคำขอการสำรวจ HTTP (ข้อมูลเพิ่มเติมที่นี่ ) นั่นหมายความว่านอกเหนือจากการส่งต่อการรับส่งข้อมูล WebSocket แล้วคุณต้องส่งต่อtransport=pollingคำขอ HTTP ใด ๆ

วิธีแก้ปัญหาด้านล่างนี้ควรเปลี่ยนเส้นทางการรับส่งข้อมูลซ็อกเก็ตทั้งหมดอย่างถูกต้องโดยไม่ต้องเปลี่ยนเส้นทางการรับส่งข้อมูลอื่น ๆ

  1. เปิดใช้งาน Apache2 mods ต่อไปนี้:

    sudo a2enmod proxy rewrite proxy_http proxy_wstunnel
    
  2. ใช้การตั้งค่าเหล่านี้ในไฟล์ * .conf ของคุณ (เช่น/etc/apache2/sites-available/mysite.com.conf) ฉันได้รวมความคิดเห็นเพื่ออธิบายแต่ละชิ้น:

    <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. เราได้รวมส่วนพิเศษสำหรับเส้นทาง/nodeการจราจรที่ผมพบว่ามีประโยชน์ดูที่นี่สำหรับข้อมูลเพิ่มเติม


1
สิ่งนี้ทำงานได้อย่างสมบูรณ์แบบสำหรับฉัน โปรดทราบว่าหากคำขอของคุณเข้าสู่ URL อื่นที่ไม่ใช่รูทคุณสามารถทำได้เช่นRewriteRule /path/(.*)
Rob Gwynn-Jones

9

สำหรับฉันมันใช้งานได้หลังจากเพิ่มเพียงบรรทัดเดียวใน httpd.conf ด้านล่าง (บรรทัดตัวหนา)


<VirtualHost *:80>
    ServerName: xxxxx

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

เวอร์ชัน Apache คือ 2.4.6 บน CentOS


8

ด้วยความช่วยเหลือจากคำตอบเหล่านี้ในที่สุดฉันก็ได้รับ reverse proxy สำหรับ Node-RED ที่ทำงานบน Raspberry Pi โดยใช้ Ubuntu Mate และ Apache2 โดยใช้การกำหนดค่าไซต์ 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>

ฉันต้องเปิดใช้งานโมดูลเช่นนี้ด้วย:

sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel

5

การตั้งค่าของฉัน:

  • Apache 2.4.10 (ปิด Debian)
  • Node.js (เวอร์ชัน 4.1.1) แอปที่ทำงานบนพอร์ต 3000 ที่ยอมรับ WebSockets ที่พา ธ /api/ws

ดังที่ได้กล่าวไว้ข้างต้นโดย @Basj ตรวจสอบให้แน่ใจว่าเปิดใช้ a2enmod proxy และ ws_tunnel แล้ว

นี่คือภาพหน้าจอของไฟล์กำหนดค่า Apache ที่แก้ไขปัญหาของฉัน:

การกำหนดค่า Apache

ส่วนที่เกี่ยวข้องเป็นข้อความ:

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

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

หวังว่าจะช่วยได้


ฉันมีปัญหาในการตั้งค่าแอปด้วย URL ที่ต้องการแทนที่จะเป็นค่าเริ่มต้นคุณช่วยฉันหน่อยได้ไหม (ฉันกำลังนั่งอยู่บนเซิร์ฟเวอร์แคชวานิช)
Josh

6
คุณสามารถคัดลอก / วางแทนภาพหน้าจอได้ไหม ขอบคุณล่วงหน้าซึ่งจะช่วยเพิ่มความสามารถในการอ่าน
Basj

4

ทำสิ่งต่อไปนี้สำหรับแอปพลิเคชัน Spring ที่รันเนื้อหาแบบคงที่ส่วนที่เหลือและ websocket

Apache ใช้เป็น Proxy และ SSL Endpoint สำหรับ URI ต่อไปนี้:

  • / app →เนื้อหาคงที่
  • / api → REST API
  • / api / ws → websocket

การกำหนดค่า 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>

ฉันใช้ vHost config เดียวกันสำหรับการกำหนดค่า SSL ไม่จำเป็นต้องเปลี่ยนพร็อกซีอะไรเลย

การกำหนดค่าสปริง

server.use-forward-headers: true

ใช้แท็กตำแหน่งเพื่อแยกตำแหน่งเช่น: <Location / app> ProxyPass localhost: 8080 / app ProxyPassReverse localhost: 8080 / app </Location>
CrazyMerlin

2

นอกเหนือจากคำตอบหลัก: หากคุณมีมากกว่าหนึ่งบริการบนเซิร์ฟเวอร์เดียวกันที่ใช้ websockets คุณอาจต้องการทำสิ่งนี้เพื่อแยกพวกเขาโดยใช้เส้นทางที่กำหนดเอง (*):

เซิร์ฟเวอร์โหนด:

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

HTML ของลูกค้า:

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

การกำหนดค่า 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]

(*) หมายเหตุ: การใช้ค่าเริ่มต้นRewriteCond %{REQUEST_URI} ^/socket.ioจะไม่เฉพาะเจาะจงสำหรับเว็บไซต์และคำขอ websockets จะผสมกันระหว่างเว็บไซต์ต่างๆ!


2

ใช้ลิงก์นี้สำหรับโซลูชันแบบสมบูรณ์สำหรับ ws https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

คุณต้องทำตามขั้นตอนด้านล่าง ..

ไปที่ /etc/apache2/mods-available

ขั้นตอนที่ 1

เปิดใช้งานmode proxy_wstunnel.loadโดยใช้คำสั่งด้านล่าง

$a2enmod proxy_wstunnel.load

ขั้นตอนที่ 2

ไปที่ /etc/apache2/sites-available

และเพิ่มบรรทัดด้านล่างในไฟล์. config ของคุณภายในโฮสต์เสมือน

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

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

หมายเหตุ: 8080 หมายถึงพอร์ตที่ใช้ tomcat ของคุณเพราะเราต้องการเชื่อมต่อwsที่ไฟล์ War ของเราใส่ไว้ใน tomcat และ tomcat ให้บริการ apache สำหรับwsในคราวและแมวตัวผู้ให้บริการสำหรับ ขอบคุณ

การกำหนดค่าของฉัน

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

ขอโทษฉันไม่เข้าใจจริงๆ (โดยเฉพาะประโยคสุดท้ายของคุณ) คุณสามารถปรับปรุงการจัดรูปแบบของคำตอบและเพิ่มรายละเอียดสำหรับผู้ที่ไม่ทราบสิ่งที่คุณพูดถึง
Basj

Tomcat @ArvindMadhukar คืออะไร?
Basj

1

สำหรับ "การสำรวจ" การขนส่ง

ด้าน 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>

ด้านลูกค้า:

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

1

ทำ:

  1. ติดตั้ง Apache 2.4 แล้ว (ใช้ไม่ได้กับ 2.2) a2enmod proxyและa2enmod proxy_wstunnel.load

  2. ทำสิ่งนี้ในการกำหนดค่า Apache
    เพียงเพิ่มสองบรรทัดในไฟล์ของคุณโดยที่ 8080 เป็นพอร์ตที่ใช้ Tomcat ของคุณ

    <VirtualHost *:80>
    ProxyPass "/ws2/" "ws://localhost:8080/" 
    ProxyPass "/wss2/" "wss://localhost:8080/"
    
    </VirtualHost *:80>
    
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.