View Javadoc
1   /*
2    * Copyright 2014 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.websocketx.extensions.compression;
17  
18  import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData;
19  import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionDecoder;
20  import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder;
21  import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilterProvider;
22  import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtension;
23  import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandshaker;
24  
25  import java.util.Collections;
26  
27  import static io.netty.util.internal.ObjectUtil.*;
28  
29  /**
30   * <a href="https://tools.ietf.org/id/draft-tyoshino-hybi-websocket-perframe-deflate-06.txt">perframe-deflate</a>
31   * handshake implementation.
32   */
33  public final class DeflateFrameServerExtensionHandshaker implements WebSocketServerExtensionHandshaker {
34  
35      public static final int DEFAULT_COMPRESSION_LEVEL = 6;
36      public static final int MIN_WINDOW_SIZE = 8;
37      public static final int MAX_WINDOW_SIZE = 15;
38      public static final int DEFAULT_MEM_LEVEL = 8;
39      public static final int MIN_MEM_LEVEL = 1;
40      public static final int MAX_MEM_LEVEL = 9;
41  
42      static final String X_WEBKIT_DEFLATE_FRAME_EXTENSION = "x-webkit-deflate-frame";
43      static final String DEFLATE_FRAME_EXTENSION = "deflate-frame";
44  
45      private final int compressionLevel;
46      private final int windowSize;
47      private final int memLevel;
48      private final WebSocketExtensionFilterProvider extensionFilterProvider;
49      private final int maxAllocation;
50  
51      /**
52       * Constructor with default configuration.
53       * @deprecated
54       *            Use {@link DeflateFrameServerExtensionHandshaker#DeflateFrameServerExtensionHandshaker(int, int)}
55       *            with {@link DeflateFrameServerExtensionHandshaker#DEFAULT_COMPRESSION_LEVEL}.
56       */
57      @Deprecated
58      public DeflateFrameServerExtensionHandshaker() {
59          this(DEFAULT_COMPRESSION_LEVEL, 0);
60      }
61  
62      /**
63       * Constructor with custom configuration.
64       *
65       * @param compressionLevel
66       *            Compression level between 0 and 9 (default is 6).
67       * @deprecated
68       *            Use {@link DeflateFrameServerExtensionHandshaker#DeflateFrameServerExtensionHandshaker(int, int)}.
69       */
70      @Deprecated
71      public DeflateFrameServerExtensionHandshaker(int compressionLevel) {
72          this(compressionLevel, 0);
73      }
74  
75      /**
76       * Constructor with custom configuration.
77       *
78       * @param compressionLevel
79       *            Compression level between 0 and 9 (default is 6).
80       * @param maxAllocation
81       *            Maximum size of the decompression buffer. Must be &gt;= 0. If zero, maximum size is not limited.
82       */
83      public DeflateFrameServerExtensionHandshaker(int compressionLevel, int maxAllocation) {
84          this(compressionLevel, MAX_WINDOW_SIZE, DEFAULT_MEM_LEVEL, WebSocketExtensionFilterProvider.DEFAULT,
85                  maxAllocation);
86      }
87  
88      /**
89       * Constructor with custom configuration including compressor memory limits.
90       *
91       * @param compressionLevel
92       *            Compression level between 0 and 9 (default is 6).
93       * @param windowSize
94       *            zlib window size in bits (8-15) for the server-side deflater. Lower values reduce
95       *            per-connection memory at the cost of compression ratio.
96       * @param memLevel
97       *            zlib memory level for the server-side deflater (1-9). Lower values reduce per-connection
98       *            memory at the cost of compression ratio.
99       * @param maxAllocation
100      *            Maximum size of the decompression buffer. Must be &gt;= 0. If zero, maximum size is not limited.
101      */
102     public DeflateFrameServerExtensionHandshaker(int compressionLevel, int windowSize, int memLevel,
103                                                  int maxAllocation) {
104         this(compressionLevel, windowSize, memLevel, WebSocketExtensionFilterProvider.DEFAULT, maxAllocation);
105     }
106 
107     /**
108      * Constructor with custom configuration.
109      *
110      * @param compressionLevel
111      *            Compression level between 0 and 9 (default is 6).
112      * @param extensionFilterProvider
113      *            provides server extension filters for per frame deflate encoder and decoder.
114      * @deprecated
115      *            Use {@link DeflateFrameServerExtensionHandshaker#DeflateFrameServerExtensionHandshaker(int,
116      *            WebSocketExtensionFilterProvider, int)}.
117      */
118     @Deprecated
119     public DeflateFrameServerExtensionHandshaker(int compressionLevel,
120                                                  WebSocketExtensionFilterProvider extensionFilterProvider) {
121         this(compressionLevel, extensionFilterProvider, 0);
122     }
123 
124     /**
125      * Constructor with custom configuration.
126      *
127      * @param compressionLevel
128      *            Compression level between 0 and 9 (default is 6).
129      * @param extensionFilterProvider
130      *            provides server extension filters for per frame deflate encoder and decoder.
131      * @param maxAllocation
132      *            Maximum size of the decompression buffer. Must be &gt;= 0. If zero, maximum size is not limited.
133      * @deprecated
134      *            Use {@link DeflateFrameServerExtensionHandshaker#DeflateFrameServerExtensionHandshaker(int, int,
135      *            int, WebSocketExtensionFilterProvider, int)}.
136      */
137     @Deprecated
138     public DeflateFrameServerExtensionHandshaker(int compressionLevel,
139             WebSocketExtensionFilterProvider extensionFilterProvider,
140             int maxAllocation) {
141         this(compressionLevel, MAX_WINDOW_SIZE, DEFAULT_MEM_LEVEL, extensionFilterProvider, maxAllocation);
142     }
143 
144     /**
145      * Constructor with full custom configuration.
146      *
147      * @param compressionLevel
148      *            Compression level between 0 and 9 (default is 6).
149      * @param windowSize
150      *            zlib window size in bits (8-15) for the server-side deflater.
151      * @param memLevel
152      *            zlib memory level for the server-side deflater (1-9).
153      * @param extensionFilterProvider
154      *            provides server extension filters for per frame deflate encoder and decoder.
155      * @param maxAllocation
156      *            Maximum size of the decompression buffer. Must be &gt;= 0. If zero, maximum size is not limited.
157      */
158     public DeflateFrameServerExtensionHandshaker(int compressionLevel, int windowSize, int memLevel,
159             WebSocketExtensionFilterProvider extensionFilterProvider,
160             int maxAllocation) {
161         if (compressionLevel < 0 || compressionLevel > 9) {
162             throw new IllegalArgumentException(
163                     "compressionLevel: " + compressionLevel + " (expected: 0-9)");
164         }
165         this.compressionLevel = compressionLevel;
166         this.windowSize = checkInRange(windowSize, MIN_WINDOW_SIZE, MAX_WINDOW_SIZE, "windowSize");
167         this.memLevel = checkInRange(memLevel, MIN_MEM_LEVEL, MAX_MEM_LEVEL, "memLevel");
168         this.extensionFilterProvider = checkNotNull(extensionFilterProvider, "extensionFilterProvider");
169         this.maxAllocation = checkPositiveOrZero(maxAllocation, "maxAllocation");
170     }
171 
172     @Override
173     public WebSocketServerExtension handshakeExtension(WebSocketExtensionData extensionData) {
174         if (!X_WEBKIT_DEFLATE_FRAME_EXTENSION.equals(extensionData.name()) &&
175             !DEFLATE_FRAME_EXTENSION.equals(extensionData.name())) {
176             return null;
177         }
178 
179         if (extensionData.parameters().isEmpty()) {
180             return new DeflateFrameServerExtension(compressionLevel, windowSize, memLevel, extensionData.name(),
181                                                    extensionFilterProvider, maxAllocation);
182         } else {
183             return null;
184         }
185     }
186 
187     private static class DeflateFrameServerExtension implements WebSocketServerExtension {
188 
189         private final String extensionName;
190         private final int compressionLevel;
191         private final int windowSize;
192         private final int memLevel;
193         private final WebSocketExtensionFilterProvider extensionFilterProvider;
194         private final int maxAllocation;
195 
196         DeflateFrameServerExtension(int compressionLevel, int windowSize, int memLevel, String extensionName,
197                 WebSocketExtensionFilterProvider extensionFilterProvider,
198                 int maxAllocation) {
199             this.extensionName = extensionName;
200             this.compressionLevel = compressionLevel;
201             this.windowSize = windowSize;
202             this.memLevel = memLevel;
203             this.extensionFilterProvider = extensionFilterProvider;
204             this.maxAllocation = maxAllocation;
205         }
206 
207         @Override
208         public int rsv() {
209             return RSV1;
210         }
211 
212         @Override
213         public WebSocketExtensionEncoder newExtensionEncoder() {
214             return new PerFrameDeflateEncoder(compressionLevel, windowSize, memLevel, false,
215                                               extensionFilterProvider.encoderFilter());
216         }
217 
218         @Override
219         public WebSocketExtensionDecoder newExtensionDecoder() {
220             return new PerFrameDeflateDecoder(false, extensionFilterProvider.decoderFilter(), maxAllocation);
221         }
222 
223         @Override
224         public WebSocketExtensionData newReponseData() {
225             return new WebSocketExtensionData(extensionName, Collections.<String, String>emptyMap());
226         }
227     }
228 
229 }