我已经成功了。
Scenario
------------- ---------------- ----------
| Browser |<----->| Apache httpd |<----->| Tomcat |
| | SSL | 2.4.9 | SSL | 7.0.52 |
------------- ---------------- ----------
浏览器WebSocket通过Apache httpd,反向代理到Tomcat中的Web应用程序。所有 SSL 从前到后。
以下是每个部分的配置:
浏览器客户端
请注意 url 中的尾随“/”:wss://host/app/ws/
。有必要匹配正确的 wss ProxyPass 指令(在 Apache 配置部分中进一步显示)并防止 301 重定向到https://host/app/ws
。也就是说,它使用 https 方案进行重定向,而不是后端的 wss 方案。
Test Page
<!doctype html>
<body>
<script type="text/javascript">
var connection = new WebSocket("wss://host/app/ws/");
connection.onopen = function () {
console.log("connected");
};
connection.onclose = function () {
console.log("onclose");
};
connection.onerror = function (error) {
console.log(error);
};
</script>
</body>
</html>
阿帕奇httpd
我正在使用 Apache httpd 2.4.9,它提供了开箱即用的功能mod_proxy_wstunnel。但是,那mod_proxy_wstunnel.so使用 wss:// 方案时提供的不支持 SSL。它最终尝试以明文方式连接到后端(Tomcat),这导致 SSL 握手失败。查看错误here https://issues.apache.org/bugzilla/show_bug.cgi?id=55320。所以,你必须打补丁mod_proxy_wstunnel.c您自己按照错误报告中的建议更正进行操作。这是一个简单的 3 行更改。
Suggested correction,
314a315
> int is_ssl = 0;
320a322
> is_ssl = 1;
344c346
< backend->is_ssl = 0;
---
> backend->is_ssl = is_ssl;
然后重建模块并替换为新的mod_proxy_wstunnel.so与旧的。
构建 Apache httpd
这是我用来构建我想要的模块的 (2.4.9) 命令。您可能不需要全部。
./configure --prefix=/usr/local/apache --with-included-apr --enable-alias=shared
--enable-authz_host=shared --enable-authz_user=shared
--enable-deflate=shared --enable-negotiation=shared
--enable-proxy=shared --enable-ssl=shared --enable-reqtimeout=shared
--enable-status=shared --enable-auth_basic=shared
--enable-dir=shared --enable-authn_file=shared
--enable-autoindex=shared --enable-env=shared --enable-php5=shared
--enable-authz_default=shared --enable-cgi=shared
--enable-setenvif=shared --enable-authz_groupfile=shared
--enable-mime=shared --enable-proxy_http=shared
--enable-proxy_wstunnel=shared
注意最后一个开关:--enable-proxy_wstunnel=shared
起初,我错误地使用了--enable-proxy-wstunnel=shared
,这似乎构建得很好,但当我使用生成的 .so 文件时最终无法工作。看到不同?您要确保在中使用下划线"proxy_wstunnel"
而不是破折号。
Apache httpd 配置
httpd.conf
...
LoadModule proxy_module modules/mod_proxy.so
...
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
...
LoadModule ssl_module modules/mod_ssl.so
...
Include conf/extra/httpd-ssl.conf
...
LogLevel debug
ProxyRequests off
# Note, this is the preferred ProxyPass configuration, and *should* be equivalent
# to the same inline version below, but it does NOT WORK!
#<Location /app/ws/>
# ProxyPass wss://localhost:8443/app/ws
# ProxyPassReverse wss://localhost:8443/app/ws
#</Location>
#<Location /app/>
# ProxyPass https://localhost:8443/app/
# ProxyPassReverse https://localhost:8443/app/
#</Location>
# NOTE: Pay strict attention to the slashes "/" or lack thereof!
# WebSocket url endpoint
ProxyPass /app/ws/ wss://localhost:8443/app/ws
ProxyPassReverse /app/ws/ wss://localhost:8443/app/ws
# Everything else
ProxyPass /app/ https://localhost:8443/app/
ProxyPassReverse /app/ https://localhost:8443/app/
如果您在上面的配置中没有看到我的注释,那么这里又是:请严格注意斜杠“/”或缺少斜杠!
另外,如果您在 apache 日志中看到调试日志语句,表明已建立 wss 连接然后关闭,则可能像我一样启用了 mod_reqtimeout,因此请确保它未加载:
#LoadModule reqtimeout_module modules/mod_reqtimeout.so
Tomcat
假设您的 HTTP 连接器设置正确,则在 tomcat 中无需进行太多配置。虽然为了帮助调试,我发现创建一个很有用$CATALINA_HOME/bin/setenv.sh
看起来像这样:
setenv.sh
CATALINA_OPTS=$CATALINA_OPTS" -Djavax.net.debug=all -Djavax.net.debug=ssl:handshake:verbose"
这使我能够查看我修改的 mod_proxy_wstunnel.so 是否适用于 wss://。当它不工作时,我的 catalina.out 日志文件将显示:
javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
http-nio-8443-exec-1, SEND TLSv1 ALERT: fatal, description = internal_error
http-nio-8443-exec-1, WRITE: TLSv1 Alert, length = 2
http-nio-8443-exec-1, called closeOutbound()
http-nio-8443-exec-1, closeOutboundInternal()
最后的想法
虽然我使用的是 Apache httpd 2.4.9,我已经看到 mod_proxy_wstunnel 的向后移植可以应用于版本 2.2.x http://www.amoss.me.uk/2013/06/apache-2-2-websocket-proxying-ubuntu-mod_proxy_wstunnel/。希望我上面的笔记可以应用于那些旧版本。