IEnumerable <WebSocketsFrame> IExtension.ApplyOutgoing(NetContext context, WebSocketConnection connection, WebSocketsFrame frame) { if (!frame.IsControlFrame && frame.PayloadLength > parent.compressMessagesLargerThanBytes) { if (frame.Reserved1) { throw new InvalidOperationException("Reserved1 flag is already set; extension conflict?"); } int headerBytes = 0; if (outbound == null) { outbound = new ZStream(); const int BITS = 12; // 4096 byte outbound buffer (instead of full 32k=15) outbound.deflateInit(zlibConst.Z_BEST_COMPRESSION, BITS); headerBytes = 2; } BufferStream tmp = null; var payload = frame.Payload; payload.Position = 0; byte[] inBuffer = null, outBuffer = null; try { inBuffer = context.GetBuffer(); outBuffer = context.GetBuffer(); tmp = new BufferStream(context, 0); outbound.next_out = outBuffer; outbound.next_in = inBuffer; int remaining = frame.PayloadLength; while (remaining > 0) { int readCount = payload.Read(inBuffer, 0, inBuffer.Length); if (readCount <= 0) { break; } remaining -= readCount; outbound.next_in_index = 0; outbound.avail_in = readCount; do { outbound.next_out_index = 0; outbound.avail_out = outBuffer.Length; long priorOut = outbound.total_out; int err = outbound.deflate(remaining == 0 ? zlibConst.Z_SYNC_FLUSH : zlibConst.Z_NO_FLUSH); if (err != zlibConst.Z_OK && err != zlibConst.Z_STREAM_END) { throw new ZStreamException("deflating: " + outbound.msg); } int outCount = (int)(outbound.total_out - priorOut); if (outCount > 0) { if (headerBytes == 0) { tmp.Write(outBuffer, 0, outCount); } else { if (outCount < headerBytes) { throw new InvalidOperationException("Failed to write entire header"); } // check the generated header meets our expectations // CMF is very specific - CM must be 8, and CINFO must be <=7 (for 32k window) if ((outBuffer[0] & 15) != 8) { throw new InvalidOperationException("Zlib CM header was incorrect"); } if ((outBuffer[0] & 128) != 0) // if msb set, is > 7 - invalid { throw new InvalidOperationException("Zlib CINFO header was incorrect"); } // FLG is less important; FCHECK is irrelevent, FLEVEL doesn't matter; but // FDICT must be zero, to ensure that we aren't expecting a an initialization dictionary if ((outBuffer[1] & 32) != 0) { throw new InvalidOperationException("Zlib FLG.FDICT header was set (must not be)"); } // skip the header, and write anything else outCount -= headerBytes; if (outCount > 0) { tmp.Write(outBuffer, headerBytes, outCount); } headerBytes = 0; // all written now } } } while (outbound.avail_in > 0 || outbound.avail_out == 0); } if (remaining != 0) { throw new EndOfStreamException(); } if (headerBytes != 0) { throw new InvalidOperationException("Zlib header was not written"); } // verify the last 4 bytes, then drop them tmp.Position = tmp.Length - 4; NetContext.Fill(tmp, outBuffer, 4); if (!(outBuffer[0] == 0x00 && outBuffer[1] == 0x00 && outBuffer[2] == 0xFF && outBuffer[3] == 0xFF)) { throw new InvalidOperationException("expectation failed: 0000FFFF in the tail"); } if (parent.disableContextTakeover && tmp.Length >= frame.PayloadLength) { // compressing it didn't do anything useful; since we're going to discard // the compression context, we might as well stick with the original data payload.Position = 0; payload = null; // so that it doesn't get disposed } else { // set our final output tmp.Position = 0; tmp.SetLength(tmp.Length - 4); long bytesSaved = frame.PayloadLength - tmp.Length; frame.Payload = tmp; frame.PayloadLength = (int)tmp.Length; frame.Reserved1 = true; tmp = payload as BufferStream; parent.RegisterOutboundBytesSaved(bytesSaved); } } #if DEBUG catch (Exception ex) { Debug.WriteLine(ex); throw; } #endif finally { if (outbound != null) { outbound.next_out = null; outbound.next_in = null; } if (tmp != null) { tmp.Dispose(); } if (inBuffer != null) { context.Recycle(inBuffer); } if (outBuffer != null) { context.Recycle(outBuffer); } if (parent.disableContextTakeover) { ClearContext(false, true); } } } yield return(frame); }