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 org.jboss.netty.handler.codec.spdy;
17  
18  import org.jboss.netty.buffer.ChannelBuffer;
19  import org.jboss.netty.buffer.ChannelBuffers;
20  
21  import java.util.zip.DataFormatException;
22  import java.util.zip.Inflater;
23  
24  import static org.jboss.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 ChannelBuffer decompressed;
35  
36      SpdyHeaderBlockZlibDecoder(SpdyVersion spdyVersion, int maxHeaderSize) {
37          super(spdyVersion, maxHeaderSize);
38      }
39  
40      @Override
41      void decode(ChannelBuffer headerBlock, SpdyHeadersFrame frame) throws Exception {
42          if (headerBlock == null) {
43              throw new NullPointerException("headerBlock");
44          }
45          if (frame == null) {
46              throw new NullPointerException("frame");
47          }
48  
49          int len = setInput(headerBlock);
50  
51          int numBytes;
52          do {
53              numBytes = decompress(frame);
54          } while (numBytes > 0);
55  
56          // z_stream has an internal 64-bit hold buffer
57          // it is always capable of consuming the entire input
58          if (decompressor.getRemaining() != 0) {
59              // we reached the end of the deflate stream
60              throw INVALID_HEADER_BLOCK;
61          }
62  
63          headerBlock.skipBytes(len);
64      }
65  
66      private int setInput(ChannelBuffer compressed) {
67          int len = compressed.readableBytes();
68  
69          if (compressed.hasArray()) {
70              decompressor.setInput(compressed.array(), compressed.arrayOffset() + compressed.readerIndex(), len);
71          } else {
72              byte[] in = new byte[len];
73              compressed.getBytes(compressed.readerIndex(), in);
74              decompressor.setInput(in, 0, in.length);
75          }
76  
77          return len;
78      }
79  
80      private int decompress(SpdyHeadersFrame frame) throws Exception {
81          ensureBuffer();
82          byte[] out = decompressed.array();
83          int off = decompressed.arrayOffset() + decompressed.writerIndex();
84          try {
85              int numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
86              if (numBytes == 0 && decompressor.needsDictionary()) {
87                  try {
88                      decompressor.setDictionary(SPDY_DICT);
89                  } catch (IllegalArgumentException e) {
90                      throw INVALID_HEADER_BLOCK;
91                  }
92                  numBytes = decompressor.inflate(out, off, decompressed.writableBytes());
93              }
94  
95              decompressed.writerIndex(decompressed.writerIndex() + numBytes);
96              super.decodeHeaderBlock(decompressed, frame);
97              decompressed.discardReadBytes();
98  
99              return numBytes;
100         } catch (DataFormatException e) {
101             throw INVALID_HEADER_BLOCK;
102         }
103     }
104 
105     private void ensureBuffer() {
106         if (decompressed == null) {
107             decompressed = ChannelBuffers.dynamicBuffer(DEFAULT_BUFFER_CAPACITY);
108         }
109         decompressed.ensureWritableBytes(1);
110     }
111 
112     @Override
113     void endHeaderBlock(SpdyHeadersFrame frame) throws Exception {
114         super.endHeaderBlock(frame);
115         decompressed = null;
116     }
117 
118     @Override
119     public void end() {
120         super.end();
121         decompressed = null;
122         decompressor.end();
123     }
124 }