DEFLATE preserve LZ77 sliding window

I am trying to implement my own WebSocket client. I successfully was able implement everything I need, except compression extension. I use inflate (decompressing) and deflate (compressing).

As testing setup I have create small NodeJS WebSocket server, after connection once a second, server is sending Hello string.

import ws from "ws";
import http from "http";

let server = http.createServer();
var wss = new ws.Server({ server: server, perMessageDeflate: true });

wss.on("connection", function (ws) {
  console.log("new connection");

  setInterval(() => {
    console.log("Sending Hello");
    ws.send("Hello");
  }, 1000);
});

server.listen(8000);

On rust side I correctly receive message payload and successfully inflate it:

use inflate::inflate_bytes;

let Ok(payload) = inflate_bytes(&payload) else {
    println!("Received un-inflatable message: {:?}", payload);
    return;
};

But this works correctly only for the first Hello, all following messages are throwing error at inflate.

As I understand I need to use same LZ77 sliding window that I have used for compressing/decompressing previous messages, but I cant find any ready crate for this, is there any at all ?

Also if there is such crate, is it possible to use that implementation in threads (where multiple threads at the same time decompress/compress different messages) ?

Those crates look a bit old, but they're probably fine. You are almost certainly looking for inflate::InflateStream - Rust and the equivalent to keep the state, (though it's a little more work to reliably decode, take care to read the docs!)

But as I understand this is not thread safe solution, and so to use it in multiple threads I would have to wrap this stream in Mutex and block each time I would like to compress/decompress?

If you're trying to, you're probably using it wrong anyway? You just read those bytes, and that already needed a mut handle, which ensures that you're protected (since only one mut ref can exist at a time)

Not to mention, decompressing has to happen in the same order as it was compressed, so it doesn't make sense to split it over threads.