WebSocket/Apache错误AH01991:SSL输入过滤器阅读失败
我正在尝试使用Apache的mod_proxy_wstunnel
来在客户端网站和后端Websocket服务器之间进行代理数据,但是似乎Apache正在重置套接字。
目前,我的服务器/客户端代码非常简单:
客户端JS:
var socket = new WebSocket("wss://local-test.mysite.com/wss/", "basic-protocol");
socket.onopen = function(e) {
console.log('open: ', e);
};
socket.onerror = function(e) {
console.log('error: ', e);
};
socket.onmessage = function(e) {
console.log('message: ', e);
};
socket.onclose = function(e) {
console.log('close: ', e);
};
服务器代码(C ++)有点复杂,但实际上它可以执行以下操作(一些struct
详细信息均未出现短暂性):
int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(server, ...); // binds to port 4433
listen(server, 1024);
int client = accept(server, ...);
char buff[4096];
long r = recv(client, buffer, sizeof(buffer), 0);
这个位有效,因为我的JS加载并创建websocket
,我的服务器将获取以下HTTP数据:
GET /wss/ HTTP/1.1
Host: localhost:4433
Pragma: no-cache
Cache-Control: no-cache
User-Agent: { user agent string }
Origin: https://local-test.mysite.com
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: { cookie data here }
Sec-WebSocket-Key: {base64 key}
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: basic-protocol
X-Forwarded-For: 192.168.11.1
X-Forwarded-Host: local-test.mysite.com
X-Forwarded-Server: local-test.mysite.com
Connection: Upgrade
Upgrade: websocket
然后我将其解析为sec-websocket-key
,请附加这“魔术字符串”(“ 258EAFA5-E914-47DA-95CA-C5AB0DC85B11”
),获取SHA1 HASH,BASE64编码,然后沿以下响应发送以下响应:
HTTP/1.1 101 Switching Protocols\r\n
Connection: Upgrade\r\n
Upgrade: websocket\r\n
Sec-WebSocket-Accept: {hashed response key}\r\n
Sec-WebSocket-Protocol: basic-protocol\r\n\r\n
然后我的服务器返回到<<<<<<<<<<<<<<<<<<<<<<<<<<<<代码> recv 等待下一个数据(我会从DOM中的按钮发送),但立即将econnreset
错误时,JS WebSocket使用错误代码1006(异常终止)关闭,而我的Apache日志报告以下:
[ssl:info] (70014)End of file found: [client 192.168.11.1:54673] AH01991: SSL input filter read failed.
我不确定我是否将Apache配置设置为正确的apache配置或如果是我的服务器响应代码,但这是我的apache配置(对于相关的虚拟主机):
Listen local-test.mysite.com:80
Listen local-test.mysite.com:443
<VirtualHost local-test.mysite.com:80>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,QSA]
</VirtualHost>
<VirtualHost local-test.mysite.com:443>
ServerAdmin admin@mysite
DocumentRoot "/Code/source/dev/web"
ErrorLog /var/log/httpd/web/error.log
CustomLog /var/log/httpd/web/access.log common
ServerName local-test.mysite.com
ServerAlias local-test
SSLEngine on
SSLOptions +StrictRequire
SSLCertificateFile /etc/httpd/ssl/mysite.crt
SSLCertificateKeyFile /etc/httpd/ssl/mysite.key
<Directory /Code/source/dev/web>
RewriteEngine On
RewriteBase /
RewriteRule ^/index\.php$ - [L,NC]
RewriteCond %{REQUEST_URI} !\.(png|jpg|gif|jpeg|bmp|svg|ico)$
RewriteCond %{REQUEST_URI} !\.(css|js|map|ttf|woff|woff2|eot)$
RewriteRule . index.php [L]
</Directory>
# Websocket config
SSLProxyEngine on
ProxyPreserveHost On
ProxyRequests Off
ProxyPass /wss ws://localhost:4433 keepalive=On enablereuse=On connectiontimeout=1800
ProxyPassReverse /wss ws://localhost:4433
RewriteEngine on
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:4433%{REQUEST_URI} [P,L]
</VirtualHost>
我尝试了proxypass
和重写
规则,无济于事。
我的环境:
- Apache/2.4.51(Fedora)
- Chrome版本103.0.5060.53
I'm trying to use Apache's mod_proxy_wstunnel
to proxy data between a client website and a back end WebSocket server, but it seems like Apache is resetting the socket.
My server/client code is very simple at the moment:
Client JS:
var socket = new WebSocket("wss://local-test.mysite.com/wss/", "basic-protocol");
socket.onopen = function(e) {
console.log('open: ', e);
};
socket.onerror = function(e) {
console.log('error: ', e);
};
socket.onmessage = function(e) {
console.log('message: ', e);
};
socket.onclose = function(e) {
console.log('close: ', e);
};
The server code (C++) is a little more complex, but essentially it does the following (some struct
details left out for briefness):
int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
bind(server, ...); // binds to port 4433
listen(server, 1024);
int client = accept(server, ...);
char buff[4096];
long r = recv(client, buffer, sizeof(buffer), 0);
This bit works, as once my JS loads up and creates the WebSocket
, my server gets the following HTTP data:
GET /wss/ HTTP/1.1
Host: localhost:4433
Pragma: no-cache
Cache-Control: no-cache
User-Agent: { user agent string }
Origin: https://local-test.mysite.com
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: { cookie data here }
Sec-WebSocket-Key: {base64 key}
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: basic-protocol
X-Forwarded-For: 192.168.11.1
X-Forwarded-Host: local-test.mysite.com
X-Forwarded-Server: local-test.mysite.com
Connection: Upgrade
Upgrade: websocket
Which I then parse out the Sec-WebSocket-Key
, append the "magic string" ("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
), get the SHA1 hash, base64 encode that, and then send along the following response back:
HTTP/1.1 101 Switching Protocols\r\n
Connection: Upgrade\r\n
Upgrade: websocket\r\n
Sec-WebSocket-Accept: {hashed response key}\r\n
Sec-WebSocket-Protocol: basic-protocol\r\n\r\n
My server then goes back to recv
to wait for the next bit of data (which I'd send from a button in the DOM), but immediately errors out with a ECONNRESET
error while the JS websocket closes with error code 1006 (abnormal termination), and my Apache log reports the following:
[ssl:info] (70014)End of file found: [client 192.168.11.1:54673] AH01991: SSL input filter read failed.
I'm not sure if I've got my Apache config set correctly to proxy data or if it's my server response code, but here's my Apache config (for the relevant virtual host):
Listen local-test.mysite.com:80
Listen local-test.mysite.com:443
<VirtualHost local-test.mysite.com:80>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,QSA]
</VirtualHost>
<VirtualHost local-test.mysite.com:443>
ServerAdmin admin@mysite
DocumentRoot "/Code/source/dev/web"
ErrorLog /var/log/httpd/web/error.log
CustomLog /var/log/httpd/web/access.log common
ServerName local-test.mysite.com
ServerAlias local-test
SSLEngine on
SSLOptions +StrictRequire
SSLCertificateFile /etc/httpd/ssl/mysite.crt
SSLCertificateKeyFile /etc/httpd/ssl/mysite.key
<Directory /Code/source/dev/web>
RewriteEngine On
RewriteBase /
RewriteRule ^/index\.php$ - [L,NC]
RewriteCond %{REQUEST_URI} !\.(png|jpg|gif|jpeg|bmp|svg|ico)$
RewriteCond %{REQUEST_URI} !\.(css|js|map|ttf|woff|woff2|eot)$
RewriteRule . index.php [L]
</Directory>
# Websocket config
SSLProxyEngine on
ProxyPreserveHost On
ProxyRequests Off
ProxyPass /wss ws://localhost:4433 keepalive=On enablereuse=On connectiontimeout=1800
ProxyPassReverse /wss ws://localhost:4433
RewriteEngine on
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:4433%{REQUEST_URI} [P,L]
</VirtualHost>
I've tried all sorts of variations for the ProxyPass
and Rewrite
rules, to no avail.
My environment:
- Apache/2.4.51 (Fedora)
- Chrome Version 103.0.5060.53
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
事实证明,这实际上是我的服务器回复,而不是 我的apache配置:
首先,我的base64编码字符串是“字符串”的sha1 hash而不是二进制版本(例如
b1feae)。 ..
而不是0xb1 0xfe 0xae ...
),其次,我没有明确返回sec-websocket-protocol
在某些情况下(在Chrome做到并将关闭连接时,Firefox似乎在我的某些测试中不在乎[Firefox])。As it turns out, it was actually my server reply and not my Apache config:
First, my base64 encoded string was of the "stringized" SHA1 hash instead of the binary version (e.g.
b1feae...
instead of0xb1 0xfe 0xae ...
), and secondly I wasn't explicitly returning theSec-WebSocket-Protocol
in certain instances (to which FireFox didn't seem to care about in some of my tests while Chrome did and would close the connection [as it should]).