Repository: housleyjk/ws-rs (language: rust)

Issue:
[permessage-deflate] Failed to perform decompression: -2
```
Encountered an error: WebSocket Protocol Error: Failed to perform decompression: -2
Enable a logger to see more information.
```

And I have no idea how to enable a logger! 😢 

-----
I've set up a server with `ssl` and `permessage-deflate`. This is working excellently in "production":  proxied through `nginx` and set up with a DNS record + SSL certificate.

However, when developing locally, sending messages to the server cause it to fail decompression, and print the above error. (Note: even if I don't use the `ssl` feature, just using `permessage-deflate` causes the same issue)

-----
**To reproduce**

1. Clone https://gitlab.com/fuzz-chat/server, commit `4147d152819a0f9de4c93b6b7e45b28f1033dad1`
2. `cargo run`
3. In Chrome:
```
var ws = new WebSocket('ws://localhost:9015?name=Mitch')
```
then
```
ws.send(JSON.stringify({
	_type: "SEND_MESSAGE",
	content: "4real though, this ws-rs library is awesome, and the docs are super legit!",
	date: new Date()
}))
```


Discussion / hints:
This happened on my box just recently. It's possible that there is a linking problem with pkg-config. Or there could be changes in libz since version 1.2.8 that `libz-sys` and WS-RS expect that lead to this issue, I don't know. Anyway, the fix for me was to set the `LIBZ_SYS_STATIC=1` environment variable see [the libz-sys build script](https://github.com/alexcrichton/libz-sys/blob/master/build.rs#L27) before compiling libz-sys.

I'm sorry this took so long, but this was a real pain to figure out. Let me know if that works for you.
Oh and for setting up a logger, see most of the examples, like [client](https://github.com/housleyjk/ws-rs/blob/master/examples/client.rs#L12). 
First, when attaching `env_logger`, without setting `LIBZ_SYS_STATIC=1`:
```
2017-02-18 20:09:28.565283333 UTC [Mitch connected]
ERROR:ws::handler: WS Error <Protocol>: Failed to perform decompression: -2
2017-02-18 20:09:29.630413341 UTC [Mitch disconnected]
ERROR:ws::deflate::context: Compression stream encountered bad state.
ERROR:ws::deflate::context: Decompression stream encountered bad state.
```

So, I know that the streams encounter a bad state, which I suppose might be useful.

-----

When I set `LIBZ_SYS_STATIC=1` and did a clean build, my problem was solved. Is there a way that I can have that environment variable automatically set when I run my build? I found [Pass environment variables to dependencies](https://github.com/rust-lang/cargo/issues/97) and [Allow reexporting of features between packages](https://github.com/rust-lang/cargo/pull/712), but I'm not sure how to use the new re-exporting feature to solve this problem.

What I'd like is for people to be able to clone my project and do `cargo run`, and it works out-of-the-box. I'd rather not require them to set `LIBZ_SYS_STATIC` themselves, or have to maintain `bash`/`windows` build scripts
I understand, but I haven't figured that one out yet myself. I would like to avoid needing to set an environment variable too. What type of system are you using? I'm on Arch Linux, and I've only seen this issue on my personal box. I'm wondering if this is really a `libz-sys` issue that has to be fixed from there. I'm thinking it's essentially a bug in that build script. Alternatively, perhaps we can add a script to WS-RS that sets the environment variable so that when WS-RS is built and `libz-sys` is built as a dependency, it will just work, but I do think this is something that should be fixed in `libz-sys` if possible.
My personal box is also Arch Linux.
Interestingly, my server (which is also Arch Linux), is also failing just as consistently as my my personal machine. I update it less frequently, so perhaps some update borked it between sometime before January 18 (when I reported the issue) and now, when it is also having the same "Websocket Protocol Error".

TL;DR, I'm running Arch Linux too, neat!
This seems to be working for me, I'll reopen/investigate further if I run into this again
I'm hitting this in ws 0.7.3.
`deflate` is failing in `deflateStateCheck`. The problem is that `strm->s->state != strm`. My guess is that the `z_stream` is being moved. You have the `z_stream` as a direct member of `Compresser`, and there's no way to stop Rust moving the `Compresser` value between stream init and use, and any such movement will trigger this failure.
In my case the move occurs at `entry.insert(Connection::new(tok, sock, handler, settings, connection_id));` in `io.rs`.
This is a debug build; it may be with with optimizations enabled the bug goes away.
I'll submit a PR.

Fix patch:
diff --git a/src/deflate/context.rs b/src/deflate/context.rs
index 1cbe3c8..795e490 100644
--- a/src/deflate/context.rs
+++ b/src/deflate/context.rs
@@ -54,7 +54,9 @@ trait Context {
 }
 
 pub struct Compresser {
-    stream: ffi::z_stream,
+    // Box the z_stream to ensure it isn't moved. Moving the z_stream
+    // causes zlib to fail, because it maintains internal pointers.
+    stream: Box<ffi::z_stream>,
 }
 
 impl Compresser {
@@ -64,9 +66,9 @@ impl Compresser {
         debug_assert!(window_bits <= 15, "Received too large window size.");
 
         unsafe {
-            let mut stream = mem::zeroed();
+            let mut stream: Box<ffi::z_stream> = Box::new(mem::zeroed());
             let result = ffi::deflateInit2_(
-                &mut stream,
+                stream.as_mut(),
                 9,
                 ffi::Z_DEFLATED,
                 -window_bits as c_int,
@@ -103,7 +105,7 @@ impl Compresser {
 
     pub fn reset(&mut self) -> Result<()> {
         match unsafe {
-            ffi::deflateReset(&mut self.stream)
+            ffi::deflateReset(self.stream.as_mut())
         } {
             ffi::Z_OK => Ok(()),
             code => Err(Error::new(
@@ -115,14 +117,14 @@ impl Compresser {
 
 impl Context for Compresser {
     fn stream(&mut self) -> &mut ffi::z_stream {
-        &mut self.stream
+        self.stream.as_mut()
     }
 }
 
 impl Drop for Compresser {
     fn drop(&mut self) {
         match unsafe {
-            ffi::deflateEnd(&mut self.stream)
+            ffi::deflateEnd(self.stream.as_mut())
         } {
             ffi::Z_STREAM_ERROR => error!("Compression stream encountered bad state."),
             // Ignore discarded data error because we are raw
@@ -133,7 +135,7 @@ impl Drop for Compresser {
 }
 
 pub struct Decompresser {
-    stream: ffi::z_stream,
+    stream: Box<ffi::z_stream>,
 }
 
 impl Decompresser {
@@ -142,9 +144,9 @@ impl Decompresser {
         debug_assert!(window_bits <= 15, "Received too large window size.");
 
         unsafe {
-            let mut stream = mem::zeroed();
+            let mut stream: Box<ffi::z_stream> = Box::new(mem::zeroed());
             let result = ffi::inflateInit2_(
-                &mut stream,
+                stream.as_mut(),
                 -window_bits as c_int,
                 ZLIB_VERSION.as_ptr() as *const c_char,
                 mem::size_of::<ffi::z_stream>() as c_int,
@@ -177,7 +179,7 @@ impl Decompresser {
 
     pub fn reset(&mut self) -> Result<()> {
         match unsafe {
-            ffi::inflateReset(&mut self.stream)
+            ffi::inflateReset(self.stream.as_mut())
         } {
             ffi::Z_OK => Ok(()),
             code => Err(Error::new(
@@ -189,14 +191,14 @@ impl Decompresser {
 
 impl Context for Decompresser {
     fn stream(&mut self) -> &mut ffi::z_stream {
-        &mut self.stream
+        self.stream.as_mut()
     }
 }
 
 impl Drop for Decompresser {
     fn drop(&mut self) {
         match unsafe {
-            ffi::inflateEnd(&mut self.stream)
+            ffi::inflateEnd(self.stream.as_mut())
         } {
             ffi::Z_STREAM_ERROR => error!("Decompression stream encountered bad state."),
             ffi::Z_OK => trace!("Deallocated decompression context."),
@@ -224,13 +226,15 @@ mod test {
             let mut compressed = Vec::with_capacity(data.len());
             let mut decompressed = Vec::with_capacity(data.len());
 
-            let mut com = Compresser::new(i);
+            let com = Compresser::new(i);
+            let mut moved_com = com;
 
-            com.compress(&data, &mut compressed).expect("Failed to compress data.");
+            moved_com.compress(&data, &mut compressed).expect("Failed to compress data.");
 
-            let mut dec = Decompresser::new(i);
+            let dec = Decompresser::new(i);
+            let mut moved_dec = dec;
 
-            dec.decompress(&compressed, &mut decompressed).expect("Failed to decompress data.");
+            moved_dec.decompress(&compressed, &mut decompressed).expect("Failed to decompress data.");
 
             assert_eq!(data, &decompressed[..]);
         }


Test patch: