1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.websocketx.extensions.compression;
17
18 import io.netty.handler.codec.compression.ZlibCodecFactory;
19 import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionData;
20 import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionDecoder;
21 import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionEncoder;
22 import io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionFilterProvider;
23 import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtension;
24 import io.netty.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandshaker;
25
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.Map.Entry;
29
30 import static io.netty.util.internal.ObjectUtil.*;
31
32
33
34
35
36 public final class PerMessageDeflateServerExtensionHandshaker implements WebSocketServerExtensionHandshaker {
37
38 public static final int MIN_WINDOW_SIZE = 8;
39 public static final int MAX_WINDOW_SIZE = 15;
40
41 static final String PERMESSAGE_DEFLATE_EXTENSION = "permessage-deflate";
42 static final String CLIENT_MAX_WINDOW = "client_max_window_bits";
43 static final String SERVER_MAX_WINDOW = "server_max_window_bits";
44 static final String CLIENT_NO_CONTEXT = "client_no_context_takeover";
45 static final String SERVER_NO_CONTEXT = "server_no_context_takeover";
46
47 private final int compressionLevel;
48 private final boolean allowServerWindowSize;
49 private final int preferredClientWindowSize;
50 private final boolean allowServerNoContext;
51 private final boolean preferredClientNoContext;
52 private final WebSocketExtensionFilterProvider extensionFilterProvider;
53 private final int maxAllocation;
54
55
56
57
58
59
60
61
62 @Deprecated
63 public PerMessageDeflateServerExtensionHandshaker() {
64 this(0);
65 }
66
67
68
69
70
71
72
73 public PerMessageDeflateServerExtensionHandshaker(int maxAllocation) {
74 this(6, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), MAX_WINDOW_SIZE, false, false, maxAllocation);
75 }
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 @Deprecated
98 public PerMessageDeflateServerExtensionHandshaker(int compressionLevel, boolean allowServerWindowSize,
99 int preferredClientWindowSize,
100 boolean allowServerNoContext, boolean preferredClientNoContext) {
101 this(compressionLevel, allowServerWindowSize, preferredClientWindowSize, allowServerNoContext,
102 preferredClientNoContext, 0);
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public PerMessageDeflateServerExtensionHandshaker(int compressionLevel, boolean allowServerWindowSize,
125 int preferredClientWindowSize,
126 boolean allowServerNoContext, boolean preferredClientNoContext, int maxAllocation) {
127 this(compressionLevel, allowServerWindowSize, preferredClientWindowSize, allowServerNoContext,
128 preferredClientNoContext, WebSocketExtensionFilterProvider.DEFAULT, maxAllocation);
129 }
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153 @Deprecated
154 public PerMessageDeflateServerExtensionHandshaker(int compressionLevel, boolean allowServerWindowSize,
155 int preferredClientWindowSize,
156 boolean allowServerNoContext, boolean preferredClientNoContext,
157 WebSocketExtensionFilterProvider extensionFilterProvider) {
158 this(compressionLevel, allowServerWindowSize, preferredClientWindowSize, allowServerNoContext,
159 preferredClientNoContext, extensionFilterProvider, 0);
160 }
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 public PerMessageDeflateServerExtensionHandshaker(int compressionLevel, boolean allowServerWindowSize,
184 int preferredClientWindowSize,
185 boolean allowServerNoContext, boolean preferredClientNoContext,
186 WebSocketExtensionFilterProvider extensionFilterProvider,
187 int maxAllocation) {
188 if (preferredClientWindowSize > MAX_WINDOW_SIZE || preferredClientWindowSize < MIN_WINDOW_SIZE) {
189 throw new IllegalArgumentException(
190 "preferredServerWindowSize: " + preferredClientWindowSize + " (expected: 8-15)");
191 }
192 if (compressionLevel < 0 || compressionLevel > 9) {
193 throw new IllegalArgumentException(
194 "compressionLevel: " + compressionLevel + " (expected: 0-9)");
195 }
196 this.compressionLevel = compressionLevel;
197 this.allowServerWindowSize = allowServerWindowSize;
198 this.preferredClientWindowSize = preferredClientWindowSize;
199 this.allowServerNoContext = allowServerNoContext;
200 this.preferredClientNoContext = preferredClientNoContext;
201 this.extensionFilterProvider = checkNotNull(extensionFilterProvider, "extensionFilterProvider");
202 this.maxAllocation = checkPositiveOrZero(maxAllocation, "maxAllocation");
203 }
204
205 @Override
206 public WebSocketServerExtension handshakeExtension(WebSocketExtensionData extensionData) {
207 if (!PERMESSAGE_DEFLATE_EXTENSION.equals(extensionData.name())) {
208 return null;
209 }
210
211 boolean deflateEnabled = true;
212 int clientWindowSize = MAX_WINDOW_SIZE;
213 int serverWindowSize = MAX_WINDOW_SIZE;
214 boolean serverNoContext = false;
215 boolean clientNoContext = false;
216
217 Iterator<Entry<String, String>> parametersIterator =
218 extensionData.parameters().entrySet().iterator();
219 while (deflateEnabled && parametersIterator.hasNext()) {
220 Entry<String, String> parameter = parametersIterator.next();
221
222 if (CLIENT_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) {
223
224 clientWindowSize = preferredClientWindowSize;
225 } else if (SERVER_MAX_WINDOW.equalsIgnoreCase(parameter.getKey())) {
226
227 if (allowServerWindowSize) {
228 serverWindowSize = Integer.parseInt(parameter.getValue());
229 if (serverWindowSize > MAX_WINDOW_SIZE || serverWindowSize < MIN_WINDOW_SIZE) {
230 deflateEnabled = false;
231 }
232 } else {
233 deflateEnabled = false;
234 }
235 } else if (CLIENT_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) {
236
237 clientNoContext = preferredClientNoContext;
238 } else if (SERVER_NO_CONTEXT.equalsIgnoreCase(parameter.getKey())) {
239
240 if (allowServerNoContext) {
241 serverNoContext = true;
242 } else {
243 deflateEnabled = false;
244 }
245 } else {
246
247 deflateEnabled = false;
248 }
249 }
250
251 if (deflateEnabled) {
252 return new PermessageDeflateExtension(compressionLevel, serverNoContext,
253 serverWindowSize, clientNoContext, clientWindowSize, extensionFilterProvider, maxAllocation);
254 } else {
255 return null;
256 }
257 }
258
259 private static class PermessageDeflateExtension implements WebSocketServerExtension {
260
261 private final int compressionLevel;
262 private final boolean serverNoContext;
263 private final int serverWindowSize;
264 private final boolean clientNoContext;
265 private final int clientWindowSize;
266 private final WebSocketExtensionFilterProvider extensionFilterProvider;
267 private final int maxAllocation;
268
269 PermessageDeflateExtension(int compressionLevel, boolean serverNoContext,
270 int serverWindowSize, boolean clientNoContext, int clientWindowSize,
271 WebSocketExtensionFilterProvider extensionFilterProvider, int maxAllocation) {
272 this.compressionLevel = compressionLevel;
273 this.serverNoContext = serverNoContext;
274 this.serverWindowSize = serverWindowSize;
275 this.clientNoContext = clientNoContext;
276 this.clientWindowSize = clientWindowSize;
277 this.extensionFilterProvider = extensionFilterProvider;
278 this.maxAllocation = maxAllocation;
279 }
280
281 @Override
282 public int rsv() {
283 return RSV1;
284 }
285
286 @Override
287 public WebSocketExtensionEncoder newExtensionEncoder() {
288 return new PerMessageDeflateEncoder(compressionLevel, serverWindowSize, serverNoContext,
289 extensionFilterProvider.encoderFilter());
290 }
291
292 @Override
293 public WebSocketExtensionDecoder newExtensionDecoder() {
294 return new PerMessageDeflateDecoder(clientNoContext, extensionFilterProvider.decoderFilter(),
295 maxAllocation);
296 }
297
298 @Override
299 public WebSocketExtensionData newReponseData() {
300 HashMap<String, String> parameters = new HashMap<String, String>(4);
301 if (serverNoContext) {
302 parameters.put(SERVER_NO_CONTEXT, null);
303 }
304 if (clientNoContext) {
305 parameters.put(CLIENT_NO_CONTEXT, null);
306 }
307 if (serverWindowSize != MAX_WINDOW_SIZE) {
308 parameters.put(SERVER_MAX_WINDOW, Integer.toString(serverWindowSize));
309 }
310 if (clientWindowSize != MAX_WINDOW_SIZE) {
311 parameters.put(CLIENT_MAX_WINDOW, Integer.toString(clientWindowSize));
312 }
313 return new WebSocketExtensionData(PERMESSAGE_DEFLATE_EXTENSION, parameters);
314 }
315 }
316
317 }