All Products
Search
Document Center

Alibaba Cloud Service Mesh:Resolve the issue of an unexpected return code when WebSocket is disabled

Last Updated:Oct 24, 2023

When you use Google Chrome with WebSocket disabled to access an application that is deployed in a namespace with Istio sidecar injection enabled, the response is "wasclean": false and the return code is 1006, which is inconsistent with the default or custom return code. However, this situation does not occur if Firefox or Safari is used. This topic describes how to use an Envoy filter to resolve the issue.

Prerequisites

Step 1: Deploy a sample application

You can use an Alibaba Cloud image or build a Docker image to deploy the application. Perform the following operations:

Method 1: Use an Alibaba Cloud image to deploy an application

  1. Create a sample.yaml file that contains the following content:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: websocket-test
      namespace: default
      labels:
        app: websocket-test
        version: current
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: websocket-test
          version: current
      template:
        metadata:
          labels:
            app: websocket-test
            version: current
        spec:
          containers:
          - name: websocket-test
            image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/asm-websocketsample:v1
            imagePullPolicy: Always
            command: ["node", "ws.js"]
    
    
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: websocket-test
      name: websocket-test
      namespace: default
    spec:
      ports:
      - name: http
        port: 80
        protocol: TCP
        targetPort: 9900
      selector:
        app: websocket-test
      type: ClusterIP
  2. Run the following command to deploy the application on the WebSocket server in the default namespace:
    You must enable automatic sidecar injection for the default namespace. For more information, see Enable automatic sidecar proxy injection.
    kubectl apply -f sample.yaml

Method 2: Build a Docker image to deploy the application

  1. Create a package.json file that contains the following content. The Node.js application is used in the example.
    {
      "name": "wssample",
      "version": "0.0.1",
      "main": "ws.js",
      "license": "UNLICENSED",
      "scripts": {
        "start": "node --trace-warnings ./ws.js"
      },
      "dependencies": {
        "ws": "^8.0.0"
      }
    }
  2. Run the following code to deploy the application on the WebSocket server.
    const WebSocket = require("ws");
    const http = require("http");
    const wss = new WebSocket.Server({ noServer: true });
    
    const server = http.createServer()
    
    
    server.on("upgrade", async (request, socket, head) => {
      const handleAuth = (ws) => {
        wss.emit("connection", ws, request);
      };
      wss.handleUpgrade(request, socket, head, handleAuth);
    })
    
    wss.on("connection", (conn, req) => {
      // By default, 1005 is returned. If sidecar injection is enabled, 1006 is returned. 
      // conn.close()
      // The custom return code is 4321. 
      // However, if sidecar injection is enabled, 1006 is returned. 
      // If sidecar injection is not enabled, 4321 is returned. 
      conn.close(4321, "test")
    
    });
    
    server.listen({ host: '0.0.0.0', port: 9900 });
    
                            
  3. Build a Docker image file that contains the following content:
    FROM node:16.7.0-alpine3.14
    WORKDIR /root/app
    COPY . .
    RUN yarn install

Step 2: Configure the ASM instance

  1. Log on to the ASM console.

  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column.

  4. Create a gateway.
    1. On the details page of the ASM instance, choose ASM Gateways > Gateway in the left-side navigation pane. On the page that appears, click Create from YAML.

    2. Set Namespace to default, select a scenario template, paste the following content into the YAML editor box, and then click Create.
      apiVersion: networking.istio.io/v1beta1
      kind: Gateway
      metadata:
        name: websocket-test
        namespace: default
      spec:
        selector:
          istio: ingressgateway
        servers:
          - hosts:
              - '*'
            port:
              name: http
              number: 80
              protocol: HTTP
  5. Create a destination rule.
    1. On the details page of the ASM instance, choose Traffic Management Center > DestinationRule in the left-side navigation pane. On the page that appears, click Create from YAML.

    2. Set Namespace to default, select a scenario template, paste the following content into the YAML editor box, and then click Create.
      apiVersion: networking.istio.io/v1alpha3
      kind: DestinationRule
      metadata:
        name: websocket-test
        namespace: default
      spec:
        host: websocket-test
        subsets:
        - name: current
          labels:
            version: current
  6. Create a virtual service.
    1. On the details page of the ASM instance, choose Traffic Management Center > VirtualService in the left-side navigation pane. On the page that appears, click Create from YAML.

    2. Set Namespace to default, select a scenario template, paste the following content into the YAML editor box, and then click Create.
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: websocket-test
        namespace: default
      spec:
        gateways:
        - websocket-test
        hosts:
        - '*'
        http:
        - name: default
          route:
          - destination:
              host: websocket-test
              subset: current

Step 3: Access the application on the WebSocket server

You can access the application on the WebSocket server directly or by using an Envoy filter. This topic compares the two methods. If the access succeeds, the custom code 4321 is returned in this example.

Method 1: Access the application on the WebSocket server directly

  1. Create a client.html file that contains the following content:
    Replace the IP address of the ingress gateway in the sample YAML file with the IP address of the ingress gateway that you use.
    <!DOCTYPE html>
    <html>
    
    <head>
        <title>WebSocket example</title>
    
    </head>
    
    <body>
    
        <script>
            var ws=new WebSocket('ws://{IP address of the ingress gateway}');
    
            ws.onopen = function (ev) {
                console.log(ev)
            };
            ws.onmessage = function (ev) {
                console.log("on msg", ev)
            };
            ws.onclose = function (ev) {
                console.log("on close", ev)
            };
            ws.onerror = function (ev) {
                console.log("on error", ev)
            };
    
        </script>
    </body>
    
    </html>
  2. Open the client.html file by using Google Chrome and press F12 on the keyboard to open Developer Tools.
  3. Refresh the page. On the Console tab, view the return code.
    As shown in the following figure, the return code is 1006, instead of the custom return code 4321. 1006

Method 2: Access the application on the WebSocket server by using an Envoy filter

  1. Create an Envoy filter that contains the following content:
    apiVersion: networking.istio.io/v1alpha3
    kind: EnvoyFilter
    metadata:
      labels:
        asm-system: 'true'
        provider: asm
      name: hack-to-fix-delayedclosetimeout-istio-upper-version
      namespace: istio-system
    spec:
      configPatches:
        - applyTo: NETWORK_FILTER
          match:
            listener:
              filterChain:
                filter:
                  name: envoy.filters.network.http_connection_manager
            proxy:
              proxyVersion: ^1.*.*
          patch:
            operation: MERGE
            value:
              typed_config:
                '@type': >-
                  type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                delayed_close_timeout: 0s
                    
  2. Bind the Envoy filter to the specified workload or namespace. For more information, see Bind an Envoy filter template to a workload or namespace.
  3. Open the client.html file by using Google Chrome and press F12 on the keyboard to open Developer Tools.
  4. Refresh the page. On the Console tab, view the return code.
    As shown in the following figure, the return code is 4321, which meets your expectations. Therefore, you can use the Envoy filter to resolve the issue of inconsistent return codes. 4321