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
An ACK managed cluster is created. For more information, see Create an ACK managed cluster.
A Service Mesh (ASM) instance is created. The ACK cluster is added to the ASM instance. For more information, see Create an ASM instance and Add a cluster to an ASM instance.
- At least one ingress gateway service is deployed. For more information, see Create an ingress gateway.
An application is deployed in the ASM instance. For more information, see Deploy an application in an ASM instance.
- Use kubectl to connect to the ACK cluster and to the ASM instance. For more information, see Connect to ACK clusters by using kubectl and Use kubectl on the control plane to access Istio resources.
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
- 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
- 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
- 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" } }
- 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 });
- 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
Log on to the ASM console.
In the left-side navigation pane, choose .
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.
- Create a gateway.
On the details page of the ASM instance, choose in the left-side navigation pane. On the page that appears, click Create from YAML.
- 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
- Create a destination rule.
On the details page of the ASM instance, choose in the left-side navigation pane. On the page that appears, click Create from YAML.
- 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
- Create a virtual service.
On the details page of the ASM instance, choose in the left-side navigation pane. On the page that appears, click Create from YAML.
- 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
- 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>
- Open the client.html file by using Google Chrome and press F12 on the keyboard to open Developer Tools.
- 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.
Method 2: Access the application on the WebSocket server by using an Envoy filter
- 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
- Bind the Envoy filter to the specified workload or namespace. For more information, see Bind an Envoy filter template to a workload or namespace.
- Open the client.html file by using Google Chrome and press F12 on the keyboard to open Developer Tools.
- 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.