OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | 5 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
6 | 6 |
7 class _WebSocketMessageType { | 7 class _WebSocketMessageType { |
8 static const int NONE = 0; | 8 static const int NONE = 0; |
9 static const int BINARY = 1; | 9 static const int BINARY = 1; |
10 static const int TEXT = 2; | 10 static const int TEXT = 2; |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 if (_currentMessageType == _WebSocketMessageType.NONE) { | 81 if (_currentMessageType == _WebSocketMessageType.NONE) { |
82 throw new WebSocketException("Protocol error"); | 82 throw new WebSocketException("Protocol error"); |
83 } | 83 } |
84 break; | 84 break; |
85 | 85 |
86 case _WebSocketOpcode.TEXT: | 86 case _WebSocketOpcode.TEXT: |
87 if (_currentMessageType != _WebSocketMessageType.NONE) { | 87 if (_currentMessageType != _WebSocketMessageType.NONE) { |
88 throw new WebSocketException("Protocol error"); | 88 throw new WebSocketException("Protocol error"); |
89 } | 89 } |
90 _currentMessageType = _WebSocketMessageType.TEXT; | 90 _currentMessageType = _WebSocketMessageType.TEXT; |
91 if (onMessageStart !== null) { | 91 if (onMessageStart != null) { |
92 onMessageStart(_WebSocketMessageType.TEXT); | 92 onMessageStart(_WebSocketMessageType.TEXT); |
93 } | 93 } |
94 break; | 94 break; |
95 | 95 |
96 case _WebSocketOpcode.BINARY: | 96 case _WebSocketOpcode.BINARY: |
97 if (_currentMessageType != _WebSocketMessageType.NONE) { | 97 if (_currentMessageType != _WebSocketMessageType.NONE) { |
98 throw new WebSocketException("Protocol error"); | 98 throw new WebSocketException("Protocol error"); |
99 } | 99 } |
100 _currentMessageType = _WebSocketMessageType.BINARY; | 100 _currentMessageType = _WebSocketMessageType.BINARY; |
101 if (onMessageStart !== null) { | 101 if (onMessageStart != null) { |
102 onMessageStart(_WebSocketMessageType.BINARY); | 102 onMessageStart(_WebSocketMessageType.BINARY); |
103 } | 103 } |
104 break; | 104 break; |
105 | 105 |
106 case _WebSocketOpcode.CLOSE: | 106 case _WebSocketOpcode.CLOSE: |
107 case _WebSocketOpcode.PING: | 107 case _WebSocketOpcode.PING: |
108 case _WebSocketOpcode.PONG: | 108 case _WebSocketOpcode.PONG: |
109 // Control frames cannot be fragmented. | 109 // Control frames cannot be fragmented. |
110 if (!_fin) throw new WebSocketException("Protocol error"); | 110 if (!_fin) throw new WebSocketException("Protocol error"); |
111 break; | 111 break; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
185 if (_remainingPayloadBytes == 0) { | 185 if (_remainingPayloadBytes == 0) { |
186 _controlFrameEnd(); | 186 _controlFrameEnd(); |
187 } | 187 } |
188 } else { | 188 } else { |
189 switch (_currentMessageType) { | 189 switch (_currentMessageType) { |
190 case _WebSocketMessageType.NONE: | 190 case _WebSocketMessageType.NONE: |
191 throw new WebSocketException("Protocol error"); | 191 throw new WebSocketException("Protocol error"); |
192 | 192 |
193 case _WebSocketMessageType.TEXT: | 193 case _WebSocketMessageType.TEXT: |
194 case _WebSocketMessageType.BINARY: | 194 case _WebSocketMessageType.BINARY: |
195 if (onMessageData !== null) { | 195 if (onMessageData != null) { |
196 onMessageData(buffer, index, payload); | 196 onMessageData(buffer, index, payload); |
197 } | 197 } |
198 index += payload; | 198 index += payload; |
199 if (_remainingPayloadBytes == 0) { | 199 if (_remainingPayloadBytes == 0) { |
200 _messageFrameEnd(); | 200 _messageFrameEnd(); |
201 } | 201 } |
202 break; | 202 break; |
203 | 203 |
204 default: | 204 default: |
205 throw new WebSocketException("Protocol error"); | 205 throw new WebSocketException("Protocol error"); |
206 } | 206 } |
207 } | 207 } |
208 | 208 |
209 // Hack - as we always do index++ below. | 209 // Hack - as we always do index++ below. |
210 index--; | 210 index--; |
211 break; | 211 break; |
212 } | 212 } |
213 | 213 |
214 // Move to the next byte. | 214 // Move to the next byte. |
215 index++; | 215 index++; |
216 } | 216 } |
217 } catch (e) { | 217 } catch (e) { |
218 if (onClosed !== null) onClosed(WebSocketStatus.PROTOCOL_ERROR, | 218 if (onClosed != null) onClosed(WebSocketStatus.PROTOCOL_ERROR, |
219 "Protocol error"); | 219 "Protocol error"); |
220 _state = FAILURE; | 220 _state = FAILURE; |
221 } | 221 } |
222 } | 222 } |
223 | 223 |
224 /** | 224 /** |
225 * Indicate that the underlying communication channel has been closed. | 225 * Indicate that the underlying communication channel has been closed. |
226 */ | 226 */ |
227 void closed() { | 227 void closed() { |
228 if (_state == START || _state == CLOSED || _state == FAILURE) return; | 228 if (_state == START || _state == CLOSED || _state == FAILURE) return; |
229 if (onClosed !== null) onClosed(WebSocketStatus.ABNORMAL_CLOSURE, | 229 if (onClosed != null) onClosed(WebSocketStatus.ABNORMAL_CLOSURE, |
230 "Connection closed unexpectedly"); | 230 "Connection closed unexpectedly"); |
231 _state = CLOSED; | 231 _state = CLOSED; |
232 } | 232 } |
233 | 233 |
234 void _lengthDone() { | 234 void _lengthDone() { |
235 if (_masked) { | 235 if (_masked) { |
236 _state = MASK; | 236 _state = MASK; |
237 _remainingMaskingKeyBytes = 4; | 237 _remainingMaskingKeyBytes = 4; |
238 } else { | 238 } else { |
239 _remainingPayloadBytes = _len; | 239 _remainingPayloadBytes = _len; |
240 _startPayload(); | 240 _startPayload(); |
241 } | 241 } |
242 } | 242 } |
243 | 243 |
244 void _maskDone() { | 244 void _maskDone() { |
245 _remainingPayloadBytes = _len; | 245 _remainingPayloadBytes = _len; |
246 _startPayload(); | 246 _startPayload(); |
247 } | 247 } |
248 | 248 |
249 void _startPayload() { | 249 void _startPayload() { |
250 // If there is no actual payload perform perform callbacks without | 250 // If there is no actual payload perform perform callbacks without |
251 // going through the PAYLOAD state. | 251 // going through the PAYLOAD state. |
252 if (_remainingPayloadBytes == 0) { | 252 if (_remainingPayloadBytes == 0) { |
253 if (_isControlFrame()) { | 253 if (_isControlFrame()) { |
254 switch (_opcode) { | 254 switch (_opcode) { |
255 case _WebSocketOpcode.CLOSE: | 255 case _WebSocketOpcode.CLOSE: |
256 if (onClosed !== null) onClosed(1005, ""); | 256 if (onClosed != null) onClosed(1005, ""); |
257 _state = CLOSED; | 257 _state = CLOSED; |
258 break; | 258 break; |
259 case _WebSocketOpcode.PING: | 259 case _WebSocketOpcode.PING: |
260 if (onPing !== null) onPing(null); | 260 if (onPing != null) onPing(null); |
261 break; | 261 break; |
262 case _WebSocketOpcode.PONG: | 262 case _WebSocketOpcode.PONG: |
263 if (onPong !== null) onPong(null); | 263 if (onPong != null) onPong(null); |
264 break; | 264 break; |
265 } | 265 } |
266 _prepareForNextFrame(); | 266 _prepareForNextFrame(); |
267 } else { | 267 } else { |
268 _messageFrameEnd(); | 268 _messageFrameEnd(); |
269 } | 269 } |
270 } else { | 270 } else { |
271 _state = PAYLOAD; | 271 _state = PAYLOAD; |
272 } | 272 } |
273 } | 273 } |
274 | 274 |
275 void _messageFrameEnd() { | 275 void _messageFrameEnd() { |
276 if (_fin) { | 276 if (_fin) { |
277 if (onMessageEnd !== null) onMessageEnd(); | 277 if (onMessageEnd != null) onMessageEnd(); |
278 _currentMessageType = _WebSocketMessageType.NONE; | 278 _currentMessageType = _WebSocketMessageType.NONE; |
279 } | 279 } |
280 _prepareForNextFrame(); | 280 _prepareForNextFrame(); |
281 } | 281 } |
282 | 282 |
283 void _controlFrameEnd() { | 283 void _controlFrameEnd() { |
284 switch (_opcode) { | 284 switch (_opcode) { |
285 case _WebSocketOpcode.CLOSE: | 285 case _WebSocketOpcode.CLOSE: |
286 int status = WebSocketStatus.NO_STATUS_RECEIVED; | 286 int status = WebSocketStatus.NO_STATUS_RECEIVED; |
287 String reason = ""; | 287 String reason = ""; |
288 if (_controlPayload.length > 0) { | 288 if (_controlPayload.length > 0) { |
289 if (_controlPayload.length == 1) { | 289 if (_controlPayload.length == 1) { |
290 throw new WebSocketException("Protocol error"); | 290 throw new WebSocketException("Protocol error"); |
291 } | 291 } |
292 status = _controlPayload[0] << 8 | _controlPayload[1]; | 292 status = _controlPayload[0] << 8 | _controlPayload[1]; |
293 if (status == WebSocketStatus.NO_STATUS_RECEIVED) { | 293 if (status == WebSocketStatus.NO_STATUS_RECEIVED) { |
294 throw new WebSocketException("Protocol error"); | 294 throw new WebSocketException("Protocol error"); |
295 } | 295 } |
296 if (_controlPayload.length > 2) { | 296 if (_controlPayload.length > 2) { |
297 var decoder = _StringDecoders.decoder(Encoding.UTF_8); | 297 var decoder = _StringDecoders.decoder(Encoding.UTF_8); |
298 decoder.write( | 298 decoder.write( |
299 _controlPayload.getRange(2, _controlPayload.length - 2)); | 299 _controlPayload.getRange(2, _controlPayload.length - 2)); |
300 reason = decoder.decoded(); | 300 reason = decoder.decoded(); |
301 } | 301 } |
302 } | 302 } |
303 if (onClosed !== null) onClosed(status, reason); | 303 if (onClosed != null) onClosed(status, reason); |
304 _state = CLOSED; | 304 _state = CLOSED; |
305 break; | 305 break; |
306 | 306 |
307 case _WebSocketOpcode.PING: | 307 case _WebSocketOpcode.PING: |
308 if (onPing !== null) onPing(_controlPayload); | 308 if (onPing != null) onPing(_controlPayload); |
309 break; | 309 break; |
310 | 310 |
311 case _WebSocketOpcode.PONG: | 311 case _WebSocketOpcode.PONG: |
312 if (onPong !== null) onPong(_controlPayload); | 312 if (onPong != null) onPong(_controlPayload); |
313 break; | 313 break; |
314 } | 314 } |
315 _prepareForNextFrame(); | 315 _prepareForNextFrame(); |
316 } | 316 } |
317 | 317 |
318 bool _isControlFrame() { | 318 bool _isControlFrame() { |
319 return _opcode == _WebSocketOpcode.CLOSE || | 319 return _opcode == _WebSocketOpcode.CLOSE || |
320 _opcode == _WebSocketOpcode.PING || | 320 _opcode == _WebSocketOpcode.PING || |
321 _opcode == _WebSocketOpcode.PONG; | 321 _opcode == _WebSocketOpcode.PONG; |
322 } | 322 } |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
365 } | 365 } |
366 | 366 |
367 void _startProcessing(List<int> unparsedData) { | 367 void _startProcessing(List<int> unparsedData) { |
368 _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor(); | 368 _WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor(); |
369 processor.onMessageStart = _onWebSocketMessageStart; | 369 processor.onMessageStart = _onWebSocketMessageStart; |
370 processor.onMessageData = _onWebSocketMessageData; | 370 processor.onMessageData = _onWebSocketMessageData; |
371 processor.onMessageEnd = _onWebSocketMessageEnd; | 371 processor.onMessageEnd = _onWebSocketMessageEnd; |
372 processor.onPing = _onWebSocketPing; | 372 processor.onPing = _onWebSocketPing; |
373 processor.onPong = _onWebSocketPong; | 373 processor.onPong = _onWebSocketPong; |
374 processor.onClosed = _onWebSocketClosed; | 374 processor.onClosed = _onWebSocketClosed; |
375 if (unparsedData !== null) { | 375 if (unparsedData != null) { |
376 processor.update(unparsedData, 0, unparsedData.length); | 376 processor.update(unparsedData, 0, unparsedData.length); |
377 } | 377 } |
378 _socket.onData = () { | 378 _socket.onData = () { |
379 int available = _socket.available(); | 379 int available = _socket.available(); |
380 List<int> data = new List<int>(available); | 380 List<int> data = new List<int>(available); |
381 int read = _socket.readList(data, 0, available); | 381 int read = _socket.readList(data, 0, available); |
382 processor.update(data, 0, read); | 382 processor.update(data, 0, read); |
383 }; | 383 }; |
384 _socket.onClosed = () { | 384 _socket.onClosed = () { |
385 processor.closed(); | 385 processor.closed(); |
386 if (_closeSent) { | 386 if (_closeSent) { |
387 // Got socket close in response to close frame. Don't treat | 387 // Got socket close in response to close frame. Don't treat |
388 // that as an error. | 388 // that as an error. |
389 if (_closeTimer !== null) _closeTimer.cancel(); | 389 if (_closeTimer != null) _closeTimer.cancel(); |
390 } else { | 390 } else { |
391 if (_onClosed !== null) _onClosed(WebSocketStatus.ABNORMAL_CLOSURE, | 391 if (_onClosed != null) _onClosed(WebSocketStatus.ABNORMAL_CLOSURE, |
392 "Unexpected close"); | 392 "Unexpected close"); |
393 } | 393 } |
394 _socket.close(); | 394 _socket.close(); |
395 }; | 395 }; |
396 } | 396 } |
397 | 397 |
398 void set onMessage(void callback(Object message)) { | 398 void set onMessage(void callback(Object message)) { |
399 _onMessage = callback; | 399 _onMessage = callback; |
400 } | 400 } |
401 | 401 |
402 void set onClosed(void callback(int status, String reason)) { | 402 void set onClosed(void callback(int status, String reason)) { |
403 _onClosed = callback; | 403 _onClosed = callback; |
404 } | 404 } |
405 | 405 |
406 send(message) { | 406 send(message) { |
407 if (_closeSent) { | 407 if (_closeSent) { |
408 throw new WebSocketException("Connection closed"); | 408 throw new WebSocketException("Connection closed"); |
409 } | 409 } |
410 List<int> data; | 410 List<int> data; |
411 int opcode; | 411 int opcode; |
412 if (message !== null) { | 412 if (message != null) { |
413 if (message is String) { | 413 if (message is String) { |
414 opcode = _WebSocketOpcode.TEXT; | 414 opcode = _WebSocketOpcode.TEXT; |
415 data = _StringEncoders.encoder(Encoding.UTF_8).encodeString(message); | 415 data = _StringEncoders.encoder(Encoding.UTF_8).encodeString(message); |
416 } else { | 416 } else { |
417 if (message is !List<int>) { | 417 if (message is !List<int>) { |
418 throw new ArgumentError(message); | 418 throw new ArgumentError(message); |
419 } | 419 } |
420 opcode = _WebSocketOpcode.BINARY; | 420 opcode = _WebSocketOpcode.BINARY; |
421 data = message; | 421 data = message; |
422 } | 422 } |
423 } else { | 423 } else { |
424 opcode = _WebSocketOpcode.TEXT; | 424 opcode = _WebSocketOpcode.TEXT; |
425 } | 425 } |
426 _sendFrame(opcode, data); | 426 _sendFrame(opcode, data); |
427 } | 427 } |
428 | 428 |
429 close([int status, String reason]) { | 429 close([int status, String reason]) { |
430 if (status == WebSocketStatus.RESERVED_1004 || | 430 if (status == WebSocketStatus.RESERVED_1004 || |
431 status == WebSocketStatus.NO_STATUS_RECEIVED || | 431 status == WebSocketStatus.NO_STATUS_RECEIVED || |
432 status == WebSocketStatus.RESERVED_1015) { | 432 status == WebSocketStatus.RESERVED_1015) { |
433 throw new WebSocketException("Reserved status code $status"); | 433 throw new WebSocketException("Reserved status code $status"); |
434 } | 434 } |
435 | 435 |
436 if (_closeSent) return; | 436 if (_closeSent) return; |
437 List<int> data; | 437 List<int> data; |
438 if (status !== null) { | 438 if (status != null) { |
439 data = new List<int>(); | 439 data = new List<int>(); |
440 data.add((status >> 8) & 0xFF); | 440 data.add((status >> 8) & 0xFF); |
441 data.add(status & 0xFF); | 441 data.add(status & 0xFF); |
442 if (reason !== null) { | 442 if (reason != null) { |
443 data.addAll( | 443 data.addAll( |
444 _StringEncoders.encoder(Encoding.UTF_8).encodeString(reason)); | 444 _StringEncoders.encoder(Encoding.UTF_8).encodeString(reason)); |
445 } | 445 } |
446 } | 446 } |
447 _sendFrame(_WebSocketOpcode.CLOSE, data); | 447 _sendFrame(_WebSocketOpcode.CLOSE, data); |
448 | 448 |
449 if (_closeReceived) { | 449 if (_closeReceived) { |
450 // Close the socket when the close frame has been sent - if it | 450 // Close the socket when the close frame has been sent - if it |
451 // does not take too long. | 451 // does not take too long. |
452 _socket.outputStream.close(); | 452 _socket.outputStream.close(); |
453 _socket.outputStream.onClosed = () { | 453 _socket.outputStream.onClosed = () { |
454 if (_closeTimer !== null) _closeTimer.cancel(); | 454 if (_closeTimer != null) _closeTimer.cancel(); |
455 _socket.close(); | 455 _socket.close(); |
456 }; | 456 }; |
457 _closeTimer = new Timer(5000, (t) { | 457 _closeTimer = new Timer(5000, (t) { |
458 _socket.close(); | 458 _socket.close(); |
459 }); | 459 }); |
460 } else { | 460 } else { |
461 // Half close the socket and expect a close frame in response | 461 // Half close the socket and expect a close frame in response |
462 // before closing the socket. If a close frame does not arrive | 462 // before closing the socket. If a close frame does not arrive |
463 // within a reasonable amount of time just close the socket. | 463 // within a reasonable amount of time just close the socket. |
464 _socket.outputStream.close(); | 464 _socket.outputStream.close(); |
(...skipping 17 matching lines...) Expand all Loading... |
482 | 482 |
483 _onWebSocketMessageData(List<int> buffer, int offset, int count) { | 483 _onWebSocketMessageData(List<int> buffer, int offset, int count) { |
484 if (_currentMessageType == _WebSocketMessageType.TEXT) { | 484 if (_currentMessageType == _WebSocketMessageType.TEXT) { |
485 _decoder.write(buffer.getRange(offset, count)); | 485 _decoder.write(buffer.getRange(offset, count)); |
486 } else { | 486 } else { |
487 _outputStream.write(buffer.getRange(offset, count)); | 487 _outputStream.write(buffer.getRange(offset, count)); |
488 } | 488 } |
489 } | 489 } |
490 | 490 |
491 _onWebSocketMessageEnd() { | 491 _onWebSocketMessageEnd() { |
492 if (_onMessage !== null) { | 492 if (_onMessage != null) { |
493 if (_currentMessageType == _WebSocketMessageType.TEXT) { | 493 if (_currentMessageType == _WebSocketMessageType.TEXT) { |
494 _onMessage(_decoder.decoded()); | 494 _onMessage(_decoder.decoded()); |
495 } else { | 495 } else { |
496 _onMessage(_outputStream.read()); | 496 _onMessage(_outputStream.read()); |
497 } | 497 } |
498 } | 498 } |
499 _decoder = null; | 499 _decoder = null; |
500 _outputStream = null; | 500 _outputStream = null; |
501 } | 501 } |
502 | 502 |
503 _onWebSocketPing(List<int> payload) { | 503 _onWebSocketPing(List<int> payload) { |
504 _sendFrame(_WebSocketOpcode.PONG, payload); | 504 _sendFrame(_WebSocketOpcode.PONG, payload); |
505 } | 505 } |
506 | 506 |
507 _onWebSocketPong(List<int> payload) { | 507 _onWebSocketPong(List<int> payload) { |
508 // Currently pong messages are ignored. | 508 // Currently pong messages are ignored. |
509 } | 509 } |
510 | 510 |
511 _onWebSocketClosed(int status, String reason) { | 511 _onWebSocketClosed(int status, String reason) { |
512 _closeReceived = true; | 512 _closeReceived = true; |
513 if (_onClosed !== null) _onClosed(status, reason); | 513 if (_onClosed != null) _onClosed(status, reason); |
514 if (_closeSent) { | 514 if (_closeSent) { |
515 // Got close frame in response to close frame. Now close the socket. | 515 // Got close frame in response to close frame. Now close the socket. |
516 if (_closeTimer !== null) _closeTimer.cancel(); | 516 if (_closeTimer != null) _closeTimer.cancel(); |
517 _socket.close(); | 517 _socket.close(); |
518 } else { | 518 } else { |
519 if (status != WebSocketStatus.NO_STATUS_RECEIVED) { | 519 if (status != WebSocketStatus.NO_STATUS_RECEIVED) { |
520 close(status); | 520 close(status); |
521 } else { | 521 } else { |
522 close(); | 522 close(); |
523 } | 523 } |
524 } | 524 } |
525 } | 525 } |
526 | 526 |
(...skipping 20 matching lines...) Expand all Loading... |
547 } else if (dataLength > 125) { | 547 } else if (dataLength > 125) { |
548 header[index++] = 126; | 548 header[index++] = 126; |
549 lengthBytes = 2; | 549 lengthBytes = 2; |
550 } | 550 } |
551 // Write the length in network byte order into the header. | 551 // Write the length in network byte order into the header. |
552 for (int i = 0; i < lengthBytes; i++) { | 552 for (int i = 0; i < lengthBytes; i++) { |
553 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; | 553 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; |
554 } | 554 } |
555 assert(index == headerSize); | 555 assert(index == headerSize); |
556 _socket.outputStream.write(header); | 556 _socket.outputStream.write(header); |
557 if (data !== null) { | 557 if (data != null) { |
558 _socket.outputStream.write(data); | 558 _socket.outputStream.write(data); |
559 } | 559 } |
560 } | 560 } |
561 | 561 |
562 Socket _socket; | 562 Socket _socket; |
563 Timer _closeTimer; | 563 Timer _closeTimer; |
564 int _hash; | 564 int _hash; |
565 | 565 |
566 Function _onMessage; | 566 Function _onMessage; |
567 Function _onClosed; | 567 Function _onClosed; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
600 String key = request.headers.value("Sec-WebSocket-Key"); | 600 String key = request.headers.value("Sec-WebSocket-Key"); |
601 SHA1 sha1 = new SHA1(); | 601 SHA1 sha1 = new SHA1(); |
602 sha1.update("$key$_webSocketGUID".charCodes); | 602 sha1.update("$key$_webSocketGUID".charCodes); |
603 String accept = _Base64._encode(sha1.digest()); | 603 String accept = _Base64._encode(sha1.digest()); |
604 response.headers.add("Sec-WebSocket-Accept", accept); | 604 response.headers.add("Sec-WebSocket-Accept", accept); |
605 response.contentLength = 0; | 605 response.contentLength = 0; |
606 | 606 |
607 // Upgrade the connection and get the underlying socket. | 607 // Upgrade the connection and get the underlying socket. |
608 WebSocketConnection conn = | 608 WebSocketConnection conn = |
609 new _WebSocketConnection(response.detachSocket()); | 609 new _WebSocketConnection(response.detachSocket()); |
610 if (_onOpen !== null) _onOpen(conn); | 610 if (_onOpen != null) _onOpen(conn); |
611 } | 611 } |
612 | 612 |
613 void set onOpen(callback(WebSocketConnection connection)) { | 613 void set onOpen(callback(WebSocketConnection connection)) { |
614 _onOpen = callback; | 614 _onOpen = callback; |
615 } | 615 } |
616 | 616 |
617 bool _isWebSocketUpgrade(HttpRequest request) { | 617 bool _isWebSocketUpgrade(HttpRequest request) { |
618 if (request.method != "GET") { | 618 if (request.method != "GET") { |
619 return false; | 619 return false; |
620 } | 620 } |
(...skipping 24 matching lines...) Expand all Loading... |
645 } | 645 } |
646 | 646 |
647 | 647 |
648 class _WebSocketClientConnection | 648 class _WebSocketClientConnection |
649 extends _WebSocketConnectionBase implements WebSocketClientConnection { | 649 extends _WebSocketConnectionBase implements WebSocketClientConnection { |
650 _WebSocketClientConnection(HttpClientConnection this._conn, | 650 _WebSocketClientConnection(HttpClientConnection this._conn, |
651 [List<String> protocols]) { | 651 [List<String> protocols]) { |
652 _conn.onRequest = _onHttpClientRequest; | 652 _conn.onRequest = _onHttpClientRequest; |
653 _conn.onResponse = _onHttpClientResponse; | 653 _conn.onResponse = _onHttpClientResponse; |
654 _conn.onError = (e) { | 654 _conn.onError = (e) { |
655 if (_onClosed !== null) { | 655 if (_onClosed != null) { |
656 _onClosed(WebSocketStatus.ABNORMAL_CLOSURE, "$e"); | 656 _onClosed(WebSocketStatus.ABNORMAL_CLOSURE, "$e"); |
657 } | 657 } |
658 }; | 658 }; |
659 | 659 |
660 // Generate the nonce now as it is also used to set the hash code. | 660 // Generate the nonce now as it is also used to set the hash code. |
661 _generateNonceAndHash(); | 661 _generateNonceAndHash(); |
662 } | 662 } |
663 | 663 |
664 void set onRequest(void callback(HttpClientRequest request)) { | 664 void set onRequest(void callback(HttpClientRequest request)) { |
665 _onRequest = callback; | 665 _onRequest = callback; |
666 } | 666 } |
667 | 667 |
668 void set onOpen(void callback()) { | 668 void set onOpen(void callback()) { |
669 _onOpen = callback; | 669 _onOpen = callback; |
670 } | 670 } |
671 | 671 |
672 void set onNoUpgrade(void callback(HttpClientResponse request)) { | 672 void set onNoUpgrade(void callback(HttpClientResponse request)) { |
673 _onNoUpgrade = callback; | 673 _onNoUpgrade = callback; |
674 } | 674 } |
675 | 675 |
676 void _onHttpClientRequest(HttpClientRequest request) { | 676 void _onHttpClientRequest(HttpClientRequest request) { |
677 if (_onRequest !== null) { | 677 if (_onRequest != null) { |
678 _onRequest(request); | 678 _onRequest(request); |
679 } | 679 } |
680 // Setup the initial handshake. | 680 // Setup the initial handshake. |
681 request.headers.add(HttpHeaders.CONNECTION, "upgrade"); | 681 request.headers.add(HttpHeaders.CONNECTION, "upgrade"); |
682 request.headers.set(HttpHeaders.UPGRADE, "websocket"); | 682 request.headers.set(HttpHeaders.UPGRADE, "websocket"); |
683 request.headers.set("Sec-WebSocket-Key", _nonce); | 683 request.headers.set("Sec-WebSocket-Key", _nonce); |
684 request.headers.set("Sec-WebSocket-Version", "13"); | 684 request.headers.set("Sec-WebSocket-Version", "13"); |
685 request.contentLength = 0; | 685 request.contentLength = 0; |
686 request.outputStream.close(); | 686 request.outputStream.close(); |
687 } | 687 } |
688 | 688 |
689 void _onHttpClientResponse(HttpClientResponse response) { | 689 void _onHttpClientResponse(HttpClientResponse response) { |
690 if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS) { | 690 if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS) { |
691 if (_onNoUpgrade !== null) { | 691 if (_onNoUpgrade != null) { |
692 _onNoUpgrade(response); | 692 _onNoUpgrade(response); |
693 } else { | 693 } else { |
694 _conn.detachSocket().socket.close(); | 694 _conn.detachSocket().socket.close(); |
695 throw new WebSocketException("Protocol upgrade refused"); | 695 throw new WebSocketException("Protocol upgrade refused"); |
696 } | 696 } |
697 return; | 697 return; |
698 } | 698 } |
699 | 699 |
700 if (!_isWebSocketUpgrade(response)) { | 700 if (!_isWebSocketUpgrade(response)) { |
701 _conn.detachSocket().socket.close(); | 701 _conn.detachSocket().socket.close(); |
702 throw new WebSocketException("Protocol upgrade failed"); | 702 throw new WebSocketException("Protocol upgrade failed"); |
703 return; | 703 return; |
704 } | 704 } |
705 | 705 |
706 // Connection upgrade successful. | 706 // Connection upgrade successful. |
707 DetachedSocket detached = _conn.detachSocket(); | 707 DetachedSocket detached = _conn.detachSocket(); |
708 _socketConnected(detached.socket); | 708 _socketConnected(detached.socket); |
709 if (_onOpen !== null) _onOpen(); | 709 if (_onOpen != null) _onOpen(); |
710 _startProcessing(detached.unparsedData); | 710 _startProcessing(detached.unparsedData); |
711 } | 711 } |
712 | 712 |
713 void _generateNonceAndHash() { | 713 void _generateNonceAndHash() { |
714 Random random = new Random(); | 714 Random random = new Random(); |
715 assert(_nonce == null); | 715 assert(_nonce == null); |
716 void intToBigEndianBytes(int value, List<int> bytes, int offset) { | 716 void intToBigEndianBytes(int value, List<int> bytes, int offset) { |
717 bytes[offset] = (value >> 24) & 0xFF; | 717 bytes[offset] = (value >> 24) & 0xFF; |
718 bytes[offset + 1] = (value >> 16) & 0xFF; | 718 bytes[offset + 1] = (value >> 16) & 0xFF; |
719 bytes[offset + 2] = (value >> 8) & 0xFF; | 719 bytes[offset + 2] = (value >> 8) & 0xFF; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
788 | 788 |
789 HttpClient client = new HttpClient(); | 789 HttpClient client = new HttpClient(); |
790 HttpClientConnection conn = client.open("GET", uri.domain, port, path); | 790 HttpClientConnection conn = client.open("GET", uri.domain, port, path); |
791 if (protocols is String) protocols = [protocols]; | 791 if (protocols is String) protocols = [protocols]; |
792 _wsconn = new WebSocketClientConnection(conn, protocols); | 792 _wsconn = new WebSocketClientConnection(conn, protocols); |
793 _wsconn.onOpen = () { | 793 _wsconn.onOpen = () { |
794 // HTTP client not needed after socket have been detached. | 794 // HTTP client not needed after socket have been detached. |
795 client.shutdown(); | 795 client.shutdown(); |
796 client = null; | 796 client = null; |
797 _readyState = WebSocket.OPEN; | 797 _readyState = WebSocket.OPEN; |
798 if (_onopen !== null) _onopen(); | 798 if (_onopen != null) _onopen(); |
799 }; | 799 }; |
800 _wsconn.onMessage = (message) { | 800 _wsconn.onMessage = (message) { |
801 if (_onmessage !== null) { | 801 if (_onmessage != null) { |
802 _onmessage(new _WebSocketMessageEvent(message)); | 802 _onmessage(new _WebSocketMessageEvent(message)); |
803 } | 803 } |
804 }; | 804 }; |
805 _wsconn.onClosed = (status, reason) { | 805 _wsconn.onClosed = (status, reason) { |
806 _readyState = WebSocket.CLOSED; | 806 _readyState = WebSocket.CLOSED; |
807 if (_onclose !== null) { | 807 if (_onclose != null) { |
808 _onclose(new _WebSocketCloseEvent(true, status, reason)); | 808 _onclose(new _WebSocketCloseEvent(true, status, reason)); |
809 } | 809 } |
810 }; | 810 }; |
811 _wsconn.onNoUpgrade = (response) { | 811 _wsconn.onNoUpgrade = (response) { |
812 if (_onclose !== null) { | 812 if (_onclose != null) { |
813 _onclose( | 813 _onclose( |
814 new _WebSocketCloseEvent(true, | 814 new _WebSocketCloseEvent(true, |
815 WebSocketStatus.ABNORMAL_CLOSURE, | 815 WebSocketStatus.ABNORMAL_CLOSURE, |
816 "Connection not upgraded")); | 816 "Connection not upgraded")); |
817 } | 817 } |
818 }; | 818 }; |
819 } | 819 } |
820 | 820 |
821 int get readyState => _readyState; | 821 int get readyState => _readyState; |
822 int get bufferedAmount => 0; | 822 int get bufferedAmount => 0; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
864 | 864 |
865 class _WebSocketCloseEvent implements CloseEvent { | 865 class _WebSocketCloseEvent implements CloseEvent { |
866 _WebSocketCloseEvent(this._wasClean, this._code, this._reason); | 866 _WebSocketCloseEvent(this._wasClean, this._code, this._reason); |
867 bool get wasClean => _wasClean; | 867 bool get wasClean => _wasClean; |
868 int get code => _code; | 868 int get code => _code; |
869 String get reason => _reason; | 869 String get reason => _reason; |
870 bool _wasClean; | 870 bool _wasClean; |
871 int _code; | 871 int _code; |
872 String _reason; | 872 String _reason; |
873 } | 873 } |
OLD | NEW |