【问题标题】:mod_proxy_wstunnel - Mac OS X 10.11.6, Apache 2.4.18mod_proxy_wstunnel - Mac OS X 10.11.6,Apache 2.4.18
【发布时间】:2017-05-08 08:43:19
【问题描述】:

几天来我一直在尝试设置反向代理到 localhost websocket url,但没有成功。

ProxyPass /chat/stream/ wss://localhost:8000/chat/stream/
ProxyPassReverse /chat/stream/ wss://localhost:8000/chat/stream/

我在 apache error_log 中收到一个错误,内容如下:

没有对 URL /chat/stream/ 有效的协议处理程序。如果你是 使用 mod_proxy 的 DSO 版本,确保代理子模块是 使用 LoadModule 包含在配置中。

我已经通过谷歌阅读了无数使用这种方法的人的页面,所以我想知道我们在 Server.app 5.2 附带的 Apache 的设置/安装中是否存在一些问题?

我在 httpd_server_app.conf 中加载了所有标准模块

mod_proxy mod_proxy_wstunnel mod_proxy_http ...

有人能解释一下吗?

谢谢

亚当

【问题讨论】:

  • 如果将wss:// 替换为http:// 会发生什么?
  • 使用 http 可以工作,但是 Web 应用程序需要通过 wss(Web 套接字?)进行连接 - 客户端收到 200 响应并且握手失败。如果我直接通过 localhost:8000 在服务器上使用 Web 应用程序,则该应用程序可以正常工作。
  • 您是否使用sudo a2enmod mod_proxy_wstunnel 加载了 mod_proxy_wstunnel ?
  • 我正在使用 Mac OS X - 据我了解 mod_proxy_wstunnel 模块已加载到 httpd_server_app.conf 文件中 - 以及 mod 代理
  • 如果模块被正确加载,你应该在sudo apache2ctl -M的输出中看到它

标签: macos apache websocket macos-sierra osx-server


【解决方案1】:

如果有人发现自己处于类似情况,这就是我如何在 MacOS Server 5.2 中通过 Apache 获得 WebSocket 连接。 而且解决方案很简单。

短版:​

​我使用 MacOS Server 5.2(Apache 2.4.23 附带)通过mod_wsgi 模块运行 python Django 应用程序。

我一直试图在 MacOS 10.12 和 Server 5.2 中设置 proxypasswstunnel​,以通过在 localhost 端口 8001 上运行的名为 Daphne 的 ASGI 接口服务器处理 websocket 连接。​

我想将任何 WebSocket 连接反向代理到 wss://myapp.local/chat/stream/ws://localhost:8001/chat/stream/

​根据我在所有论坛和邮件列表中阅读的内容,我在适当的虚拟主机中简单地定义了一些 proxypass 并确保加载了 mod_proxymod_proxy_wstunnel 模块并它会工作的。

长话短说 - 据我了解,所有这些问题都归结为 MacOS Server 5 和一个重大变化:

“一个 httpd 实例作为反向代理运行,称为服务代理,另外几个 httpd 实例在该代理后面运行,以支持特定的基于 HTTP 的服务,包括网站服务的一个实例。”

代理 websocket 连接我需要做的事情如下:

输入:/Library/Server/Web/Config/Proxy/apache_serviceproxy.conf

​添加(在line 297附近(在关于用户网站的部分,webdav):

ProxyPass / http://localhost:8001/
ProxyPassReverse / http://localhost:8001/

RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://localhost:8001%{REQUEST_URI} [P]

​然后我踢了服务代理:

sudo launchctl unload -w  /Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons/com.apple.serviceproxy.plist
sudo launchctl load -w /Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons/com.apple.serviceproxy.plist

​并且网络套接字连接立即工作!

长版:​

多年来,我一直在尝试在我正在开发的应用程序中让 WebSocket 连接与 Apache 和 Andrew Godwin 的 Django Channels 项目一起运行。

Django 频道是

“一个项目使 Django 能够处理的不仅仅是普通的 HTTP 请求,包括 WebSockets 和 HTTP2,以及在发送响应后运行代码的能力,例如缩略图或背景计算。”

我对 Django Channels 的兴趣来自我对 web 应用程序中聊天系统的需求。在 youtube 上观看了几个 Andrew 的演示并阅读了文档并最终安装了 Andrew 的演示 django 频道项目后,我认为我可以在我们的 MacOS 服务器上将其投入生产。

MacOS 10.12 和 Server 5.2 的当前版本随 Apache 2.4.23 一起提供。它带有必要的 mod_proxy_wstunnel 模块,以便能够在 Apache 中代理 WebSocket 连接(ws:// 和安全 wss://),并且已经加载到服务器配置文件中:

/Library/Server/Web/Config/apache2/httpd_server_app.conf

Daphne 是 Andrew 的 ASGI 接口服务器,支持 WebSockets 和长轮询 HTTP 请求。 WSGI 没有。

由于 Daphne 在 MacOS 未占用的端口上的 localhost 上运行(我选择了8001),因此想法是让 Apache 反向代理对 Daphne 的某些请求。

Daphne 可以在指定端口(本例中为 8001)上运行,如下所示(-v2 更详细):

daphne -p 8001 yourapp.asgi:channel_layer -v2

我希望 Daphne 只处理 Web 套接字连接(因为我目前使用依赖于一些 apache 模块来提供媒体服务,例如 mod_xsendfile)。在我的例子中,websocket 连接是通过 /chat/stream/ 基于 Andrew 的演示项目。

根据我在 MacOS Server 的 Apache 实现中所读到的内容,想法是在您的“站点”的虚拟主机文件中声明这些 proxypass 命令:/Library/Server/Web/Config/apache2/sites/

在配置文件中,例如:0000_127.0.0.1_34543_.conf

我还读到,对于在 MacOS Server 上运行的 Web 应用程序的任何自定义都应在以下位置对所需 Web 应用程序的 plist 文件进行:/Library/Server/Web/Config/apache2/webapps/

在 plist 文件中,例如:com.apple.webapp.wsgi.plist

无论如何...

我编辑了0000_127.0.0.1_34543_.conf 文件添加:

ProxyPass /chat/stream/ ws://localhost:8001/
ProxyPassReverse /chat/stream/ ws://localhost:8001/

急于测试我的第一个 Web 套接字聊天连接,我刷新了页面,却发现 apache 日志中打印了一个错误:

No protocol handler was valid for the URL /chat/stream/. If you are using a DSO version of mod_proxy, make sure the proxy submodules are included in the configuration using LoadModule.

我读过很多人至少在 Ubuntu 上使用 Apache 或在 MacOS 上自定义安装找到了解决方案。 我什至尝试使用 Brew 安装 Apache,但当它不起作用时,我几乎开始安装 nginx

经过无数小时/天的谷歌搜索后,我访问了 Apache 邮件列表以寻求有关此错误的帮助。 Yann Ylavic 对他的时间非常慷慨,并为我提供了各种关于如何进行的想法。尝试以下方法后:

SetEnvIf Request_URI ^/chat/stream/ is_websocket
RequestHeader set Upgrade WebSocket env=is_websocket
ProxyPass /chat/stream/ ws://myserver.local:8001/chat/stream/

我注意到端口 8001 Daphne 上的接口服务器开始接收 ws 连接! 但是在客户端浏览器中它正在记录:

"Error during WebSocket handshake: 'Upgrade' header is missing"

据我所知,mod_dumpio 记录了 "Connection: Upgrade""Upgrade: WebSocket" 标头作为 Web 套接字握手的一部分发送:

mod_dumpio:  dumpio_in (data-HEAP): HTTP/1.1 101 Switching Protocols\r\nServer: AutobahnPython/0.17.1\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: 17WYrMeMS8a4ImHpU0gS3/k0+Cg=\r\n\r\n
mod_dumpio.c(164): [client 127.0.0.1:63944] mod_dumpio: dumpio_out
mod_dumpio.c(58): [client 127.0.0.1:63944] mod_dumpio:  dumpio_out (data-TRANSIENT): 160 bytes
mod_dumpio.c(100): [client 127.0.0.1:63944] mod_dumpio:  dumpio_out (data-TRANSIENT): HTTP/1.1 101 Switching Protocols\r\nServer: AutobahnPython/0.17.1\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: 17WYrMeMS8a4ImHpU0gS3/k0+Cg=\r\n\r\n

但是客户端浏览器在响应标头中没有显示任何内容。

我比以往任何时候都更难过。

我探索了客户端 jQuery 框架以及 Django 频道和高速公路模块,看看是否有问题,然后修改了我自己的应用程序以及关于 Apache 及其模块的各种建议组合。但没有什么对我来说很突出。

​然后我重新阅读了 apache2 目录中的 ReadMe.txt:/Library/Server/Web/Config/apache2/ReadMe.txt

"​关于Server应用中web代理架构的特别说明 5.0:

此版本的服务器应用程序包含所有基于 HTTP 的服务的修订架构。在以前的版本中,有一个 httpd 实例作为 Wiki、Profile 和日历/地址服务的反向代理,也作为网站服务。​​​在这个版本中,有一个重大变化:单个httpd 的实例作为反向代理运行,称为 Service Proxy,另外几个 httpd 实例在该代理后面运行以支持特定的基于 HTTP 的服务,包括网站服务的实例。

由于网站服务的httpd 实例现在位于反向代理或Service Proxy 后面,请注意以下几点:......只有外部Service Proxy httpd 实例侦听TCP 端口80443;它代理对网站和其他基于 HTTP 的服务的 HTTP 请求和响应。 ​...​"​

​我想知道 ServiceProxy 是否与它有关。我看了看:/Library/Server/Web/Config/Proxy/apache_serviceproxy.conf 并注意到一条评论 - "# The user websites, and webdav"​

我认为尝试添加人们在论坛上建议的proxypass 定义和rewrite 规则作为他们的解决方案不会有什么坏处。

ProxyPass / http://localhost:8001/
ProxyPassReverse / http://localhost:8001/

RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://localhost:8001%{REQUEST_URI} [P]

​果然重启ServiceProxy后就开始工作了!​

亚当

【讨论】:

  • 伙计...你摇滚!
【解决方案2】:

查看 /Library/Server/Web/Config/apache2/ 中的 ReadMe.txt 文件

它描述了新的代理服务,其中请求首先发送到运行在端口 80 和 443 上的 http 服务器,然后转发到内部端口 34580 和 34543。

对于 wstunnel 模块,与 mod proxy 模块存在冲突,mod proxy 将删除隧道标头 (https://lists.gt.net/apache/users/393509)。我通过添加到 LogFormat 来更改 apache_serviceproxy.conf 中的 LogFormat 来确认这一点

c:\"%{Connection}i\" u:\"%{Upgrade}i\"

我在 httpd_server_app.conf 中做了同样的事情,并且可以看到连接和升级 websocket 标头在进入我的 webapp 之前被删除。

解决方法只是在 /Library/Server/Web/Config/Proxy 中为我的应用程序添加一个文件。查看 apache_serviceproxy.conf 中的最后一行以了解预期的命名格式。在我的情况下,该文件称为 apache_serviceproxy_customsites_ws.conf

内容:

ProxyPass /ws/ ws://localhost:34543/ws/
ProxyPassReverse /ws/ ws://localhost:34543/ws/

这会将 ws 请求转发到预期的内部 https 端口 34543 并保留标头。您必须将其转发到 34543 或 34580。还要注意包含路径,以便在下一步中提取。

然后,在我的 webapp_script(我的 webapp 的包含文件)中,我有:

ProxyPass "/ws/"  "ws://localhost:61614/"
ProxyPassReverse "/ws/"  "ws://localhost:61614/"

这会将请求转发到我在端口 61614 上运行的 websocket 服务器。

有了它,它现在可以按预期工作了。

【讨论】:

    【解决方案3】:

    除了@adamteale 在他的回答(TL;DR)版本中提到的内容,我还必须添加

    ProxyPreserveHost On
    

    在我的 apache 虚拟主机配置中。没有这个,达芙妮没有回复任何回应。也许是,但它没有被发回给客户。

    我的设置唯一未解决的问题是 daphne 没有处理 wss 连接。要么 Apache 没有终止 wss 的 SSL,要么发生了其他事情。它会终止所有其他 http 请求的 SSL。但是,这个问题是另一个线程。我会在完成更多研究后立即提出它。

    注意:由于 MyCurrentRep ,无法将 cmets 添加到 Adam 的答案中

    【讨论】:

      猜你喜欢
      • 2017-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多