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    *   https://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  import io.netty.buffer.Unpooled;
21  import io.netty.util.internal.PlatformDependent;
22  import io.netty.util.internal.SuppressJava6Requirement;
23  
24  import java.util.zip.Deflater;
25  
26  import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
27  import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
28  
29  class SpdyHeaderBlockZlibEncoder extends SpdyHeaderBlockRawEncoder {
30  
31      private final Deflater compressor;
32  
33      private boolean finished;
34  
35      SpdyHeaderBlockZlibEncoder(SpdyVersion spdyVersion, int compressionLevel) {
36          super(spdyVersion);
37          if (compressionLevel < 0 || compressionLevel > 9) {
38              throw new IllegalArgumentException(
39                      "compressionLevel: " + compressionLevel + " (expected: 0-9)");
40          }
41          compressor = new Deflater(compressionLevel);
42          compressor.setDictionary(SPDY_DICT);
43      }
44  
45      private int setInput(ByteBuf decompressed) {
46          int len = decompressed.readableBytes();
47  
48          if (decompressed.hasArray()) {
49              compressor.setInput(decompressed.array(), decompressed.arrayOffset() + decompressed.readerIndex(), len);
50          } else {
51              byte[] in = new byte[len];
52              decompressed.getBytes(decompressed.readerIndex(), in);
53              compressor.setInput(in, 0, in.length);
54          }
55  
56          return len;
57      }
58  
59      private ByteBuf encode(ByteBufAllocator alloc, int len) {
60          ByteBuf compressed = alloc.heapBuffer(len);
61          boolean release = true;
62          try {
63              while (compressInto(compressed)) {
64                  // Although unlikely, it's possible that the compressed size is larger than the decompressed size
65                  compressed.ensureWritable(compressed.capacity() << 1);
66              }
67              release = false;
68              return compressed;
69          } finally {
70              if (release) {
71                  compressed.release();
72              }
73          }
74      }
75  
76      @SuppressJava6Requirement(reason = "Guarded by java version check")
77      private boolean compressInto(ByteBuf compressed) {
78          byte[] out = compressed.array();
79          int off = compressed.arrayOffset() + compressed.writerIndex();
80          int toWrite = compressed.writableBytes();
81          final int numBytes;
82          if (PlatformDependent.javaVersion() >= 7) {
83              numBytes = compressor.deflate(out, off, toWrite, Deflater.SYNC_FLUSH);
84          } else {
85              numBytes = compressor.deflate(out, off, toWrite);
86          }
87          compressed.writerIndex(compressed.writerIndex() + numBytes);
88          return numBytes == toWrite;
89      }
90  
91      @Override
92      public ByteBuf encode(ByteBufAllocator alloc, SpdyHeadersFrame frame) throws Exception {
93          checkNotNullWithIAE(alloc, "alloc");
94          checkNotNullWithIAE(frame, "frame");
95  
96          if (finished) {
97              return Unpooled.EMPTY_BUFFER;
98          }
99  
100         ByteBuf decompressed = super.encode(alloc, frame);
101         try {
102             if (!decompressed.isReadable()) {
103                 return Unpooled.EMPTY_BUFFER;
104             }
105 
106             int len = setInput(decompressed);
107             return encode(alloc, len);
108         } finally {
109             decompressed.release();
110         }
111     }
112 
113     @Override
114     public void end() {
115         if (finished) {
116             return;
117         }
118         finished = true;
119         compressor.end();
120         super.end();
121     }
122 }