通过入口nginx的独立WebSocket客户端失败

时间:2019-11-20 14:42:59

标签: nginx kubernetes websocket rancher nginx-ingress

在一个托管的Rancher Kubernetes集群中,我有一个公开websocket服务的服务(Spring SockJS服务器)。 借助入口规则,该服务暴露于外部:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: myIngress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600s"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600s"
    nginx.ingress.kubernetes.io/enable-access-log: "true"
spec:
  rules:
    - http:
        paths:
        - path: /app1/mySvc/
          backend:
            serviceName: mySvc
            servicePort: 80

Web应用程序通过入口Nginx连接到Web套接字服务,并且工作正常。加载的js脚本为:

    var socket = new SockJS('ws');
    stompClient = Stomp.over(socket);

    stompClient.connect({}, onConnected, onError);

相反,独立客户端(js或python)不起作用,因为它们返回了400 http错误。

例如,这是curl发送的请求和nginx的响应:

curl  --noproxy '*' --include \
     --no-buffer \
     -Lk \
--header "Sec-WebSocket-Key: l3ApADGCNFGSyFbo63yI1A==" \
--header "Sec-WebSocket-Version: 13" \
--header "Host: ingressHost" \
--header "Origin: ingressHost" \
--header "Connection: keep-alive, Upgrade" \
--header "Upgrade: websocket" \
--header "Sec-WebSocket-Extensions: permessage-deflate" \
--header "Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp" \
--header "Access-Control-Allow-Credentials: true" \
https://ingressHost/app1/mySvc/ws/websocket


HTTP/2 400
date: Wed, 20 Nov 2019 14:37:36 GMT
content-length: 34
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
access-control-allow-origin: ingressHost
access-control-allow-credentials: true
set-cookie: JSESSIONID=D0BC1540775544E34FFABA17D14C8898; Path=/; HttpOnly
strict-transport-security: max-age=15724800; includeSubDomains

Can "Upgrade" only to "WebSocket".

为什么它与浏览器一起使用而不与独立客户端一起使用?

谢谢

5 个答案:

答案 0 :(得分:1)

问题似乎不在于nginx Ingress。 JSESSIONID cookie的存在很可能表明Spring应用程序获取了请求并发送了响应。

对Spring代码的快速搜索显示,当AbstractHandshakeHandler.java(不区分大小写的匹配)时,Upgrade header isn't equal to WebSocket返回了Can "Upgrade" only to "WebSocket".

我建议在使用"Upgrade: websocket"进行呼叫时,仔细检查curl标头是否存在。

此外,它似乎是similar problem,如果应用程序具有多个控制器,则也可以在这里应用。

对于它的价值,在适当地替换了ingressHost之后,我针对问题curl和我之前实现的本地STOMP服务器尝试了相同的https://echo.websocket.org命令。它为两个工作。

您可能已经这样做了,但是您是否尝试过在浏览器中捕获网络流量以查看请求/响应交换,尤其是返回HTTP 101 Switching Protocols的请求/响应?然后尝试完全复制浏览器进行的调用,该调用成功。例如,STOMP客户端生成会话ID并使用队列/主题,该队列/主题被放置在对服务器的请求(例如/ws/329/dt1hvk2v/websocket)的URL路径中。带有curl的测试请求似乎没有它们。

答案 1 :(得分:0)

在发现traefik之前,我一直使用nginx创建入口。 您应该使用traefik进行测试。

答案 2 :(得分:0)

我在Spring应用程序中激活了跟踪,并且使用curl进行了调用:

o.s.w.s.s.s.DefaultHandshakeHandler      : Handshake failed due to invalid Upgrade header: null

因此,nginx似乎删除了Upgrade标头!

使用浏览器客户端进行测试时,标头出现在Spring Applicaton中并且可以正常工作。

答案 3 :(得分:0)

当我尝试使用http而不是https时,它可以工作。

我不明白为什么https删除独立客户端的Upgrade标头。

答案 4 :(得分:0)

我遇到了同样的问题,即标题没有转发。决定使用NGINX本身提供的NGINX ingress controller。它提供了nginx.org/websocket-services注释,可轻松实现。您可以阅读配置示例here

以下是对我来说很棘手的配置。

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
 annotations:
   kubernetes.io/ingress.class: nginx
   nginx.org/websocket-services: "blue-svc"