mod_proxy_wstunnel - Mac OS X 10.11.6,Apache 2.4.18

时间:2016-12-22 16:44:04

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

我几天都没有成功尝试将反向代理设置为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 /有效。如果你是   使用DSO版本的mod_proxy,确保代理子模块是   使用LoadModule包含在配置中。

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

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

mod_proxy的 mod_proxy_wstunnel mod_proxy_http ...

任何人都可以对此有所了解吗?

由于

亚当

3 个答案:

答案 0 :(得分:2)

如果有人发现自己处于类似的情况,那么我是如何在MacOS Server 5.2中通过Apache获得Web Socket连接的。 解决方案很简单。

简短版本:

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

我一直在尝试在MacOS 10.12中设置proxypasswstunnel& Server 5.2通过端口localhost8001上运行的名为Daphne的ASGI接口服务器处理websocket连接。

我想将任何与wss://myapp.local/chat/stream/的WebSocket连接代理转发到ws://localhost:8001/chat/stream/

根据我在所有论坛和邮件列表中阅读的内容,我只是在相应的虚拟主机中制作了一些proxypass定义,并确保mod_proxy和{{1模块已加载,它可以工作。

长话短说 - 据我所知,所有这些麻烦都归结为MacOS Server 5并且有一个重大变化:

  

" httpd的单个实例作为反向代理运行,称为服务代理,并且在该代理后面运行若干其他httpd实例,以支持特定的基于HTTP的服务,包括网站服务的实例。 #34;

我需要编写代理websocket连接的所有内容如下:

in:mod_proxy_wstunnel

添加(约/Library/Server/Web/Config/Proxy/apache_serviceproxy.conf(在关于用户网站的部分,webdav):

line 297

然后我踢了服务代理:

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]

网络套接字连接立即正常工作!

长版:

在我开发的应用程序中,我一直试图让Apache和Andrew Godwin的Django Channels项目的WebSocket连接正常运行。

Django频道

  

"一个项目,使Django能够处理的不仅仅是普通的HTTP请求,包括WebSockets和HTTP2,以及在为缩略图或背景计算等事件发送响应后运行代码的能力。 #34;

我对Django频道的兴趣来自于我在webapp中对聊天系统的要求。在youtube上观看了几个安德鲁的演示并阅读了文档并最终安装了安德鲁的demo django频道项目之后,我想我可以在我们的MacOS服务器上进行制作。

当前版本的MacOS 10.12和Server 5.2附带Apache 2.4.23。这附带了必要的mod_proxy_wstunnel模块,可以在Apache中代理WebSocket连接(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 和安全ws://)并且已经加载到服务器配置文件中:

wss://
达芙妮是安德鲁的ASGI接口服务器,支持WebSockets& amp;长轮询HTTP请求。 WSGI没有。

Daphne在MacOS不占用的端口上的localhost上运行(我使用/Library/Server/Web/Config/apache2/httpd_server_app.conf ),其想法是让Apache反向代理对Daphne的某些请求。

Daphne可以在指定的端口(在此示例中为8001)上运行,如此(8001更详细):

-v2

我希望Daphne只处理Web套接字连接(因为我目前使用某些apache模块来处理诸如daphne -p 8001 yourapp.asgi:channel_layer -v2 之类的媒体)。就我而言,mod_xsendfile连接是基于Andrew的演示项目通过websocket建立的。

根据我在MacOS Server的Apache实现中所读到的,我们的想法是在你的" sites"的虚拟主机文件中声明这些proxypass命令。在:/chat/stream/

在配置文件中,例如:/Library/Server/Web/Config/apache2/sites/

我还读到,在MacOS服务器上运行的网络应用程序的任何自定义都应该针对所需的网络应用程序的plist文件:0000_127.0.0.1_34543_.conf

在plist文件中,例如:/Library/Server/Web/Config/apache2/webapps/

总之...

我编辑了com.apple.webapp.wsgi.plist文件添加:

0000_127.0.0.1_34543_.conf

急于测试我的第一个网络套接字聊天连接我刷新页面只是为了看到在apache日志中打印出错误:

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

我读过许多人在Ubuntu上至少使用Apache或在MacOS上自定义安装时找到解决方案。 我甚至尝试使用Brew安装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.

经过无数小时/天的谷歌搜索后,我到达了Apache邮件列表,以获得有关此错误的一些帮助。 Yann Ylavic对他的时间非常慷慨,并为我提供了关于如何实现这一目标的各种想法。尝试以下后:

nginx

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

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

从我所看到的"Error during WebSocket handshake: 'Upgrade' header is missing" 正在记录mod_dumpio"Connection: Upgrade"标头是作为Web套接字握手的一部分发送的:

"Upgrade: WebSocket"

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

我比以前更难过。

我探讨了客户端jQuery框架以及Django频道&高速公路模块,看看是否有什么不对劲,然后修改了我自己的应用程序和关于Apache及其模块的各种建议组合。但没有什么能让我感到高兴。

然后我重读了apache2目录中的ReadMe.txt: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

  

"有关服务器应用程序中Web代理体系结构的特别说明   5.0:

     

此版本的Server应用程序包含所有基于HTTP的服务的修订体系结构。在以前的版本中,有一个/Library/Server/Web/Config/apache2/ReadMe.txt实例充当Wiki,Profile和Calendar / Address服务的反向代理,并且还充当网站服务。在这个版本中,有一个重大变化: httpd的单个实例作为反向代理运行,称为httpd,并在该代理后面运行几个额外的Service Proxy实例,以支持特定的基于HTTP的服务,包括一个实例网站服务。

     

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

我想知道ServiceProxy是否因此而有所作为。我看了看:443 并注意到了评论 - /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf

我认为尝试添加"# The user websites, and webdav"​定义& amp;人们在论坛上建议的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] 后果然开始工作了!

亚当

答案 1 :(得分:1)

查看/ Library / Server / Web / Config / apache2 /

中的ReadMe.txt文件

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

对于wstunnel模块,与mod代理模块存在冲突,其中mod代理将剥离隧道头(https://lists.gt.net/apache/users/393509)。我通过添加到LogFormat

来更改apache_serviceproxy.conf中的LogFormat来确认这一点
c:\"%{Connection}i\" u:\"%{Upgrade}i\"

我在httpd_server_app.conf中做了同样的事情,并且在访问我的webapp之前可以看到Connection和Upgrade websocket标题被删除。

修复仅仅是为我的应用程序在/ 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服务器。

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

答案 2 :(得分:0)

除了他的答案(TL; DR)版本中提到的@adamteale之外,我还必须添加

wss

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

我的设置唯一尚未解决的问题是daphne没有处理wss连接。要么Apache没有为http终止SSL,要么正在发生其他事情。它会终止所有其他public class TimeoutBlock { private final long timeoutMilliSeconds; private long timeoutInteval=100; public TimeoutBlock(long timeoutMilliSeconds){ this.timeoutMilliSeconds=timeoutMilliSeconds; } public void addBlock(Runnable runnable) throws Throwable{ long collectIntervals=0; Thread timeoutWorker=new Thread(runnable); timeoutWorker.start(); do{ if(collectIntervals>=this.timeoutMilliSeconds){ timeoutWorker.stop(); throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated."); } collectIntervals+=timeoutInteval; Thread.sleep(timeoutInteval); }while(timeoutWorker.isAlive()); System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds."); } /** * @return the timeoutInteval */ public long getTimeoutInteval() { return timeoutInteval; } /** * @param timeoutInteval the timeoutInteval to set */ public void setTimeoutInteval(long timeoutInteval) { this.timeoutInteval = timeoutInteval; } } 请求的SSL。但是,这个问题是另一个问题。我会在完成更多研究后立即提出。

注意:由于MyCurrentRep&lt;而无法为Adam的答案添加评论50