View Javadoc

1   /*
2    * Copyright 2009 Red Hat, Inc.
3    *
4    * Red Hat licenses this file to you under the Apache License, version 2.0
5    * (the "License"); you may not use this file except in compliance with the
6    * 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.http2;
17  
18  import org.jboss.netty.buffer.ChannelBuffer;
19  import org.jboss.netty.handler.codec.compression.ZlibEncoder;
20  import org.jboss.netty.handler.codec.compression.ZlibWrapper;
21  import org.jboss.netty.handler.codec.embedder.EncoderEmbedder;
22  
23  /**
24   * Compresses an {@link HttpMessage} and an {@link HttpChunk} in {@code gzip} or
25   * {@code deflate} encoding while respecting the {@code "Accept-Encoding"} header.
26   * If there is no matching encoding, no compression is done.  For more
27   * information on how this handler modifies the message, please refer to
28   * {@link HttpContentEncoder}.
29   *
30   * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
31   * @author <a href="http://gleamynode.net/">Trustin Lee</a>
32   * @version $Rev: 1107 $, $Date: 2012-04-15 19:00:57 +0200 (dim., 15 avr. 2012) $
33   */
34  public class HttpContentCompressor extends HttpContentEncoder {
35  
36      private final int compressionLevel;
37      private final int windowBits;
38      private final int memLevel;
39  
40      /**
41       * Creates a new handler with the default compression level (<tt>6</tt>),
42       * default window size (<tt>15</tt>) and default memory level (<tt>8</tt>).
43       */
44      public HttpContentCompressor() {
45          this(6);
46      }
47  
48      /**
49       * Creates a new handler with the specified compression level, default
50       * window size (<tt>15</tt>) and default memory level (<tt>8</tt>).
51       *
52       * @param compressionLevel
53       *        {@code 1} yields the fastest compression and {@code 9} yields the
54       *        best compression.  {@code 0} means no compression.  The default
55       *        compression level is {@code 6}.
56       */
57      public HttpContentCompressor(int compressionLevel) {
58          this(compressionLevel, 15, 8);
59      }
60  
61      /**
62       * Creates a new handler with the specified compression level, window size,
63       * and memory level..
64       *
65       * @param compressionLevel
66       *        {@code 1} yields the fastest compression and {@code 9} yields the
67       *        best compression.  {@code 0} means no compression.  The default
68       *        compression level is {@code 6}.
69       * @param windowBits
70       *        The base two logarithm of the size of the history buffer.  The
71       *        value should be in the range {@code 9} to {@code 15} inclusive.
72       *        Larger values result in better compression at the expense of
73       *        memory usage.  The default value is {@code 15}.
74       * @param memLevel
75       *        How much memory should be allocated for the internal compression
76       *        state.  {@code 1} uses minimum memory and {@code 9} uses maximum
77       *        memory.  Larger values result in better and faster compression
78       *        at the expense of memory usage.  The default value is {@code 8}
79       */
80      public HttpContentCompressor(int compressionLevel, int windowBits, int memLevel) {
81          if (compressionLevel < 0 || compressionLevel > 9) {
82              throw new IllegalArgumentException(
83                      "compressionLevel: " + compressionLevel + " (expected: 0-9)");
84          }
85          if (windowBits < 9 || windowBits > 15) {
86              throw new IllegalArgumentException(
87                      "windowBits: " + windowBits + " (expected: 9-15)");
88          }
89          if (memLevel < 1 || memLevel > 9) {
90              throw new IllegalArgumentException(
91                      "memLevel: " + memLevel + " (expected: 1-9)");
92          }
93          this.compressionLevel = compressionLevel;
94          this.windowBits = windowBits;
95          this.memLevel = memLevel;
96      }
97  
98      @Override
99      protected EncoderEmbedder<ChannelBuffer> newContentEncoder(String acceptEncoding) throws Exception {
100         ZlibWrapper wrapper = determineWrapper(acceptEncoding);
101         if (wrapper == null) {
102             return null;
103         }
104 
105         return new EncoderEmbedder<ChannelBuffer>(
106                 new ZlibEncoder(wrapper, compressionLevel, windowBits, memLevel));
107     }
108 
109     @Override
110     protected String getTargetContentEncoding(String acceptEncoding) throws Exception {
111         ZlibWrapper wrapper = determineWrapper(acceptEncoding);
112         if (wrapper == null) {
113             return null;
114         }
115 
116         switch (wrapper) {
117         case GZIP:
118             return "gzip";
119         case ZLIB:
120             return "deflate";
121         default:
122             throw new Error();
123         }
124     }
125 
126     private ZlibWrapper determineWrapper(String acceptEncoding) {
127         float starQ = -1.0f;
128         float gzipQ = -1.0f;
129         float deflateQ = -1.0f;
130         for (String encoding : acceptEncoding.split(",")) {
131             float q = 1.0f;
132             int equalsPos = encoding.indexOf('=');
133             if (equalsPos != -1) {
134                 try {
135                     q = Float.valueOf(encoding.substring(equalsPos + 1));
136                 } catch (NumberFormatException e) {
137                     // Ignore encoding
138                     q = 0.0f;
139                 }
140             }
141             if (encoding.indexOf("*") >= 0) {
142                 starQ = q;
143             } else if (encoding.indexOf("gzip") >= 0 && q > gzipQ) {
144                 gzipQ = q;
145             } else if (encoding.indexOf("deflate") >= 0 && q > deflateQ) {
146                 deflateQ = q;
147             }
148         }
149         if (gzipQ > 0.0f || deflateQ > 0.0f) {
150             if (gzipQ >= deflateQ) {
151                 return ZlibWrapper.GZIP;
152             } else {
153                 return ZlibWrapper.ZLIB;
154             }
155         }
156         if (starQ > 0.0f) {
157             if (gzipQ == -1.0f) {
158                 return ZlibWrapper.GZIP;
159             }
160             if (deflateQ == -1.0f) {
161                 return ZlibWrapper.ZLIB;
162             }
163         }
164         return null;
165     }
166 }