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