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.ByteBuf;
19 import io.netty.buffer.Unpooled;
20 import io.netty.handler.codec.http.DefaultFullHttpRequest;
21 import io.netty.handler.codec.http.FullHttpRequest;
22 import io.netty.handler.codec.http.FullHttpResponse;
23 import io.netty.handler.codec.http.HttpHeaders;
24 import io.netty.handler.codec.http.HttpHeaders.Names;
25 import io.netty.handler.codec.http.HttpHeaders.Values;
26 import io.netty.handler.codec.http.HttpMethod;
27 import io.netty.handler.codec.http.HttpResponseStatus;
28 import io.netty.handler.codec.http.HttpVersion;
29
30 import java.net.URI;
31 import java.nio.ByteBuffer;
32
33
34
35
36
37
38
39
40
41
42
43 public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
44
45 private ByteBuf expectedChallengeResponseBytes;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol,
63 HttpHeaders customHeaders, int maxFramePayloadLength) {
64 super(webSocketURL, version, subprotocol, customHeaders, maxFramePayloadLength);
65 }
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 @Override
86 protected FullHttpRequest newHandshakeRequest() {
87
88 int spaces1 = WebSocketUtil.randomNumber(1, 12);
89 int spaces2 = WebSocketUtil.randomNumber(1, 12);
90
91 int max1 = Integer.MAX_VALUE / spaces1;
92 int max2 = Integer.MAX_VALUE / spaces2;
93
94 int number1 = WebSocketUtil.randomNumber(0, max1);
95 int number2 = WebSocketUtil.randomNumber(0, max2);
96
97 int product1 = number1 * spaces1;
98 int product2 = number2 * spaces2;
99
100 String key1 = Integer.toString(product1);
101 String key2 = Integer.toString(product2);
102
103 key1 = insertRandomCharacters(key1);
104 key2 = insertRandomCharacters(key2);
105
106 key1 = insertSpaces(key1, spaces1);
107 key2 = insertSpaces(key2, spaces2);
108
109 byte[] key3 = WebSocketUtil.randomBytes(8);
110
111 ByteBuffer buffer = ByteBuffer.allocate(4);
112 buffer.putInt(number1);
113 byte[] number1Array = buffer.array();
114 buffer = ByteBuffer.allocate(4);
115 buffer.putInt(number2);
116 byte[] number2Array = buffer.array();
117
118 byte[] challenge = new byte[16];
119 System.arraycopy(number1Array, 0, challenge, 0, 4);
120 System.arraycopy(number2Array, 0, challenge, 4, 4);
121 System.arraycopy(key3, 0, challenge, 8, 8);
122 expectedChallengeResponseBytes = Unpooled.wrappedBuffer(WebSocketUtil.md5(challenge));
123
124
125 URI wsURL = uri();
126 String path = rawPath(wsURL);
127
128
129 FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path);
130 HttpHeaders headers = request.headers();
131
132 headers.add(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET)
133 .add(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE)
134 .add(HttpHeaders.Names.HOST, websocketHostValue(wsURL))
135 .add(HttpHeaders.Names.ORIGIN, websocketOriginValue(wsURL))
136 .add(HttpHeaders.Names.SEC_WEBSOCKET_KEY1, key1)
137 .add(HttpHeaders.Names.SEC_WEBSOCKET_KEY2, key2);
138
139 String expectedSubprotocol = expectedSubprotocol();
140 if (expectedSubprotocol != null && !expectedSubprotocol.isEmpty()) {
141 headers.add(Names.SEC_WEBSOCKET_PROTOCOL, expectedSubprotocol);
142 }
143
144 if (customHeaders != null) {
145 headers.add(customHeaders);
146 }
147
148
149
150 headers.set(Names.CONTENT_LENGTH, key3.length);
151 request.content().writeBytes(key3);
152 return request;
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175 @Override
176 protected void verify(FullHttpResponse response) {
177 if (response.getStatus().equals(HttpResponseStatus.SWITCHING_PROTOCOLS)) {
178 throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
179 }
180
181 HttpHeaders headers = response.headers();
182
183 String upgrade = headers.get(Names.UPGRADE);
184 if (!Values.WEBSOCKET.equalsIgnoreCase(upgrade)) {
185 throw new WebSocketHandshakeException("Invalid handshake response upgrade: "
186 + upgrade);
187 }
188
189 if (!headers.containsValue(Names.CONNECTION, Values.UPGRADE, true)) {
190 throw new WebSocketHandshakeException("Invalid handshake response connection: "
191 + headers.get(Names.CONNECTION));
192 }
193
194 ByteBuf challenge = response.content();
195 if (!challenge.equals(expectedChallengeResponseBytes)) {
196 throw new WebSocketHandshakeException("Invalid challenge");
197 }
198 }
199
200 private static String insertRandomCharacters(String key) {
201 int count = WebSocketUtil.randomNumber(1, 12);
202
203 char[] randomChars = new char[count];
204 int randCount = 0;
205 while (randCount < count) {
206 int rand = (int) (Math.random() * 0x7e + 0x21);
207 if (0x21 < rand && rand < 0x2f || 0x3a < rand && rand < 0x7e) {
208 randomChars[randCount] = (char) rand;
209 randCount += 1;
210 }
211 }
212
213 for (int i = 0; i < count; i++) {
214 int split = WebSocketUtil.randomNumber(0, key.length());
215 String part1 = key.substring(0, split);
216 String part2 = key.substring(split);
217 key = part1 + randomChars[i] + part2;
218 }
219
220 return key;
221 }
222
223 private static String insertSpaces(String key, int spaces) {
224 for (int i = 0; i < spaces; i++) {
225 int split = WebSocketUtil.randomNumber(1, key.length() - 1);
226 String part1 = key.substring(0, split);
227 String part2 = key.substring(split);
228 key = part1 + ' ' + part2;
229 }
230
231 return key;
232 }
233
234 @Override
235 protected WebSocketFrameDecoder newWebsocketDecoder() {
236 return new WebSocket00FrameDecoder(maxFramePayloadLength());
237 }
238
239 @Override
240 protected WebSocketFrameEncoder newWebSocketEncoder() {
241 return new WebSocket00FrameEncoder();
242 }
243 }