Apache 2.4.7 mod_proxy_wstunnel 隧道太多(HTTP 以及 WS)

Bri*_*ett 3 reverse-proxy mod-proxy apache-2.4 websocket

我在 Ubuntu 14.04 LTS 上运行 Apache 2.4.7 作为反向代理。这个 Apache 服务器充当许多不同后端应用程序的入口点,这些应用程序通过<Location>块中不同的 mod_proxy 配置进行访问

我需要提供对使用 WebSocket 的应用程序的反向代理访问。该应用程序是一个 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";
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,由于需要一个在 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>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

这最初有效 - 浏览器请求http://apps.example.com/newapp/,页面通过 HTTP 加载,静态资产被加载,JavaScript 代码连接到 websocket,一切都很棒。

但是,当通过 HTTP 发出新请求时 - 例如,对于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/,就会接管来自客户端的所有进一步入站请求,无论它们是否匹配Location。我通过向RequestHeader set Test "some_identifying_value"每个Location文件添加一个指令来测试这一点- 静态文件的 HTTP 请求和 for在它们上面/api/socket/infoTest标头,但是错误代理的 HTTP 请求上没有Test 标头,这表明它们被直接传递而没有被由 Apache 指令处理。

最终,我的问题是:是否可以将任何版本的 Apache(我很高兴升级!)配置为反向代理基于 WebSocket 的应用程序,以便在 WebSocket 完成后,HTTP 请求也被正确反向代理连接的?如果是这样,这是如何配置的?

Bri*_*ett 7

安德斯的回答让我成功了 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>
Run Code Online (Sandbox Code Playgroud)

最终的重写规则至关重要 - 没有它,我们会将请求/newapp/api/socket传递给 WebSocket 服务器 - 它会拒绝。

正则表达式正在解析之后的所有内容api- 可能有更好的方法来捕获该块,但这有效。然后我们必须记住重新添加/api/到最终重定向 URL。

最重要的是,HTTP 请求在 WebSocket 连接建立后继续工作!