1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.compression;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.util.internal.ObjectUtil;
22
23 import java.util.List;
24 import java.util.zip.CRC32;
25 import java.util.zip.DataFormatException;
26 import java.util.zip.Deflater;
27 import java.util.zip.Inflater;
28
29
30
31
32 public class JdkZlibDecoder extends ZlibDecoder {
33 private static final int FHCRC = 0x02;
34 private static final int FEXTRA = 0x04;
35 private static final int FNAME = 0x08;
36 private static final int FCOMMENT = 0x10;
37 private static final int FRESERVED = 0xE0;
38
39
40
41
42
43
44
45 private static final int MIN_OUTPUT_BUFFER_SIZE = 512;
46
47 private Inflater inflater;
48 private final byte[] dictionary;
49
50
51 private final ByteBufChecksum crc;
52 private final boolean decompressConcatenated;
53
54 private enum GzipState {
55 HEADER_START,
56 HEADER_END,
57 FLG_READ,
58 XLEN_READ,
59 SKIP_FNAME,
60 SKIP_COMMENT,
61 PROCESS_FHCRC,
62 FOOTER_START,
63 }
64
65 private GzipState gzipState = GzipState.HEADER_START;
66 private int flags = -1;
67 private int xlen = -1;
68 private boolean needsRead;
69
70 private static final int DEFAULT_MAX_FORWARD_BYTES = CompressionUtil.DEFAULT_MAX_FORWARD_BYTES;
71 private final int maxForwardBytes;
72
73 private volatile boolean finished;
74
75 private boolean decideZlibOrNone;
76
77
78
79
80
81
82 @Deprecated
83 public JdkZlibDecoder() {
84 this(ZlibWrapper.ZLIB, null, false, 0);
85 }
86
87
88
89
90
91
92
93
94
95 public JdkZlibDecoder(int maxAllocation) {
96 this(ZlibWrapper.ZLIB, null, false, maxAllocation);
97 }
98
99
100
101
102
103
104
105
106 @Deprecated
107 public JdkZlibDecoder(byte[] dictionary) {
108 this(ZlibWrapper.ZLIB, dictionary, false, 0);
109 }
110
111
112
113
114
115
116
117
118
119
120 public JdkZlibDecoder(byte[] dictionary, int maxAllocation) {
121 this(ZlibWrapper.ZLIB, dictionary, false, maxAllocation);
122 }
123
124
125
126
127
128
129
130
131 @Deprecated
132 public JdkZlibDecoder(ZlibWrapper wrapper) {
133 this(wrapper, null, false, 0);
134 }
135
136
137
138
139
140
141
142
143
144
145 public JdkZlibDecoder(ZlibWrapper wrapper, int maxAllocation) {
146 this(wrapper, null, false, maxAllocation);
147 }
148
149
150
151
152 @Deprecated
153 public JdkZlibDecoder(ZlibWrapper wrapper, boolean decompressConcatenated) {
154 this(wrapper, null, decompressConcatenated, 0);
155 }
156
157 public JdkZlibDecoder(ZlibWrapper wrapper, boolean decompressConcatenated, int maxAllocation) {
158 this(wrapper, null, decompressConcatenated, maxAllocation);
159 }
160
161
162
163
164 @Deprecated
165 public JdkZlibDecoder(boolean decompressConcatenated) {
166 this(ZlibWrapper.GZIP, null, decompressConcatenated, 0);
167 }
168
169 public JdkZlibDecoder(boolean decompressConcatenated, int maxAllocation) {
170 this(ZlibWrapper.GZIP, null, decompressConcatenated, maxAllocation);
171 }
172
173 private JdkZlibDecoder(ZlibWrapper wrapper, byte[] dictionary, boolean decompressConcatenated, int maxAllocation) {
174 super(maxAllocation);
175 this.maxForwardBytes = maxAllocation > 0 ? maxAllocation : DEFAULT_MAX_FORWARD_BYTES;
176
177 ObjectUtil.checkNotNull(wrapper, "wrapper");
178
179 this.decompressConcatenated = decompressConcatenated;
180 switch (wrapper) {
181 case GZIP:
182 inflater = new Inflater(true);
183 crc = ByteBufChecksum.wrapChecksum(new CRC32());
184 break;
185 case NONE:
186 inflater = new Inflater(true);
187 crc = null;
188 break;
189 case ZLIB:
190 inflater = new Inflater();
191 crc = null;
192 break;
193 case ZLIB_OR_NONE:
194
195 decideZlibOrNone = true;
196 crc = null;
197 break;
198 default:
199 throw new IllegalArgumentException("Only GZIP or ZLIB is supported, but you used " + wrapper);
200 }
201 this.dictionary = dictionary;
202 }
203
204 @Override
205 public boolean isClosed() {
206 return finished;
207 }
208
209 @Override
210 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
211 needsRead = true;
212 if (finished) {
213
214 in.skipBytes(in.readableBytes());
215 return;
216 }
217
218 int readableBytes = in.readableBytes();
219 if (readableBytes == 0) {
220 return;
221 }
222
223 if (decideZlibOrNone) {
224
225 if (readableBytes < 2) {
226 return;
227 }
228
229 boolean nowrap = !looksLikeZlib(in.getShort(in.readerIndex()));
230 inflater = new Inflater(nowrap);
231 decideZlibOrNone = false;
232 }
233
234 if (crc != null) {
235 if (gzipState != GzipState.HEADER_END) {
236 if (gzipState == GzipState.FOOTER_START) {
237 if (!handleGzipFooter(in)) {
238
239 return;
240 }
241
242 assert gzipState == GzipState.HEADER_START;
243 }
244 if (!readGZIPHeader(in)) {
245
246 return;
247 }
248
249 readableBytes = in.readableBytes();
250 if (readableBytes == 0) {
251 return;
252 }
253 }
254 }
255
256 if (inflater.needsInput()) {
257 if (in.hasArray()) {
258 inflater.setInput(in.array(), in.arrayOffset() + in.readerIndex(), readableBytes);
259 } else {
260 byte[] array = new byte[readableBytes];
261 in.getBytes(in.readerIndex(), array);
262 inflater.setInput(array);
263 }
264 }
265
266 ByteBuf decompressed = prepareDecompressBuffer(ctx, null, preferredOutputBufferSize());
267 try {
268 boolean readFooter = false;
269
270
271
272
273 boolean pendingOutput = false;
274 while (pendingOutput || !inflater.needsInput()) {
275 byte[] outArray = decompressed.array();
276 int writerIndex = decompressed.writerIndex();
277 int outIndex = decompressed.arrayOffset() + writerIndex;
278 int writable = decompressed.writableBytes();
279 int outputLength = inflater.inflate(outArray, outIndex, writable);
280 pendingOutput = outputLength == writable;
281 if (outputLength > 0) {
282 decompressed.writerIndex(writerIndex + outputLength);
283 if (crc != null) {
284 crc.update(outArray, outIndex, outputLength);
285 }
286 if (maxAllocation == 0 && decompressed.readableBytes() >= maxForwardBytes) {
287
288
289 ByteBuf buffer = decompressed;
290 decompressed = null;
291 needsRead = false;
292 ctx.fireChannelRead(buffer);
293 }
294 } else if (inflater.needsDictionary()) {
295 if (dictionary == null) {
296 throw new DecompressionException(
297 "decompression failure, unable to set dictionary as non was specified");
298 }
299 try {
300 inflater.setDictionary(dictionary);
301 } catch (IllegalArgumentException e) {
302
303 throw new DecompressionException("decompression failure, dictionary mismatch", e);
304 }
305 }
306
307 if (inflater.finished()) {
308 if (crc == null) {
309 finished = true;
310 } else {
311 readFooter = true;
312 }
313 break;
314 } else {
315 decompressed = prepareDecompressBuffer(ctx, decompressed, preferredOutputBufferSize());
316 }
317 }
318
319 in.skipBytes(readableBytes - inflater.getRemaining());
320
321 if (readFooter) {
322 gzipState = GzipState.FOOTER_START;
323 handleGzipFooter(in);
324 }
325 } catch (DataFormatException e) {
326 throw new DecompressionException("decompression failure", e);
327 } finally {
328 if (decompressed != null) {
329 if (decompressed.isReadable()) {
330 needsRead = false;
331 ctx.fireChannelRead(decompressed);
332 } else {
333 decompressed.release();
334 }
335 }
336 }
337 }
338
339
340
341
342
343
344
345 private int preferredOutputBufferSize() {
346 return Math.max(inflater.getRemaining() << 1, MIN_OUTPUT_BUFFER_SIZE);
347 }
348
349 private boolean handleGzipFooter(ByteBuf in) {
350 if (readGZIPFooter(in)) {
351 finished = !decompressConcatenated;
352
353 if (!finished) {
354 inflater.reset();
355 crc.reset();
356 xlen = -1;
357 gzipState = GzipState.HEADER_START;
358 return true;
359 }
360 }
361 return false;
362 }
363
364 @Override
365 protected void decompressionBufferExhausted(ByteBuf buffer) {
366 finished = true;
367 }
368
369 @Override
370 protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
371 super.handlerRemoved0(ctx);
372 if (inflater != null) {
373 inflater.end();
374 }
375 }
376
377 private boolean readGZIPHeader(ByteBuf in) {
378 switch (gzipState) {
379 case HEADER_START:
380 if (in.readableBytes() < 10) {
381 return false;
382 }
383
384 int magic0 = in.readUnsignedByte();
385 int magic1 = in.readUnsignedByte();
386
387 if (magic0 != 31 || magic1 != 139) {
388 throw new DecompressionException("Input is not in the GZIP format");
389 }
390 crc.update(magic0);
391 crc.update(magic1);
392
393 int method = in.readUnsignedByte();
394 if (method != Deflater.DEFLATED) {
395 throw new DecompressionException("Unsupported compression method "
396 + method + " in the GZIP header");
397 }
398 crc.update(method);
399
400 flags = in.readUnsignedByte();
401 crc.update(flags);
402
403 if ((flags & FRESERVED) != 0) {
404 throw new DecompressionException(
405 "Reserved flags are set in the GZIP header");
406 }
407
408
409 crc.update(in, in.readerIndex(), 4);
410 in.skipBytes(4);
411
412 crc.update(in.readUnsignedByte());
413 crc.update(in.readUnsignedByte());
414
415 gzipState = GzipState.FLG_READ;
416
417 case FLG_READ:
418 if ((flags & FEXTRA) != 0) {
419 if (in.readableBytes() < 2) {
420 return false;
421 }
422 int xlen1 = in.readUnsignedByte();
423 int xlen2 = in.readUnsignedByte();
424 crc.update(xlen1);
425 crc.update(xlen2);
426
427
428
429
430
431 xlen = xlen2 << 8 | xlen1;
432 }
433 gzipState = GzipState.XLEN_READ;
434
435 case XLEN_READ:
436 if (xlen != -1) {
437 if (in.readableBytes() < xlen) {
438 return false;
439 }
440 crc.update(in, in.readerIndex(), xlen);
441 in.skipBytes(xlen);
442 }
443 gzipState = GzipState.SKIP_FNAME;
444
445 case SKIP_FNAME:
446 if (!skipIfNeeded(in, FNAME)) {
447 return false;
448 }
449 gzipState = GzipState.SKIP_COMMENT;
450
451 case SKIP_COMMENT:
452 if (!skipIfNeeded(in, FCOMMENT)) {
453 return false;
454 }
455 gzipState = GzipState.PROCESS_FHCRC;
456
457 case PROCESS_FHCRC:
458 if ((flags & FHCRC) != 0) {
459 if (!verifyCrc16(in)) {
460 return false;
461 }
462 }
463 crc.reset();
464 gzipState = GzipState.HEADER_END;
465
466 case HEADER_END:
467 return true;
468 default:
469 throw new IllegalStateException();
470 }
471 }
472
473
474
475
476
477
478
479
480 private boolean skipIfNeeded(ByteBuf in, int flagMask) {
481 if ((flags & flagMask) != 0) {
482 for (;;) {
483 if (!in.isReadable()) {
484
485 return false;
486 }
487 int b = in.readUnsignedByte();
488 crc.update(b);
489 if (b == 0x00) {
490 break;
491 }
492 }
493 }
494
495 return true;
496 }
497
498
499
500
501
502
503
504
505 private boolean readGZIPFooter(ByteBuf in) {
506 if (in.readableBytes() < 8) {
507 return false;
508 }
509
510 boolean enoughData = verifyCrc(in);
511 assert enoughData;
512
513
514 int dataLength = 0;
515 for (int i = 0; i < 4; ++i) {
516 dataLength |= in.readUnsignedByte() << i * 8;
517 }
518 int readLength = inflater.getTotalOut();
519 if (dataLength != readLength) {
520 throw new DecompressionException(
521 "Number of bytes mismatch. Expected: " + dataLength + ", Got: " + readLength);
522 }
523 return true;
524 }
525
526
527
528
529
530
531
532
533 private boolean verifyCrc(ByteBuf in) {
534 if (in.readableBytes() < 4) {
535 return false;
536 }
537 long crcValue = 0;
538 for (int i = 0; i < 4; ++i) {
539 crcValue |= (long) in.readUnsignedByte() << i * 8;
540 }
541 long readCrc = crc.getValue();
542 if (crcValue != readCrc) {
543 throw new DecompressionException(
544 "CRC value mismatch. Expected: " + crcValue + ", Got: " + readCrc);
545 }
546 return true;
547 }
548
549 private boolean verifyCrc16(ByteBuf in) {
550 if (in.readableBytes() < 2) {
551 return false;
552 }
553 long readCrc32 = crc.getValue();
554 long crc16Value = 0;
555 long readCrc16 = 0;
556 for (int i = 0; i < 2; ++i) {
557 crc16Value |= (long) in.readUnsignedByte() << (i * 8);
558 readCrc16 |= ((readCrc32 >> (i * 8)) & 0xff) << (i * 8);
559 }
560
561 if (crc16Value != readCrc16) {
562 throw new DecompressionException(
563 "CRC16 value mismatch. Expected: " + crc16Value + ", Got: " + readCrc16);
564 }
565 return true;
566 }
567
568
569
570
571
572
573
574
575 private static boolean looksLikeZlib(short cmf_flg) {
576 return (cmf_flg & 0x7800) == 0x7800 &&
577 cmf_flg % 31 == 0;
578 }
579
580 @Override
581 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
582
583 discardSomeReadBytes();
584
585 if (needsRead && !ctx.channel().config().isAutoRead()) {
586 ctx.read();
587 }
588 ctx.fireChannelReadComplete();
589 }
590 }