Websocket握手与HAProxy挂起

Websocket握手与HAProxy挂起,websocket,haproxy,sockjs,Websocket,Haproxy,Sockjs,使用最新的Chrome浏览器,我尝试使用sockjs客户端与haproxy后面的后端服务器通信。在我的本地主机上(中间没有haproxy),工作正常-客户端可以使用websocket协议连接、发送和接收消息。例如: conn.onopen = function() { if (conn.readyState === SockJS.OPEN) { conn.send("hello server"); console.log("msg sent");

使用最新的Chrome浏览器,我尝试使用
sockjs
客户端与
haproxy
后面的后端服务器通信。在我的本地主机上(中间没有
haproxy
),工作正常-客户端可以使用
websocket
协议连接、发送和接收消息。例如:

conn.onopen = function() {
    if (conn.readyState === SockJS.OPEN) {
        conn.send("hello server");
        console.log("msg sent");
    }
};
一旦我用HAProxy将它部署到服务器上,奇怪的事情发生了,
sockjs
认为连接是打开的(比如在
conn.readyState==sockjs.open
中,'msg sent'出现在控制台日志中),然而,
websocket
握手只是挂起,服务器从未接收到msg。下面是我在haproxy日志中看到的内容:

Oct 23 09:08:25 localhost.localdomain haproxy[14121]: 129.xx.xxx.105:55000 [23/Oct/2012:09:08:24.459] public www/content 777/0/0/1/778 200 375 - - ---- 3/3/0/1/0 0/0 "GET /sockjs/info HTTP/1.1"
Oct 23 09:10:54 localhost.localdomain haproxy[14121]: 129.xx.xxx.105:55015 [23/Oct/2012:09:08:25.398] public www/content 0/0/0/1/149017 101 147 - - CD-- 4/4/0/0/0 0/0 "GET /sockjs/478/kyi342s8/websocket HTTP/1.1"
请注意,第二个日志msg仅在我关闭haproxy后面的后端服务器时出现。在关闭之前,日志中没有错误,但是websocket握手没有完成,服务器没有收到任何消息

使用Chrome的开发者工具,在网络选项卡中,我看到以下内容:

Request URL:ws://www.mysite.com/sockjs/478/kyi342s8/websocket
Request Method:GET
Status Code:101 Switching Protocols

Request Headers
Connection:Upgrade
Host:www.mysite.com
Origin:http://www.mysite.com
Sec-WebSocket-Extensions:x-webkit-deflate-frame
Sec-WebSocket-Key:TFEIKYhlqWWBZKlXzXAuWQ==
Sec-WebSocket-Version:13
Upgrade:websocket
(Key3):00:00:00:00:00:00:00:00

Response Headers
Connection:Upgrade
Sec-WebSocket-Accept:D+s3va02KH6QTso24ywcdxcfDgM=
Upgrade:websocket
(Challenge Response):00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00
websocket
对象的“Type”和“Time Latency”在开发人员工具的“network”选项卡下都显示“Pending”

最后,这是我的haproxy配置(版本1.4.22):

有人知道为什么会这样吗?这是由于一些haproxyconfig,甚至是服务器的一些常规网络设置(例如
iptables
)造成的吗


另外,我曾尝试在
haproxy
中启用
http假装keepalive
,但它不起作用。

我已经遇到过这种情况,我不记得它是什么服务器,但它并不完全符合WebSocket规范。事实上,它失败了,因为它在连接头中找到了“close”标记以及“升级”令牌,而规范说需要“升级”令牌,并且(幸运的是)不建议拒绝其他令牌


从理论上讲,如果您如您所评论的那样使用“option http假装保持活动”,它应该可以工作。至少对我来说是这样。但这里可能有一个不同的问题。

最有可能的情况是,您的网络中有透明的防火墙,它会破坏连接到端口80的websocket连接

要验证这种情况,请执行以下操作:

  • 让haproxy在不同的端口上侦听,并尝试它是否开始工作
  • 尝试在网络之外访问您的服务
  • 如果它开始工作,那么你的网络就有问题了

    不幸的是,在这种情况下,SockJS不会使用回退传输,因为客户端认为它已连接,但连接并未真正建立


    可能的解决方案是:让haproxy在两个端口上侦听,在端口80上侦听Web流量,在另一个端口侦听SockJS流量。这保证了透明的HTTP代理不会干扰您的websocket连接。

    就像@Joes所说的,这是行为不正常的防火墙造成的。有人说,例如FortiGate导致了这一问题

    更多讨论:

    通过SSL提供SockJS似乎可以解决这个问题

    global
            log 127.0.0.1   local1 info
            log 127.0.0.1   local1 notice
            #log loghost    local0 info
            maxconn 4096
            chroot /usr/share/haproxy
            uid 99
            gid 99
            daemon
            #debug
            #quiet
    
    defaults
            log             global
            mode            http
            option          httplog
            option          dontlognull
            retries         3
            option          redispatch
            maxconn         500
            timeout connect 6s
    
    frontend public
            mode    http
            bind    *:80
            timeout client  300s
            option  http-server-close
            #option         http-pretend-keepalive
            # define ACLs
            acl host_static hdr_beg(host) -i static. data.
            acl host_www hdr_beg(host) -i www.
            acl url_static path_end .ico .txt .pdf .png .jpg .css .js .csv
            acl is_stats path_beg /haproxy/stats
            # define rules
            use_backend nginx if host_static or host_www url_static
            use_backend stats if is_stats
            default_backend www
    
    backend nginx
            timeout server 20s
            server nginx 127.0.0.1:8484
    
    backend stats
            stats enable
            stats uri /haproxy/stats
    
    backend www
            timeout server 300s
            option forwardfor
            #no option httpclose
            option http-server-close
            server sockcontent 127.0.0.1:8080