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 inflater.setDictionary(dictionary);
300 }
301
302 if (inflater.finished()) {
303 if (crc == null) {
304 finished = true;
305 } else {
306 readFooter = true;
307 }
308 break;
309 } else {
310 decompressed = prepareDecompressBuffer(ctx, decompressed, preferredOutputBufferSize());
311 }
312 }
313
314 in.skipBytes(readableBytes - inflater.getRemaining());
315
316 if (readFooter) {
317 gzipState = GzipState.FOOTER_START;
318 handleGzipFooter(in);
319 }
320 } catch (DataFormatException e) {
321 throw new DecompressionException("decompression failure", e);
322 } finally {
323 if (decompressed != null) {
324 if (decompressed.isReadable()) {
325 needsRead = false;
326 ctx.fireChannelRead(decompressed);
327 } else {
328 decompressed.release();
329 }
330 }
331 }
332 }
333
334
335
336
337
338
339
340 private int preferredOutputBufferSize() {
341 return Math.max(inflater.getRemaining() << 1, MIN_OUTPUT_BUFFER_SIZE);
342 }
343
344 private boolean handleGzipFooter(ByteBuf in) {
345 if (readGZIPFooter(in)) {
346 finished = !decompressConcatenated;
347
348 if (!finished) {
349 inflater.reset();
350 crc.reset();
351 xlen = -1;
352 gzipState = GzipState.HEADER_START;
353 return true;
354 }
355 }
356 return false;
357 }
358
359 @Override
360 protected void decompressionBufferExhausted(ByteBuf buffer) {
361 finished = true;
362 }
363
364 @Override
365 protected void handlerRemoved0(ChannelHandlerContext ctx) throws Exception {
366 super.handlerRemoved0(ctx);
367 if (inflater != null) {
368 inflater.end();
369 }
370 }
371
372 private boolean readGZIPHeader(ByteBuf in) {
373 switch (gzipState) {
374 case HEADER_START:
375 if (in.readableBytes() < 10) {
376 return false;
377 }
378
379 int magic0 = in.readByte();
380 int magic1 = in.readByte();
381
382 if (magic0 != 31) {
383 throw new DecompressionException("Input is not in the GZIP format");
384 }
385 crc.update(magic0);
386 crc.update(magic1);
387
388 int method = in.readUnsignedByte();
389 if (method != Deflater.DEFLATED) {
390 throw new DecompressionException("Unsupported compression method "
391 + method + " in the GZIP header");
392 }
393 crc.update(method);
394
395 flags = in.readUnsignedByte();
396 crc.update(flags);
397
398 if ((flags & FRESERVED) != 0) {
399 throw new DecompressionException(
400 "Reserved flags are set in the GZIP header");
401 }
402
403
404 crc.update(in, in.readerIndex(), 4);
405 in.skipBytes(4);
406
407 crc.update(in.readUnsignedByte());
408 crc.update(in.readUnsignedByte());
409
410 gzipState = GzipState.FLG_READ;
411
412 case FLG_READ:
413 if ((flags & FEXTRA) != 0) {
414 if (in.readableBytes() < 2) {
415 return false;
416 }
417 int xlen1 = in.readUnsignedByte();
418 int xlen2 = in.readUnsignedByte();
419 crc.update(xlen1);
420 crc.update(xlen2);
421
422
423
424
425
426 xlen = xlen2 << 8 | xlen1;
427 }
428 gzipState = GzipState.XLEN_READ;
429
430 case XLEN_READ:
431 if (xlen != -1) {
432 if (in.readableBytes() < xlen) {
433 return false;
434 }
435 crc.update(in, in.readerIndex(), xlen);
436 in.skipBytes(xlen);
437 }
438 gzipState = GzipState.SKIP_FNAME;
439
440 case SKIP_FNAME:
441 if (!skipIfNeeded(in, FNAME)) {
442 return false;
443 }
444 gzipState = GzipState.SKIP_COMMENT;
445
446 case SKIP_COMMENT:
447 if (!skipIfNeeded(in, FCOMMENT)) {
448 return false;
449 }
450 gzipState = GzipState.PROCESS_FHCRC;
451
452 case PROCESS_FHCRC:
453 if ((flags & FHCRC) != 0) {
454 if (!verifyCrc16(in)) {
455 return false;
456 }
457 }
458 crc.reset();
459 gzipState = GzipState.HEADER_END;
460
461 case HEADER_END:
462 return true;
463 default:
464 throw new IllegalStateException();
465 }
466 }
467
468
469
470
471
472
473
474
475 private boolean skipIfNeeded(ByteBuf in, int flagMask) {
476 if ((flags & flagMask) != 0) {
477 for (;;) {
478 if (!in.isReadable()) {
479
480 return false;
481 }
482 int b = in.readUnsignedByte();
483 crc.update(b);
484 if (b == 0x00) {
485 break;
486 }
487 }
488 }
489
490 return true;
491 }
492
493
494
495
496
497
498
499
500 private boolean readGZIPFooter(ByteBuf in) {
501 if (in.readableBytes() < 8) {
502 return false;
503 }
504
505 boolean enoughData = verifyCrc(in);
506 assert enoughData;
507
508
509 int dataLength = in.readIntLE();
510 int readLength = inflater.getTotalOut();
511 if (dataLength != readLength) {
512 throw new DecompressionException(
513 "Number of bytes mismatch. Expected: " + dataLength + ", Got: " + readLength);
514 }
515 return true;
516 }
517
518
519
520
521
522
523
524
525 private boolean verifyCrc(ByteBuf in) {
526 if (in.readableBytes() < 4) {
527 return false;
528 }
529 long crcValue = in.readUnsignedIntLE();
530
531 long readCrc = crc.getValue();
532 if (crcValue != readCrc) {
533 throw new DecompressionException(
534 "CRC value mismatch. Expected: " + crcValue + ", Got: " + readCrc);
535 }
536 return true;
537 }
538
539 private boolean verifyCrc16(ByteBuf in) {
540 if (in.readableBytes() < 2) {
541 return false;
542 }
543
544 int crc16Value = in.readUnsignedShortLE();
545
546 int readCrc16 = (int) (crc.getValue() & 0xFFFF);
547
548 if (crc16Value != readCrc16) {
549 throw new DecompressionException(
550 "CRC16 value mismatch. Expected: " + crc16Value + ", Got: " + readCrc16);
551 }
552 return true;
553 }
554
555
556
557
558
559
560
561
562 private static boolean looksLikeZlib(short cmf_flg) {
563 return (cmf_flg & 0x7800) == 0x7800 &&
564 cmf_flg % 31 == 0;
565 }
566
567 @Override
568 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
569
570 discardSomeReadBytes();
571
572 if (needsRead && !ctx.channel().config().isAutoRead()) {
573 ctx.read();
574 }
575 ctx.fireChannelReadComplete();
576 }
577 }