Apache-2.4

Apache 2.4.7 mod_proxy_wstunnel 隧道太多(HTTP 和 WS)

  • May 30, 2020

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

Apachemod_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.pnghttp://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/,就會接管來自客戶端的所有進一步的入站請求,無論它們是否匹配LocationRequestHeader set Test "some_identifying_value"我通過向每個指令添加一個指令來測試這一點Location- 靜態文件的 HTTP 請求和 for在它們上/api/socket/infoTest標頭,但錯誤代理的 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 請求繼續工作!

引用自:https://serverfault.com/questions/837104