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

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 配置中执行的操作是:

<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 发出新请求时(例如,对于GET /newapp/static/someimage.png),就会出错。此请求与 WebSocket Location 不匹配,因此我希望它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 请求带有/api/socket/infoTest头,但错误代理的 HTTP 请求没有标Test 头,这表明它们直接通过而没有经过 Apache 指令的处理。

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

答案1

安德斯的回答帮助我解决了 95% 的问题。

基本场景:

  • 我们有一个服务器newapp.example.com
  • 端口 8080 同时运行 HTTP 和 WebSockets
  • 响应 WebSockets 请求的 URL 是/api/socket/
  • 我们正在将此应用程序反向代理为http://apps.example.com/newapp/

以下是如何在一个块中为上述场景配置 WebSocket 和 HTTP 反向代理的方法<Location>

<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 请求仍然有效!

答案2

我在 Spring Boot 应用程序前面使用 apache 2.4 作为代理,此应用程序提供一些 rest api 调用、websocket(sockjs)和一些静态页面。我在让 websocket 工作时遇到了一些问题,秘诀是添加重写规则你看到下面,现在它对我有用了,我的 apache 虚拟主机配置如下所示:

<VirtualHost *:443>
  SSLEngine on
  SSLCertificateFile /etc/httpd/ssl/my.crt
  SSLCertificateKeyFile /etc/httpd/ssl/my.key
  SSLCertificateChainFile /etc/httpd/ssl/intermediate.crt
  ProxyPreserveHost On
  ProxyPass / http://127.0.0.1:8080/
  ProxyPassReverse / http://127.0.0.1:8080/
  ProxyRequests Off
  RewriteEngine on
  RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
  RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
  RewriteRule .* ws://localhost:6868%{REQUEST_URI} [P]
  ServerName my.dnsname.com
</VirtualHost>

相关内容