From 7ab5ec5f74b97635de235b99c490a8854edecd33 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 22 Feb 2011 18:05:16 +0900 Subject: [PATCH] NETTY-383 deflate-raw support for HttpContentDecompressor * Added ZlibWrapperType.ZLIB_OR_NONE for auto-detection and updated the relevant Zlib implementation --- .../codec/compression/ZlibDecoder.java | 23 ++++++++++++------- .../codec/compression/ZlibEncoder.java | 5 ++++ .../handler/codec/compression/ZlibUtil.java | 3 +++ .../codec/compression/ZlibWrapper.java | 7 +++++- .../codec/http/HttpContentDecompressor.java | 3 ++- .../netty/util/internal/jzlib/Deflate.java | 5 ++++ .../netty/util/internal/jzlib/Inflate.java | 22 +++++++++++++++++- .../netty/util/internal/jzlib/JZlib.java | 3 ++- 8 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibDecoder.java b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibDecoder.java index 44585ed1d1..8dc7eb94c5 100644 --- a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibDecoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibDecoder.java @@ -125,25 +125,32 @@ public class ZlibDecoder extends OneToOneDecoder { z.next_out_index = 0; z.avail_out = out.length; - do { + + loop: for (;;) { // Decompress 'in' into 'out' int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH); + if (z.next_out_index > 0) { + decompressed.writeBytes(out, 0, z.next_out_index); + z.avail_out = out.length; + } + z.next_out_index = 0; + switch (resultCode) { case JZlib.Z_STREAM_END: + finished = true; // Do not decode anymore. + z.inflateEnd(); + break loop; case JZlib.Z_OK: + break; case JZlib.Z_BUF_ERROR: - decompressed.writeBytes(out, 0, z.next_out_index); - z.next_out_index = 0; - z.avail_out = out.length; - if (resultCode == JZlib.Z_STREAM_END) { - finished = true; // Do not decode anymore. - z.inflateEnd(); + if (z.avail_in <= 0) { + break loop; } break; default: ZlibUtil.fail(z, "decompression failure", resultCode); } - } while (z.avail_in > 0); + } if (decompressed.writerIndex() != 0) { // readerIndex is always 0 return decompressed; diff --git a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibEncoder.java b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibEncoder.java index e052feef14..c4da15c4d8 100644 --- a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibEncoder.java +++ b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibEncoder.java @@ -104,6 +104,11 @@ public class ZlibEncoder extends OneToOneEncoder implements LifeCycleAwareChanne if (wrapper == null) { throw new NullPointerException("wrapper"); } + if (wrapper == ZlibWrapper.ZLIB_OR_NONE) { + throw new IllegalArgumentException( + "wrapper '" + ZlibWrapper.ZLIB_OR_NONE + "' is not " + + "allowed for compression."); + } synchronized (z) { int resultCode = z.deflateInit(compressionLevel, ZlibUtil.convertWrapperType(wrapper)); diff --git a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibUtil.java b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibUtil.java index 525a0f6dc8..d456c678d3 100644 --- a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibUtil.java +++ b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibUtil.java @@ -48,6 +48,9 @@ final class ZlibUtil { case GZIP: convertedWrapperType = JZlib.W_GZIP; break; + case ZLIB_OR_NONE: + convertedWrapperType = JZlib.W_ZLIB_OR_NONE; + break; default: throw new Error(); } diff --git a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibWrapper.java b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibWrapper.java index 3bdbf71256..ac598ed7dd 100644 --- a/src/main/java/org/jboss/netty/handler/codec/compression/ZlibWrapper.java +++ b/src/main/java/org/jboss/netty/handler/codec/compression/ZlibWrapper.java @@ -35,5 +35,10 @@ public enum ZlibWrapper { /** * Raw DEFLATE stream only (no header and no footer). */ - NONE; + NONE, + /** + * Try {@link #ZLIB} first and then {@link #NONE} if the first attempt fails. + * Please note that you can specify this wrapper type only when decompressing. + */ + ZLIB_OR_NONE; } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecompressor.java b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecompressor.java index 63a9c6cdd9..88e38c68a3 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecompressor.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/HttpContentDecompressor.java @@ -35,7 +35,8 @@ public class HttpContentDecompressor extends HttpContentDecoder { if ("gzip".equalsIgnoreCase(contentEncoding) || "x-gzip".equalsIgnoreCase(contentEncoding)) { return new DecoderEmbedder(new ZlibDecoder(ZlibWrapper.GZIP)); } else if ("deflate".equalsIgnoreCase(contentEncoding) || "x-deflate".equalsIgnoreCase(contentEncoding)) { - return new DecoderEmbedder(new ZlibDecoder(ZlibWrapper.ZLIB)); + // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. + return new DecoderEmbedder(new ZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); } // 'identity' or unsupported diff --git a/src/main/java/org/jboss/netty/util/internal/jzlib/Deflate.java b/src/main/java/org/jboss/netty/util/internal/jzlib/Deflate.java index a3461a244a..ac7c3336f8 100644 --- a/src/main/java/org/jboss/netty/util/internal/jzlib/Deflate.java +++ b/src/main/java/org/jboss/netty/util/internal/jzlib/Deflate.java @@ -1306,6 +1306,11 @@ final class Deflate { private int deflateInit2(ZStream strm, int level, int method, int windowBits, int memLevel, int strategy, WrapperType wrapperType) { + + if (wrapperType == WrapperType.ZLIB_OR_NONE) { + throw new IllegalArgumentException("ZLIB_OR_NONE allowed only for inflate"); + } + // byte[] my_version=ZLIB_VERSION; // diff --git a/src/main/java/org/jboss/netty/util/internal/jzlib/Inflate.java b/src/main/java/org/jboss/netty/util/internal/jzlib/Inflate.java index 32f667643d..42c399c4ff 100644 --- a/src/main/java/org/jboss/netty/util/internal/jzlib/Inflate.java +++ b/src/main/java/org/jboss/netty/util/internal/jzlib/Inflate.java @@ -111,6 +111,7 @@ final class Inflate { z.istate.mode = BLOCKS; break; case ZLIB: + case ZLIB_OR_NONE: z.istate.mode = METHOD; break; case GZIP: @@ -174,6 +175,19 @@ final class Inflate { if (z.avail_in == 0) { return r; } + + // Switch from zlib to none if necessary. + if (z.istate.wrapperType == WrapperType.ZLIB_OR_NONE) { + if ((z.next_in[z.next_in_index] & 0xf) != JZlib.Z_DEFLATED || + (z.next_in[z.next_in_index] >> 4) + 8 > z.istate.wbits) { + z.istate.wrapperType = WrapperType.NONE; + z.istate.mode = BLOCKS; + break; + } else { + z.istate.wrapperType = WrapperType.ZLIB; + } + } + r = f; z.avail_in --; @@ -286,17 +300,23 @@ final class Inflate { gzipUncompressedBytes += decompressedBytes; z.crc32 = CRC32.crc32(z.crc32, z.next_out, old_next_out_index, decompressedBytes); } + if (z.istate.wrapperType == WrapperType.NONE) { z.istate.mode = DONE; break; } else if (z.istate.wrapperType == WrapperType.ZLIB) { z.istate.mode = CHECK4; - } else { + } else if (z.istate.wrapperType == WrapperType.GZIP){ gzipCRC32 = 0; gzipISize = 0; gzipBytesToRead = 4; z.istate.mode = GZIP_CRC32; break; + } else { + z.istate.mode = BAD; + z.msg = "unexpected state"; + z.istate.marker = 0; + break; } case CHECK4: if (z.avail_in == 0) { diff --git a/src/main/java/org/jboss/netty/util/internal/jzlib/JZlib.java b/src/main/java/org/jboss/netty/util/internal/jzlib/JZlib.java index 339e0f0225..7ab37b65af 100644 --- a/src/main/java/org/jboss/netty/util/internal/jzlib/JZlib.java +++ b/src/main/java/org/jboss/netty/util/internal/jzlib/JZlib.java @@ -54,6 +54,7 @@ public final class JZlib { public static final Enum W_NONE = WrapperType.NONE; public static final Enum W_ZLIB = WrapperType.ZLIB; public static final Enum W_GZIP = WrapperType.GZIP; + public static final Enum W_ZLIB_OR_NONE = WrapperType.ZLIB_OR_NONE; // compression levels public static final int Z_NO_COMPRESSION = 0; @@ -103,6 +104,6 @@ public final class JZlib { static final int MAX_BL_BITS = 7; static enum WrapperType { - NONE, ZLIB, GZIP; + NONE, ZLIB, GZIP, ZLIB_OR_NONE; } }