View Javadoc
1   /*
2    * Copyright 2013 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.Deflater;
22  
23  import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
24  
25  class SpdyHeaderBlockZlibEncoder extends SpdyHeaderBlockRawEncoder {
26  
27      private final Deflater compressor;
28  
29      private boolean finished;
30  
31      SpdyHeaderBlockZlibEncoder(SpdyVersion spdyVersion, int compressionLevel) {
32          super(spdyVersion);
33          if (compressionLevel < 0 || compressionLevel > 9) {
34              throw new IllegalArgumentException(
35                      "compressionLevel: " + compressionLevel + " (expected: 0-9)");
36          }
37          compressor = new Deflater(compressionLevel);
38          compressor.setDictionary(SPDY_DICT);
39      }
40  
41      private int setInput(ChannelBuffer decompressed) {
42          int len = decompressed.readableBytes();
43  
44          if (decompressed.hasArray()) {
45              compressor.setInput(decompressed.array(), decompressed.arrayOffset() + decompressed.readerIndex(), len);
46          } else {
47              byte[] in = new byte[len];
48              decompressed.getBytes(decompressed.readerIndex(), in);
49              compressor.setInput(in, 0, in.length);
50          }
51  
52          return len;
53      }
54  
55      private void encode(ChannelBuffer compressed) {
56          while (compressInto(compressed)) {
57              // Although unlikely, it's possible that the compressed size is larger than the decompressed size
58              compressed.ensureWritableBytes(compressed.capacity() << 1);
59          }
60      }
61  
62      private boolean compressInto(ChannelBuffer compressed) {
63          byte[] out = compressed.array();
64          int off = compressed.arrayOffset() + compressed.writerIndex();
65          int toWrite = compressed.writableBytes();
66          int numBytes = compressor.deflate(out, off, toWrite, Deflater.SYNC_FLUSH);
67          compressed.writerIndex(compressed.writerIndex() + numBytes);
68          return numBytes == toWrite;
69      }
70  
71      @Override
72      public synchronized ChannelBuffer encode(SpdyHeadersFrame frame) throws Exception {
73          if (frame == null) {
74              throw new IllegalArgumentException("frame");
75          }
76  
77          if (finished) {
78              return ChannelBuffers.EMPTY_BUFFER;
79          }
80  
81          ChannelBuffer decompressed = super.encode(frame);
82          if (decompressed.readableBytes() == 0) {
83              return ChannelBuffers.EMPTY_BUFFER;
84          }
85  
86          ChannelBuffer compressed = ChannelBuffers.dynamicBuffer(decompressed.readableBytes());
87          int len = setInput(decompressed);
88          encode(compressed);
89          decompressed.skipBytes(len);
90  
91          return compressed;
92      }
93  
94      @Override
95      public synchronized void end() {
96          if (finished) {
97              return;
98          }
99          finished = true;
100         compressor.end();
101         super.end();
102     }
103 }