1

I have simple client - server websocket communication and I want to know if it's possible to enable message compression for the websockets. I am using Golang library gorila/websocket.

And there are configurations like EnableCompression bool or EnableWriteCompression(bool) method, but it does not working as expected or maybe I cannot figure out how to use it.

Expected behaviour:

I am expecting to send for example - 50kb message and to be compressed to 10-20kb or something like this. But it seems that EnableWriteCompression is not working as expected or I am not using it in the right way.

The code:

server.go:

package main

import (
    "fmt"
    "github.com/gorilla/websocket"
    "log"
    "net/http"
)

var upgrader = websocket.Upgrader{}

func socketHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    conn.EnableWriteCompression(true)
    if err != nil {
        log.Print("Error during upgrade:", err)
        return
    }
    defer conn.Close()

    n := 0
    for n <= 10 {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            log.Println("Error during message reading:", err)
            break
        }
        log.Printf("Received: %s", message)
        err = conn.WriteMessage(messageType, message)
        if err != nil {
            log.Println("Error during message writing:", err)
            break
        }
        n++
    }
}

func home(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Index Page")
}

func main() {
    http.HandleFunc("/socket", socketHandler)
    http.HandleFunc("/", home)
    log.Fatal(http.ListenAndServe("localhost:8080", nil))
}

client.go:

// client.go
package main

import (
    "log"
    "os"
    "os/signal"
    "time"

    "github.com/gorilla/websocket"
)

var done chan interface{}
var interrupt chan os.Signal

func receiveHandler(connection *websocket.Conn) {
    defer close(done)
    for {
        _, msg, err := connection.ReadMessage()
        if err != nil {
            log.Println("Error in receive:", err)
            return
        }
        log.Printf("Received: %s\n", msg)
    }
}

func main() {
    done = make(chan interface{})    // Channel to indicate that the receiverHandler is done
    interrupt = make(chan os.Signal) // Channel to listen for interrupt signal to terminate gracefully

    signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT

    socketUrl := "ws://localhost:8080" + "/socket"
    conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
    if err != nil {
        log.Fatal("Error connecting to Websocket Server:", err)
    }

    defer conn.Close()
    go receiveHandler(conn)

    // Our main loop for the client
    // We send our relevant packets here
    for {
        select {
        case <-time.After(time.Duration(1) * time.Millisecond * 1000):
            conn.EnableWriteCompression(true)
            conn.SetCompressionLevel(1)
            err := conn.WriteMessage(websocket.TextMessage, []byte("Some message to send!"))
            if err != nil {
                log.Println("Error during writing to websocket:", err)
                return
            }

        case <-interrupt:
            log.Println("Received SIGINT interrupt signal. Closing all pending connections")

            err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
            if err != nil {
                log.Println(err)
                return
            }

            select {
            case <-done:
                log.Println("Exiting....")
            case <-time.After(time.Duration(1) * time.Second):
                log.Println("Exiting....")
            }
            return
        }
    }
}

2
  • 1
    Hi, you did post some code, but you didn't indicate what you expect, and what doesn't work as you expect. Can you give more details ? Commented Jan 25, 2022 at 11:13
  • Thanks, I made a "expected behaviour" section. Commented Jan 25, 2022 at 11:31

1 Answer 1

4

As per documentation:

EnableWriteCompression enables and disables write compression of subsequent text and binary messages. This function is a noop if compression was not negotiated with the peer.

You need to setup the compression on Updater and Dialer level, so that it can be negotiated during the connection upgrade:

// for server
var upgrader = websocket.Upgrader{
    EnableCompression: true,
}

// for client
dialer := websocket.Dialer{
    Proxy:             http.ProxyFromEnvironment, // From default dialer
    HandshakeTimeout:  45 * time.Second, // From default dialer
    EnableCompression: true,
}
...
conn, _, err := dialer.Dial(socketUrl, nil)

Your example however will not show that the messages are compressed as this is handled by the library.

You can verify it using something like Wireshark:

Sec-WebSocket-Extensions: permessage-deflate ...

and on messages:

.1.. .... = Per-Message Compressed: True

You might also need to adjust the compression level to see the result you expect (max seems to be 9).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.