View Javadoc
1   /*
2    * Copyright 2012 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.http;
17  
18  import io.netty.channel.Channel;
19  import io.netty.channel.embedded.EmbeddedChannel;
20  import io.netty.handler.codec.compression.Brotli;
21  import io.netty.handler.codec.compression.BrotliDecoder;
22  import io.netty.handler.codec.compression.SnappyFrameDecoder;
23  import io.netty.handler.codec.compression.ZlibCodecFactory;
24  import io.netty.handler.codec.compression.ZlibWrapper;
25  import io.netty.handler.codec.compression.Zstd;
26  import io.netty.handler.codec.compression.ZstdDecoder;
27  
28  import static io.netty.handler.codec.http.HttpHeaderValues.BR;
29  import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
30  import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
31  import static io.netty.handler.codec.http.HttpHeaderValues.SNAPPY;
32  import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
33  import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
34  import static io.netty.handler.codec.http.HttpHeaderValues.ZSTD;
35  import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
36  
37  /**
38   * Decompresses an {@link HttpMessage} and an {@link HttpContent} compressed in
39   * {@code gzip} or {@code deflate} encoding.  For more information on how this
40   * handler modifies the message, please refer to {@link HttpContentDecoder}.
41   */
42  public class HttpContentDecompressor extends HttpContentDecoder {
43  
44      private final boolean strict;
45      private final int maxAllocation;
46  
47      /**
48       * Create a new {@link HttpContentDecompressor} in non-strict mode.
49       * @deprecated
50       *            Use {@link HttpContentDecompressor#HttpContentDecompressor(int)}.
51       */
52      @Deprecated
53      public HttpContentDecompressor() {
54          this(false, 0);
55      }
56  
57      /**
58       * Create a new {@link HttpContentDecompressor} in non-strict mode.
59       * @param maxAllocation
60       *            Maximum size of the decompression buffer. Must be >= 0. If zero, maximum size is not limited.
61       */
62      public HttpContentDecompressor(int maxAllocation) {
63          this(false, maxAllocation);
64      }
65  
66      /**
67       * Create a new {@link HttpContentDecompressor}.
68       *
69       * @param strict    if {@code true} use strict handling of deflate if used, otherwise handle it in a
70       *                  more lenient fashion.
71       * @deprecated
72       *            Use {@link HttpContentDecompressor#HttpContentDecompressor(boolean, int)}.
73       */
74      @Deprecated
75      public HttpContentDecompressor(boolean strict) {
76          this(strict, 0);
77      }
78  
79      /**
80       * Create a new {@link HttpContentDecompressor}.
81       *
82       * @param strict    if {@code true} use strict handling of deflate if used, otherwise handle it in a
83       *                  more lenient fashion.
84       * @param maxAllocation
85       *             Maximum size of the decompression buffer. Must be >= 0. If zero, maximum size is not limited.
86       */
87      public HttpContentDecompressor(boolean strict, int maxAllocation) {
88          this.strict = strict;
89          this.maxAllocation = checkPositiveOrZero(maxAllocation, "maxAllocation");
90      }
91  
92      @Override
93      protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception {
94          Channel channel = ctx.channel();
95          if (GZIP.contentEqualsIgnoreCase(contentEncoding) ||
96              X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
97              return EmbeddedChannel.builder()
98                      .channelId(channel.id())
99                      .hasDisconnect(channel.metadata().hasDisconnect())
100                     .config(channel.config())
101                     .handlers(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP, maxAllocation))
102                     .build();
103         }
104         if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) ||
105             X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
106             final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
107             // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
108             return EmbeddedChannel.builder()
109                     .channelId(channel.id())
110                     .hasDisconnect(channel.metadata().hasDisconnect())
111                     .config(channel.config())
112                     .handlers(ZlibCodecFactory.newZlibDecoder(wrapper, maxAllocation))
113                     .build();
114         }
115         if (Brotli.isAvailable() && BR.contentEqualsIgnoreCase(contentEncoding)) {
116             return EmbeddedChannel.builder()
117                     .channelId(channel.id())
118                     .hasDisconnect(channel.metadata().hasDisconnect())
119                     .config(channel.config())
120                     .handlers(new BrotliDecoder())
121                     .build();
122         }
123 
124         if (SNAPPY.contentEqualsIgnoreCase(contentEncoding)) {
125             return EmbeddedChannel.builder()
126                     .channelId(channel.id())
127                     .hasDisconnect(channel.metadata().hasDisconnect())
128                     .config(channel.config())
129                     .handlers(new SnappyFrameDecoder())
130                     .build();
131         }
132 
133         if (Zstd.isAvailable() && ZSTD.contentEqualsIgnoreCase(contentEncoding)) {
134             return EmbeddedChannel.builder()
135                     .channelId(channel.id())
136                     .hasDisconnect(channel.metadata().hasDisconnect())
137                     .config(channel.config())
138                     .handlers(new ZstdDecoder())
139                     .build();
140         }
141 
142         // 'identity' or unsupported
143         return null;
144     }
145 }