Apache 2.4.7 mod_proxy_wstunnel 隧道太多(HTTP 和 WS)
我在 Ubuntu 14.04 LTS 上執行 Apache 2.4.7 作為反向代理。此 Apache 伺服器充當許多不同後端應用程序的入口點,這些應用程序通過
<Location>
塊中的不同 mod_proxy 配置訪問我需要為使用 WebSockets 的應用程序提供反向代理訪問。該應用程序是一個 Java Spring 應用程序,它通過 HTTP 提供 HTML 和其他靜態文件,然後在頁面載入後使用 WebSocket 獲取動態數據。
我
Nginx
使用以下配置讓應用程序在後面執行:location /newapp/ { proxy_pass http://newapp.example.com:8080/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }
不幸的是,由於需要 Nginx 上不可用的 Apache 身份驗證模組,我無法在生產中使用它。
在偽 Apache-config 中,我想做的是:
<Location /newapp/> if not WebSockets: ProxyPass http://newapp.example.com:8080/ ProxyPassReverse / else ProxyPass ws://newapp.example.com:8080/ ProxyPassReverse / </Location>
Apache
mod_proxy_wstunnel
模組讓我認為這應該是可能的。WebSocket 是在 URL 上訪問的/api/socket/...
,所以我嘗試ProxyPass
使用單獨的<Location>
塊分隔兩種類型:<Location /newapp/> ProxyPass http://newapp.example.com:8080/ disablereuse=on ProxyPassReverse / ProxyPassReverseCookieDomain newapp.example.com apps.example.com ProxyPassReverseCookiePath http://newapp.example.com:8080/ /newapp/ </Location> <Location /newapp/api/socket/> ProxyPass ws://newapp.example.com:8080/api/socket/ ProxyPassReverse / </Location>
這最初是有效的——瀏覽器請求http://apps.example.com/newapp/,頁面通過 HTTP 載入,靜態資源被載入,JavaScript 程式碼連接到 websocket,一切都很棒。
但是,當通過 HTTP 發出新請求時,例如 for
GET /newapp/static/someimage.png
,就會出現問題。此請求與 WebSocket 位置不匹配,因此我希望它能夠代理GET /static/someimage.png
到http://newapp.example.com:8080/
.相反,應用程序伺服器收到請求
GET /newapp/static/someimage.png
並返回 404,因為這不是它知道的 URL。這會破壞應用程序,因為應該工作的 HTTP 請求會失敗。筆記:
- 這不僅發生在圖像上 -
GET /newapp/api/ajax/someapicall
也被錯誤地代理。- 這並不總是發生。在測試完成這個問題的過程中,我設法讓應用程序完全執行。這可能是基於時間的——在發出任何新的 HTTP 請求之前,我讓應用程序執行了幾分鐘而沒有與之互動。當我確實發出新的 HTTP 請求時,它們正確地通過了。
- 禁用該
<Location /newapp/api/socket/>
部分會導致兩件事發生 - WebSocket 無法連接,並且 HTTP 請求繼續工作。- 通過瀏覽器的刷新按鈕刷新頁面後,我發現了這個問題。我沒有再次載入頁面,而是看到了應用程序的 404 螢幕。
我認為正在發生的事情:
我認為
mod_proxy_wstunnel
,一旦被第一個匹配請求啟動/newapp/api/socket/
,就會接管來自客戶端的所有進一步的入站請求,無論它們是否匹配Location
。RequestHeader set Test "some_identifying_value"
我通過向每個指令添加一個指令來測試這一點Location
- 靜態文件的 HTTP 請求和 for在它們上/api/socket/info
有Test
標頭,但錯誤代理的 HTTP 請求上沒有Test
標頭,這表明它們被直接傳遞而沒有由 Apache 指令處理。最終,我的問題是:是否可以將任何版本的 Apache(我很高興升級!)配置為反向代理基於 WebSocket 的應用程序,從而在 WebSocket 之後也正確地反向代理 HTTP 請求連接的?如果是這樣,這是如何配置的?
安德斯的回答讓我成功了 95%。
基本場景:
- 我們有一個伺服器
newapp.example.com
- 8080 埠同時執行 HTTP 和 WebSockets
- 響應 WebSockets 請求的 URL 是
/api/socket/
- 我們將此應用程序反向代理為
http://apps.example.com/newapp/
這是如何在一個
<Location>
塊中為上述場景配置 WebSockets 和 HTTP 反向代理:<Location /newapp/> ProxyPass http://newapp.example.com:8080/ ProxyPassReverse / RewriteEngine on RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC] RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC] RewriteRule /api/(.*) ws://newapp.example.com:8080/api/$1 [P] </Location>
最終的重寫規則至關重要——沒有它,我們會將請求
/newapp/api/socket
傳遞給 WebSocket 伺服器——它將被拒絕。正則表達式在之後解析所有內容
api
- 可能有更好的方法來擷取該塊,但這有效。然後我們必須記住重新添加/api/
到最終的重定向 URL。最重要的是,在 WebSocket 連接建立後,HTTP 請求繼續工作!