envoy websockets per route configuration, JavaScript test setup

In my article yesterday I discussed the configuration & test of envoy for proxying websockets at length.

Today I would like to add some additional information.

correct syntax for websocket upgrade per route

You can enable websocket upgrade per route. No need to add it globally:

– name: envoy.http_connection_manager
    config:
      #upgrade_configs:
      #  – upgrade_type: websocket
      stat_prefix: ingress_https
      use_remote_address: true
      xff_num_trusted_hops: 0
      route_config:
        virtual_hosts:
        – name: picockpit_withport
          domains: [“picockpit.local:443”]
          routes:
            – match: { prefix: “/mqtt/webclient” }
              route:
                cluster: target_dwebsocket
                upgrade_configs:
                  – upgrade_type: “websocket”
                    enabled: true
            – match: {prefix: “/”}
              route: {cluster: target_picockpit}

Please note that I decided to mirror the configuration including the port, and without – just in case:

– name: picockpit
   domains: [“picockpit.local”]
   routes:
     – match: { prefix: “/mqtt/webclient” }
       route:
         cluster: target_dwebsocket
         upgrade_configs:
           – upgrade_type: “websocket”
             enabled: true
     – match: {prefix: “/”}
       route: {cluster: target_picockpit}

Please see below (Update 3) for combining both in a single stanza, which is more elegant, and Update 2 for my doubts about the port notation being required for the JavaScript client.

Enabled globally by default?

Please note that the envoy documentation seems to imply that websocket upgrades are enabled by default globally. I have not tested this.

Maybe you only need to specify the domain including the port, as discussed in my article, for websocket connections to work.

Javascript test

A simple JavaScript test client would look like this:

function connect(){
     console.log(“Building up websocket”);
     var socket = new WebSocket(“wss://key:[email protected]/mqtt/webclient”);
     socket.onerror = function(event){
         console.log(“an error has been encountered”);
         console.log(event);
     }

    socket.onopen = function(){
         console.log(“Socket has been opened.”);
         console.log(‘Socket Status: ‘+ socket.readyState);
         socket.send(“Hello server!”);
     }
     socket.onmessage = function(msg){
         console.log(“received message”);
         console.log(msg);
     }
     console.log(“Finished with first websocket”);
}

connect();

In combination with my other post, and the simple websocket echo client, you would get a “Hello server!” back as a message:

image

Update 2

It seems that the JavaScript client is not using the domain:port authority syntax. Therefore the first stanza is probably not needed

image

This should be enough, then:

– name: picockpit
  domains: [“picockpit.local”]
  routes:
    – match: { prefix: “/mqtt/webclient” }
      route:
        cluster: target_dwebsocket
        upgrade_configs:
          – upgrade_type: “websocket”
            enabled: true
     – match: {prefix: “/”}
       route: {cluster: target_picockpit}

Update 3

The simplest way is to probably just match for both domain variations and have the same code:

– name: envoy.http_connection_manager
   config:
     #upgrade_configs:
     #  – upgrade_type: websocket
     stat_prefix: ingress_https
     use_remote_address: true
     xff_num_trusted_hops: 0
     route_config:
       virtual_hosts:
       – name: picockpit_withport
        domains: [“picockpit.local:443”, “picockpit.local”]
         routes:
           #- match: { prefix: “/mqtt/webclient” }
           – match: { prefix: “/mqtt” }
             route:
               cluster: target_dwebsocket
               upgrade_configs:
                 – upgrade_type: “websocket”
                   enabled: true
           – match: {prefix: “/”}
             route: {cluster: target_picockpit}