1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.http.websocketx;
17
18 import java.net.URI;
19 import java.nio.ByteBuffer;
20 import java.util.Arrays;
21 import java.util.Map;
22
23 import org.jboss.netty.buffer.ChannelBuffers;
24 import org.jboss.netty.channel.Channel;
25 import org.jboss.netty.channel.ChannelFuture;
26 import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
27 import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
28 import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
29 import org.jboss.netty.handler.codec.http.HttpMethod;
30 import org.jboss.netty.handler.codec.http.HttpRequest;
31 import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
32 import org.jboss.netty.handler.codec.http.HttpResponse;
33 import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
34 import org.jboss.netty.handler.codec.http.HttpResponseStatus;
35 import org.jboss.netty.handler.codec.http.HttpVersion;
36
37
38
39
40
41
42
43
44
45
46
47 public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
48
49 private byte[] expectedChallengeResponseBytes;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol,
65 Map<String, String> customHeaders) {
66 this(webSocketURL, version, subprotocol, customHeaders, Long.MAX_VALUE);
67 }
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol,
85 Map<String, String> customHeaders, long maxFramePayloadLength) {
86 super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110 @Override
111 public ChannelFuture handshake(Channel channel) {
112
113 int spaces1 = WebSocketUtil.randomNumber(1, 12);
114 int spaces2 = WebSocketUtil.randomNumber(1, 12);
115
116 int max1 = Integer.MAX_VALUE / spaces1;
117 int max2 = Integer.MAX_VALUE / spaces2;
118
119 int number1 = WebSocketUtil.randomNumber(0, max1);
120 int number2 = WebSocketUtil.randomNumber(0, max2);
121
122 int product1 = number1 * spaces1;
123 int product2 = number2 * spaces2;
124
125 String key1 = Integer.toString(product1);
126 String key2 = Integer.toString(product2);
127
128 key1 = insertRandomCharacters(key1);
129 key2 = insertRandomCharacters(key2);
130
131 key1 = insertSpaces(key1, spaces1);
132 key2 = insertSpaces(key2, spaces2);
133
134 byte[] key3 = WebSocketUtil.randomBytes(8);
135
136 ByteBuffer buffer = ByteBuffer.allocate(4);
137 buffer.putInt(number1);
138 byte[] number1Array = buffer.array();
139 buffer = ByteBuffer.allocate(4);
140 buffer.putInt(number2);
141 byte[] number2Array = buffer.array();
142
143 byte[] challenge = new byte[16];
144 System.arraycopy(number1Array, 0, challenge, 0, 4);
145 System.arraycopy(number2Array, 0, challenge, 4, 4);
146 System.arraycopy(key3, 0, challenge, 8, 8);
147 expectedChallengeResponseBytes = WebSocketUtil.md5(challenge);
148
149
150 URI wsURL = getWebSocketUrl();
151 String path = wsURL.getPath();
152 if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
153 path = wsURL.getPath() + "?" + wsURL.getQuery();
154 }
155
156
157 HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
158 request.addHeader(Names.UPGRADE, Values.WEBSOCKET);
159 request.addHeader(Names.CONNECTION, Values.UPGRADE);
160 request.addHeader(Names.HOST, wsURL.getHost());
161
162 int wsPort = wsURL.getPort();
163 String originValue = "http://" + wsURL.getHost();
164 if (wsPort != 80 && wsPort != 443) {
165
166
167 originValue = originValue + ":" + wsPort;
168 }
169 request.addHeader(Names.ORIGIN, originValue);
170
171 request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1);
172 request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2);
173 String expectedSubprotocol = getExpectedSubprotocol();
174 if (expectedSubprotocol != null && !expectedSubprotocol.equals("")) {
175 request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
176 }
177
178 if (customHeaders != null) {
179 for (String header : customHeaders.keySet()) {
180 request.addHeader(header, customHeaders.get(header));
181 }
182 }
183
184 request.setContent(ChannelBuffers.copiedBuffer(key3));
185
186 ChannelFuture future = channel.write(request);
187
188 channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket00FrameEncoder());
189
190 return future;
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215 @Override
216 public void finishHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException {
217 final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
218
219 if (!response.getStatus().equals(status)) {
220 throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
221 }
222
223 String upgrade = response.getHeader(Names.UPGRADE);
224 if (upgrade == null || !upgrade.equals(Values.WEBSOCKET)) {
225 throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
226 + response.getHeader(Names.UPGRADE));
227 }
228
229 String connection = response.getHeader(Names.CONNECTION);
230 if (connection == null || !connection.equals(Values.UPGRADE)) {
231 throw new WebSocketHandshakeException("Invalid handshake response connection: "
232 + response.getHeader(Names.CONNECTION));
233 }
234
235 byte[] challenge = response.getContent().array();
236 if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) {
237 throw new WebSocketHandshakeException("Invalid challenge");
238 }
239
240 String subprotocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
241 setActualSubprotocol(subprotocol);
242
243 setHandshakeComplete();
244
245 channel.getPipeline().get(HttpResponseDecoder.class).replace("ws-decoder",
246 new WebSocket00FrameDecoder(getMaxFramePayloadLength()));
247
248
249 }
250
251 private static String insertRandomCharacters(String key) {
252 int count = WebSocketUtil.randomNumber(1, 12);
253
254 char[] randomChars = new char[count];
255 int randCount = 0;
256 while (randCount < count) {
257 int rand = (int) (Math.random() * 0x7e + 0x21);
258 if (0x21 < rand && rand < 0x2f || 0x3a < rand && rand < 0x7e) {
259 randomChars[randCount] = (char) rand;
260 randCount += 1;
261 }
262 }
263
264 for (int i = 0; i < count; i++) {
265 int split = WebSocketUtil.randomNumber(0, key.length());
266 String part1 = key.substring(0, split);
267 String part2 = key.substring(split);
268 key = part1 + randomChars[i] + part2;
269 }
270
271 return key;
272 }
273
274 private static String insertSpaces(String key, int spaces) {
275 for (int i = 0; i < spaces; i++) {
276 int split = WebSocketUtil.randomNumber(1, key.length() - 1);
277 String part1 = key.substring(0, split);
278 String part2 = key.substring(split);
279 key = part1 + " " + part2;
280 }
281
282 return key;
283 }
284
285 }