View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
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      private final Inflater decompressor = new Inflater();
33  
34      private ByteBuf decompressed;
35  
36      SpdyHeaderBlockZlibDecoder(SpdyVersion spdyVersion, int maxHeaderSize) {
37          super(spdyVersion, maxHeaderSize);
38      }
39  
40      @Override
41      void decode(ByteBufAllocator alloc, ByteBuf headerBlock, SpdyHeadersFrame frame) throws Exception {
42          int len = setInput(headerBlock);
43  
44          int numBytes;
45          do {
46              numBytes = decompress(alloc, frame);
47          } while (numBytes > 0);
48  
49          // z_stream has an internal 64-bit hold buffer
50          // it is always capable of consuming the entire input
51          if (decompressor.getRemaining() != 0) {
52              // we reached the end of the deflate stream
53              throw INVALID_HEADER_BLOCK;
54          }
55  
56          headerBlock.skipBytes(len);
57      }
58  
59      private int setInput(ByteBuf compressed) {
60          int len = compressed.readableBytes();
61  
62          if (compressed.hasArray()) {
63              decompressor.setInput(compressed.array(), compressed.arrayOffset() + compressed.readerIndex(), len);
64          } else {
65              byte[] in = new byte[len];
66              compressed.getBytes(compressed.readerIndex(), in);
67              decompressor.setInput(in, 0, in.length);
68          }
69  
70          return len;
71      }
72  
73      private int decompress(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception {
74          ensureBuffer(alloc);
75          byte[] out = decompressed.array();
76          int off = decompressed.arrayOffset() + decompressed.writerIndex();
77          try {
78              int numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
79              if (numBytes == 0 && decompressor.needsDictionary()) {
80                  try {
81                      decompressor.setDictionary(SPDY_DICT);
82                  } catch (IllegalArgumentException ignored) {
83                      throw INVALID_HEADER_BLOCK;
84                  }
85                  numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
86              }
87              if (frame != null) {
88                  decompressed.writerIndex(decompressed.writerIndex() + numBytes);
89                  decodeHeaderBlock(decompressed, frame);
90                  decompressed.discardReadBytes();
91              }
92  
93              return numBytes;
94          } catch (DataFormatException e) {
95              throw new SpdyProtocolException("Received invalid header block", e);
96          }
97      }
98  
99      private void ensureBuffer(ByteBufAllocator alloc) {
100         if (decompressed == null) {
101             decompressed = alloc.heapBuffer(DEFAULT_BUFFER_CAPACITY);
102         }
103         decompressed.ensureWritable(1);
104     }
105 
106     @Override
107     void endHeaderBlock(SpdyHeadersFrame frame) throws Exception {
108         super.endHeaderBlock(frame);
109         releaseBuffer();
110     }
111 
112     @Override
113     public void end() {
114         super.end();
115         releaseBuffer();
116         decompressor.end();
117     }
118 
119     private void releaseBuffer() {
120         if (decompressed != null) {
121             decompressed.release();
122             decompressed = null;
123         }
124     }
125 }