1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.websocketx;
17
18 import io.netty.buffer.Unpooled;
19 import io.netty.handler.codec.http.DefaultFullHttpRequest;
20 import io.netty.handler.codec.http.FullHttpRequest;
21 import io.netty.handler.codec.http.FullHttpResponse;
22 import io.netty.handler.codec.http.HttpHeaderNames;
23 import io.netty.handler.codec.http.HttpHeaderValues;
24 import io.netty.handler.codec.http.HttpHeaders;
25 import io.netty.handler.codec.http.HttpMethod;
26 import io.netty.handler.codec.http.HttpResponseStatus;
27 import io.netty.handler.codec.http.HttpVersion;
28 import io.netty.util.CharsetUtil;
29 import io.netty.util.internal.logging.InternalLogger;
30 import io.netty.util.internal.logging.InternalLoggerFactory;
31
32 import java.net.URI;
33
34
35
36
37
38
39
40
41 public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
42
43 private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker07.class);
44 public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
45
46 private String expectedChallengeResponseString;
47
48 private final boolean allowExtensions;
49 private final boolean performMasking;
50 private final boolean allowMaskMismatch;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol,
70 boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength) {
71 this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, true, false);
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98 public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol,
99 boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
100 boolean performMasking, boolean allowMaskMismatch) {
101 this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, performMasking,
102 allowMaskMismatch, DEFAULT_FORCE_CLOSE_TIMEOUT_MILLIS);
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 public WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol,
132 boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
133 boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis) {
134 this(webSocketURL, version, subprotocol, allowExtensions, customHeaders, maxFramePayloadLength, performMasking,
135 allowMaskMismatch, forceCloseTimeoutMillis, false);
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167 WebSocketClientHandshaker07(URI webSocketURL, WebSocketVersion version, String subprotocol,
168 boolean allowExtensions, HttpHeaders customHeaders, int maxFramePayloadLength,
169 boolean performMasking, boolean allowMaskMismatch, long forceCloseTimeoutMillis,
170 boolean absoluteUpgradeUrl) {
171 super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength, forceCloseTimeoutMillis,
172 absoluteUpgradeUrl);
173 this.allowExtensions = allowExtensions;
174 this.performMasking = performMasking;
175 this.allowMaskMismatch = allowMaskMismatch;
176 }
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196 @Override
197 protected FullHttpRequest newHandshakeRequest() {
198 URI wsURL = uri();
199
200
201 byte[] nonce = WebSocketUtil.randomBytes(16);
202 String key = WebSocketUtil.base64(nonce);
203
204 String acceptSeed = key + MAGIC_GUID;
205 byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
206 expectedChallengeResponseString = WebSocketUtil.base64(sha1);
207
208 if (logger.isDebugEnabled()) {
209 logger.debug(
210 "WebSocket version 07 client handshake key: {}, expected response: {}",
211 key, expectedChallengeResponseString);
212 }
213
214
215 FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, upgradeUrl(wsURL),
216 Unpooled.EMPTY_BUFFER);
217 HttpHeaders headers = request.headers();
218
219 if (customHeaders != null) {
220 headers.add(customHeaders);
221 if (!headers.contains(HttpHeaderNames.HOST)) {
222
223
224
225 headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL));
226 }
227 } else {
228 headers.set(HttpHeaderNames.HOST, websocketHostValue(wsURL));
229 }
230
231 headers.set(HttpHeaderNames.UPGRADE, HttpHeaderValues.WEBSOCKET)
232 .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE)
233 .set(HttpHeaderNames.SEC_WEBSOCKET_KEY, key);
234
235 if (!headers.contains(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN)) {
236 headers.set(HttpHeaderNames.SEC_WEBSOCKET_ORIGIN, websocketOriginValue(wsURL));
237 }
238
239 String expectedSubprotocol = expectedSubprotocol();
240 if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
241 headers.set(HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
242 }
243
244 headers.set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, version().toAsciiString());
245 return request;
246 }
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 @Override
266 protected void verify(FullHttpResponse response) {
267 HttpResponseStatus status = response.status();
268 if (!HttpResponseStatus.SWITCHING_PROTOCOLS.equals(status)) {
269 throw new WebSocketClientHandshakeException("Invalid handshake response getStatus: " + status, response);
270 }
271
272 HttpHeaders headers = response.headers();
273 CharSequence upgrade = headers.get(HttpHeaderNames.UPGRADE);
274 if (!HttpHeaderValues.WEBSOCKET.contentEqualsIgnoreCase(upgrade)) {
275 throw new WebSocketClientHandshakeException("Invalid handshake response upgrade: " + upgrade, response);
276 }
277
278 if (!headers.containsValue(HttpHeaderNames.CONNECTION, HttpHeaderValues.UPGRADE, true)) {
279 throw new WebSocketClientHandshakeException("Invalid handshake response connection: "
280 + headers.get(HttpHeaderNames.CONNECTION), response);
281 }
282
283 CharSequence accept = headers.get(HttpHeaderNames.SEC_WEBSOCKET_ACCEPT);
284 if (accept == null || !accept.equals(expectedChallengeResponseString)) {
285 throw new WebSocketClientHandshakeException(String.format(
286 "Invalid challenge. Actual: %s. Expected: %s", accept, expectedChallengeResponseString), response);
287 }
288 }
289
290 @Override
291 protected WebSocketFrameDecoder newWebsocketDecoder() {
292 return new WebSocket07FrameDecoder(false, allowExtensions, maxFramePayloadLength(), allowMaskMismatch);
293 }
294
295 @Override
296 protected WebSocketFrameEncoder newWebSocketEncoder() {
297 return new WebSocket07FrameEncoder(performMasking);
298 }
299
300 @Override
301 public WebSocketClientHandshaker07 setForceCloseTimeoutMillis(long forceCloseTimeoutMillis) {
302 super.setForceCloseTimeoutMillis(forceCloseTimeoutMillis);
303 return this;
304 }
305
306 }