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 static io.netty.handler.codec.http.HttpHeaderValues.BR;
19  import static io.netty.handler.codec.http.HttpHeaderValues.DEFLATE;
20  import static io.netty.handler.codec.http.HttpHeaderValues.GZIP;
21  import static io.netty.handler.codec.http.HttpHeaderValues.X_DEFLATE;
22  import static io.netty.handler.codec.http.HttpHeaderValues.X_GZIP;
23  import static io.netty.handler.codec.http.HttpHeaderValues.SNAPPY;
24  import static io.netty.handler.codec.http.HttpHeaderValues.ZSTD;
25  
26  import io.netty.channel.embedded.EmbeddedChannel;
27  import io.netty.handler.codec.compression.Brotli;
28  import io.netty.handler.codec.compression.BrotliDecoder;
29  import io.netty.handler.codec.compression.SnappyFrameDecoder;
30  import io.netty.handler.codec.compression.ZlibCodecFactory;
31  import io.netty.handler.codec.compression.ZlibWrapper;
32  import io.netty.handler.codec.compression.Zstd;
33  import io.netty.handler.codec.compression.ZstdDecoder;
34  
35  /**
36   * Decompresses an {@link HttpMessage} and an {@link HttpContent} compressed in
37   * {@code gzip} or {@code deflate} encoding.  For more information on how this
38   * handler modifies the message, please refer to {@link HttpContentDecoder}.
39   */
40  public class HttpContentDecompressor extends HttpContentDecoder {
41  
42      private final boolean strict;
43  
44      /**
45       * Create a new {@link HttpContentDecompressor} in non-strict mode.
46       */
47      public HttpContentDecompressor() {
48          this(false);
49      }
50  
51      /**
52       * Create a new {@link HttpContentDecompressor}.
53       *
54       * @param strict    if {@code true} use strict handling of deflate if used, otherwise handle it in a
55       *                  more lenient fashion.
56       */
57      public HttpContentDecompressor(boolean strict) {
58          this.strict = strict;
59      }
60  
61      @Override
62      protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception {
63          if (GZIP.contentEqualsIgnoreCase(contentEncoding) ||
64              X_GZIP.contentEqualsIgnoreCase(contentEncoding)) {
65              return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
66                      ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP));
67          }
68          if (DEFLATE.contentEqualsIgnoreCase(contentEncoding) ||
69              X_DEFLATE.contentEqualsIgnoreCase(contentEncoding)) {
70              final ZlibWrapper wrapper = strict ? ZlibWrapper.ZLIB : ZlibWrapper.ZLIB_OR_NONE;
71              // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly.
72              return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
73                      ctx.channel().config(), ZlibCodecFactory.newZlibDecoder(wrapper));
74          }
75          if (Brotli.isAvailable() && BR.contentEqualsIgnoreCase(contentEncoding)) {
76              return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
77                ctx.channel().config(), new BrotliDecoder());
78          }
79  
80          if (SNAPPY.contentEqualsIgnoreCase(contentEncoding)) {
81              return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
82                      ctx.channel().config(), new SnappyFrameDecoder());
83          }
84  
85          if (Zstd.isAvailable() && ZSTD.contentEqualsIgnoreCase(contentEncoding)) {
86              return new EmbeddedChannel(ctx.channel().id(), ctx.channel().metadata().hasDisconnect(),
87                      ctx.channel().config(), new ZstdDecoder());
88          }
89  
90          // 'identity' or unsupported
91          return null;
92      }
93  }