OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 part of dart.io; | 5 part of dart.io; |
6 | 6 |
7 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | 7 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
8 | 8 |
9 class _WebSocketMessageType { | 9 class _WebSocketMessageType { |
10 static const int NONE = 0; | 10 static const int NONE = 0; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 */ | 43 */ |
44 class _WebSocketProtocolTransformer extends StreamEventTransformer { | 44 class _WebSocketProtocolTransformer extends StreamEventTransformer { |
45 static const int START = 0; | 45 static const int START = 0; |
46 static const int LEN_FIRST = 1; | 46 static const int LEN_FIRST = 1; |
47 static const int LEN_REST = 2; | 47 static const int LEN_REST = 2; |
48 static const int MASK = 3; | 48 static const int MASK = 3; |
49 static const int PAYLOAD = 4; | 49 static const int PAYLOAD = 4; |
50 static const int CLOSED = 5; | 50 static const int CLOSED = 5; |
51 static const int FAILURE = 6; | 51 static const int FAILURE = 6; |
52 | 52 |
53 _WebSocketProtocolTransformer() { | 53 bool _serverSide; |
| 54 |
| 55 _WebSocketProtocolTransformer([bool this._serverSide = false]) { |
54 _prepareForNextFrame(); | 56 _prepareForNextFrame(); |
55 _currentMessageType = _WebSocketMessageType.NONE; | 57 _currentMessageType = _WebSocketMessageType.NONE; |
56 } | 58 } |
57 | 59 |
58 /** | 60 /** |
59 * Process data received from the underlying communication channel. | 61 * Process data received from the underlying communication channel. |
60 */ | 62 */ |
61 void handleData(List<int> buffer, EventSink sink) { | 63 void handleData(List<int> buffer, EventSink sink) { |
62 int count = buffer.length; | 64 int count = buffer.length; |
63 int index = 0; | 65 int index = 0; |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
216 index++; | 218 index++; |
217 } | 219 } |
218 } catch (e) { | 220 } catch (e) { |
219 _state = FAILURE; | 221 _state = FAILURE; |
220 sink.addError(e); | 222 sink.addError(e); |
221 } | 223 } |
222 } | 224 } |
223 | 225 |
224 void _lengthDone(EventSink sink) { | 226 void _lengthDone(EventSink sink) { |
225 if (_masked) { | 227 if (_masked) { |
| 228 if (!_serverSide) { |
| 229 throw new WebSocketException("Received masked frame from server"); |
| 230 } |
226 _state = MASK; | 231 _state = MASK; |
227 _remainingMaskingKeyBytes = 4; | 232 _remainingMaskingKeyBytes = 4; |
228 } else { | 233 } else { |
| 234 if (_serverSide) { |
| 235 throw new WebSocketException("Received unmasked frame from client"); |
| 236 } |
229 _remainingPayloadBytes = _len; | 237 _remainingPayloadBytes = _len; |
230 _startPayload(sink); | 238 _startPayload(sink); |
231 } | 239 } |
232 } | 240 } |
233 | 241 |
234 void _maskDone(EventSink sink) { | 242 void _maskDone(EventSink sink) { |
235 _remainingPayloadBytes = _len; | 243 _remainingPayloadBytes = _len; |
236 _startPayload(sink); | 244 _startPayload(sink); |
237 } | 245 } |
238 | 246 |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
385 response.statusCode = HttpStatus.SWITCHING_PROTOCOLS; | 393 response.statusCode = HttpStatus.SWITCHING_PROTOCOLS; |
386 response.headers.add(HttpHeaders.CONNECTION, "Upgrade"); | 394 response.headers.add(HttpHeaders.CONNECTION, "Upgrade"); |
387 response.headers.add(HttpHeaders.UPGRADE, "websocket"); | 395 response.headers.add(HttpHeaders.UPGRADE, "websocket"); |
388 String key = request.headers.value("Sec-WebSocket-Key"); | 396 String key = request.headers.value("Sec-WebSocket-Key"); |
389 SHA1 sha1 = new SHA1(); | 397 SHA1 sha1 = new SHA1(); |
390 sha1.add("$key$_webSocketGUID".codeUnits); | 398 sha1.add("$key$_webSocketGUID".codeUnits); |
391 String accept = _Base64._encode(sha1.close()); | 399 String accept = _Base64._encode(sha1.close()); |
392 response.headers.add("Sec-WebSocket-Accept", accept); | 400 response.headers.add("Sec-WebSocket-Accept", accept); |
393 response.headers.contentLength = 0; | 401 response.headers.contentLength = 0; |
394 return response.detachSocket() | 402 return response.detachSocket() |
395 .then((socket) => new _WebSocketImpl._fromSocket(socket)); | 403 .then((socket) => new _WebSocketImpl._fromSocket(socket, true)); |
396 } | 404 } |
397 | 405 |
398 static bool _isUpgradeRequest(HttpRequest request) { | 406 static bool _isUpgradeRequest(HttpRequest request) { |
399 if (request.method != "GET") { | 407 if (request.method != "GET") { |
400 return false; | 408 return false; |
401 } | 409 } |
402 if (request.headers[HttpHeaders.CONNECTION] == null) { | 410 if (request.headers[HttpHeaders.CONNECTION] == null) { |
403 return false; | 411 return false; |
404 } | 412 } |
405 bool isUpgrade = false; | 413 bool isUpgrade = false; |
(...skipping 15 matching lines...) Expand all Loading... |
421 } | 429 } |
422 return true; | 430 return true; |
423 } | 431 } |
424 } | 432 } |
425 | 433 |
426 | 434 |
427 class _WebSocketImpl extends Stream implements WebSocket { | 435 class _WebSocketImpl extends Stream implements WebSocket { |
428 final StreamController _controller = new StreamController(); | 436 final StreamController _controller = new StreamController(); |
429 | 437 |
430 final Socket _socket; | 438 final Socket _socket; |
| 439 final bool _serverSide; |
431 int _readyState = WebSocket.CONNECTING; | 440 int _readyState = WebSocket.CONNECTING; |
432 bool _writeClosed = false; | 441 bool _writeClosed = false; |
433 int _closeCode; | 442 int _closeCode; |
434 String _closeReason; | 443 String _closeReason; |
435 | 444 |
436 static final HttpClient _httpClient = new HttpClient(); | 445 static final HttpClient _httpClient = new HttpClient(); |
437 | 446 |
438 static Future<WebSocket> connect(String url, [protocols]) { | 447 static Future<WebSocket> connect(String url, [protocols]) { |
439 Uri uri = Uri.parse(url); | 448 Uri uri = Uri.parse(url); |
440 if (uri.scheme != "ws" && uri.scheme != "wss") { | 449 if (uri.scheme != "ws" && uri.scheme != "wss") { |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 for (int i = 0; i < expectedAccept.length; i++) { | 507 for (int i = 0; i < expectedAccept.length; i++) { |
499 if (expectedAccept[i] != receivedAccept[i]) { | 508 if (expectedAccept[i] != receivedAccept[i]) { |
500 error("Bad response 'Sec-WebSocket-Accept' header"); | 509 error("Bad response 'Sec-WebSocket-Accept' header"); |
501 } | 510 } |
502 } | 511 } |
503 return response.detachSocket() | 512 return response.detachSocket() |
504 .then((socket) => new _WebSocketImpl._fromSocket(socket)); | 513 .then((socket) => new _WebSocketImpl._fromSocket(socket)); |
505 }); | 514 }); |
506 } | 515 } |
507 | 516 |
508 _WebSocketImpl._fromSocket(Socket this._socket) { | 517 _WebSocketImpl._fromSocket(Socket this._socket, |
| 518 [bool this._serverSide = false]) { |
509 _readyState = WebSocket.OPEN; | 519 _readyState = WebSocket.OPEN; |
510 | 520 |
511 bool closed = false; | 521 bool closed = false; |
512 var transformer = new _WebSocketProtocolTransformer(); | 522 var transformer = new _WebSocketProtocolTransformer(_serverSide); |
513 _socket.transform(transformer).listen( | 523 _socket.transform(transformer).listen( |
514 (data) { | 524 (data) { |
515 _controller.add(data); | 525 _controller.add(data); |
516 }, | 526 }, |
517 onError: (error) { | 527 onError: (error) { |
518 if (closed) return; | 528 if (closed) return; |
519 closed = true; | 529 closed = true; |
520 _controller.addError(error); | 530 _controller.addError(error); |
521 _controller.close(); | 531 _controller.close(); |
522 }, | 532 }, |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
624 data = message; | 634 data = message; |
625 } | 635 } |
626 } else { | 636 } else { |
627 opcode = _WebSocketOpcode.TEXT; | 637 opcode = _WebSocketOpcode.TEXT; |
628 } | 638 } |
629 _sendFrame(opcode, data); | 639 _sendFrame(opcode, data); |
630 } | 640 } |
631 | 641 |
632 void _sendFrame(int opcode, [List<int> data]) { | 642 void _sendFrame(int opcode, [List<int> data]) { |
633 if (_writeClosed) return; | 643 if (_writeClosed) return; |
634 bool mask = false; // Masking not implemented for server. | 644 bool mask = !_serverSide; // Masking not implemented for server. |
635 int dataLength = data == null ? 0 : data.length; | 645 int dataLength = data == null ? 0 : data.length; |
636 // Determine the header size. | 646 // Determine the header size. |
637 int headerSize = (mask) ? 6 : 2; | 647 int headerSize = (mask) ? 6 : 2; |
638 if (dataLength > 65535) { | 648 if (dataLength > 65535) { |
639 headerSize += 8; | 649 headerSize += 8; |
640 } else if (dataLength > 125) { | 650 } else if (dataLength > 125) { |
641 headerSize += 2; | 651 headerSize += 2; |
642 } | 652 } |
643 List<int> header = new List<int>(headerSize); | 653 List<int> header = new List<int>(headerSize); |
644 int index = 0; | 654 int index = 0; |
645 // Set FIN and opcode. | 655 // Set FIN and opcode. |
646 header[index++] = 0x80 | opcode; | 656 header[index++] = 0x80 | opcode; |
647 // Determine size and position of length field. | 657 // Determine size and position of length field. |
648 int lengthBytes = 1; | 658 int lengthBytes = 1; |
649 int firstLengthByte = 1; | 659 int firstLengthByte = 1; |
650 if (dataLength > 65535) { | 660 if (dataLength > 65535) { |
651 header[index++] = 127; | 661 header[index++] = 127; |
652 lengthBytes = 8; | 662 lengthBytes = 8; |
653 } else if (dataLength > 125) { | 663 } else if (dataLength > 125) { |
654 header[index++] = 126; | 664 header[index++] = 126; |
655 lengthBytes = 2; | 665 lengthBytes = 2; |
656 } | 666 } |
657 // Write the length in network byte order into the header. | 667 // Write the length in network byte order into the header. |
658 for (int i = 0; i < lengthBytes; i++) { | 668 for (int i = 0; i < lengthBytes; i++) { |
659 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; | 669 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; |
660 } | 670 } |
| 671 if (mask) { |
| 672 header[1] |= 1 << 7; |
| 673 var maskBytes = _IOCrypto.getRandomBytes(4); |
| 674 header.setRange(index, 4, maskBytes); |
| 675 index += 4; |
| 676 if (data != null) { |
| 677 var list = new Uint8List(data.length); |
| 678 for (int i = 0; i < data.length; i++) { |
| 679 list[i] = data[i] ^ maskBytes[i % 4]; |
| 680 } |
| 681 data = list; |
| 682 } |
| 683 } |
661 assert(index == headerSize); | 684 assert(index == headerSize); |
662 try { | 685 try { |
663 _socket.writeBytes(header); | 686 _socket.writeBytes(header); |
664 if (data != null) { | 687 if (data != null) { |
665 _socket.writeBytes(data); | 688 _socket.writeBytes(data); |
666 } | 689 } |
667 } catch (_) { | 690 } catch (_) { |
668 // The socket can be closed before _socket.done have a chance | 691 // The socket can be closed before _socket.done have a chance |
669 // to complete. | 692 // to complete. |
670 _writeClosed = true; | 693 _writeClosed = true; |
671 } | 694 } |
672 } | 695 } |
673 } | 696 } |
OLD | NEW |