【问题标题】:Standalone WebSocket client throught Ingress nginx fails通过 Ingress nginx 的独立 WebSocket 客户端失败
【发布时间】:2020-11-04 20:22:50
【问题描述】:

在 Hosted 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".

为什么它适用于浏览器而不是独立客户端?

谢谢

【问题讨论】:

    标签: nginx kubernetes websocket rancher nginx-ingress


    【解决方案1】:

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

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

    我建议在使用curl 拨打电话时仔细检查"Upgrade: websocket" 标头是否存在。

    此外,这似乎是一个similar problem,如果应用程序有多个控制器,也可能适用于此。

    对于它的价值,在适当地替换 ingressHost 之后,我尝试了与问题中针对 https://echo.websocket.org 和我前段时间实施的本地 STOMP 服务器相同的 curl 命令。它对两者都有效。

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

    【讨论】:

      【解决方案2】:

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

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

      所以,似乎 nginx 删除了 Upgrade 标头!

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

      【讨论】:

        【解决方案3】:

        我遇到了同样的问题,标题没有被转发。决定使用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"
        

        【讨论】:

          【解决方案4】:

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

          【讨论】:

            【解决方案5】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-04-04
              • 2020-07-24
              • 1970-01-01
              • 1970-01-01
              • 2014-03-17
              • 1970-01-01
              • 2017-10-02
              相关资源
              最近更新 更多