1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.spdy;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20
21 import java.util.zip.DataFormatException;
22 import java.util.zip.Inflater;
23
24 import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
25
26 final class SpdyHeaderBlockZlibDecoder extends SpdyHeaderBlockRawDecoder {
27
28 private static final int DEFAULT_BUFFER_CAPACITY = 4096;
29 private static final SpdyProtocolException INVALID_HEADER_BLOCK =
30 new SpdyProtocolException("Invalid Header Block");
31
32
33
34
35
36
37
38 private static final int MAX_DECOMPRESSED_SIZE_MULTIPLIER = 16;
39
40 private final Inflater decompressor = new Inflater();
41 private final long maxDecompressedSize;
42
43 private ByteBuf decompressed;
44 private long totalInflated;
45
46 SpdyHeaderBlockZlibDecoder(SpdyVersion spdyVersion, int maxHeaderSize) {
47 super(spdyVersion, maxHeaderSize);
48 maxDecompressedSize = calculateMaxDecompressedSizePerBlock(maxHeaderSize);
49 }
50
51 static long calculateMaxDecompressedSizePerBlock(int maxHeaderSize) {
52 return (long) maxHeaderSize * MAX_DECOMPRESSED_SIZE_MULTIPLIER;
53 }
54
55 @Override
56 void decode(ByteBufAllocator alloc, ByteBuf headerBlock, SpdyHeadersFrame frame) throws Exception {
57 int len = setInput(headerBlock);
58
59 int numBytes;
60 do {
61 numBytes = decompress(alloc, frame);
62 totalInflated += numBytes;
63 if (totalInflated > maxDecompressedSize) {
64 throw new SpdyProtocolException(
65 "Decompressed header block exceeds " + maxDecompressedSize + " bytes");
66 }
67 } while (numBytes > 0);
68
69
70
71 if (decompressor.getRemaining() != 0) {
72
73 throw INVALID_HEADER_BLOCK;
74 }
75
76 headerBlock.skipBytes(len);
77 }
78
79 private int setInput(ByteBuf compressed) {
80 int len = compressed.readableBytes();
81
82 if (compressed.hasArray()) {
83 decompressor.setInput(compressed.array(), compressed.arrayOffset() + compressed.readerIndex(), len);
84 } else {
85 byte[] in = new byte[len];
86 compressed.getBytes(compressed.readerIndex(), in);
87 decompressor.setInput(in, 0, in.length);
88 }
89
90 return len;
91 }
92
93 private int decompress(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception {
94 ensureBuffer(alloc);
95 byte[] out = decompressed.array();
96 int off = decompressed.arrayOffset() + decompressed.writerIndex();
97 try {
98 int numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
99 if (numBytes == 0 && decompressor.needsDictionary()) {
100 try {
101 decompressor.setDictionary(SPDY_DICT);
102 } catch (IllegalArgumentException ignored) {
103 throw INVALID_HEADER_BLOCK;
104 }
105 numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
106 }
107 if (frame != null) {
108 decompressed.writerIndex(decompressed.writerIndex() + numBytes);
109 decodeHeaderBlock(decompressed, frame);
110 decompressed.discardReadBytes();
111 }
112
113 return numBytes;
114 } catch (DataFormatException e) {
115 throw new SpdyProtocolException("Received invalid header block", e);
116 }
117 }
118
119 private void ensureBuffer(ByteBufAllocator alloc) {
120 if (decompressed == null) {
121 decompressed = alloc.heapBuffer(DEFAULT_BUFFER_CAPACITY);
122 }
123 decompressed.ensureWritable(1);
124 }
125
126 @Override
127 void endHeaderBlock(SpdyHeadersFrame frame) throws Exception {
128 super.endHeaderBlock(frame);
129 releaseBuffer();
130 totalInflated = 0;
131 }
132
133 @Override
134 public void end() {
135 super.end();
136 releaseBuffer();
137 decompressor.end();
138 }
139
140 private void releaseBuffer() {
141 if (decompressed != null) {
142 decompressed.release();
143 decompressed = null;
144 }
145 }
146 }