| 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 437 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 448 final _WebSocketImpl webSocket; | 448 final _WebSocketImpl webSocket; |
| 449 | 449 |
| 450 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket); | 450 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket); |
| 451 | 451 |
| 452 void handleData(message, EventSink<List<int>> sink) { | 452 void handleData(message, EventSink<List<int>> sink) { |
| 453 if (message is _WebSocketPong) { | 453 if (message is _WebSocketPong) { |
| 454 addFrame(_WebSocketOpcode.PONG, message.payload, sink); | 454 addFrame(_WebSocketOpcode.PONG, message.payload, sink); |
| 455 return; | 455 return; |
| 456 } | 456 } |
| 457 if (message is _WebSocketPing) { | 457 if (message is _WebSocketPing) { |
| 458 addFrame(_WebSocketOpcode.PONG, message.payload, sink); | 458 addFrame(_WebSocketOpcode.PING, message.payload, sink); |
| 459 return; | 459 return; |
| 460 } | 460 } |
| 461 List<int> data; | 461 List<int> data; |
| 462 int opcode; | 462 int opcode; |
| 463 if (message != null) { | 463 if (message != null) { |
| 464 if (message is String) { | 464 if (message is String) { |
| 465 opcode = _WebSocketOpcode.TEXT; | 465 opcode = _WebSocketOpcode.TEXT; |
| 466 data = _encodeString(message); | 466 data = _encodeString(message); |
| 467 } else { | 467 } else { |
| 468 if (message is !List<int>) { | 468 if (message is !List<int>) { |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 } | 564 } |
| 565 } | 565 } |
| 566 } | 566 } |
| 567 | 567 |
| 568 | 568 |
| 569 class _WebSocketConsumer implements StreamConsumer { | 569 class _WebSocketConsumer implements StreamConsumer { |
| 570 final _WebSocketImpl webSocket; | 570 final _WebSocketImpl webSocket; |
| 571 final Socket socket; | 571 final Socket socket; |
| 572 StreamController _controller; | 572 StreamController _controller; |
| 573 StreamSubscription _subscription; | 573 StreamSubscription _subscription; |
| 574 bool _issuedPause = false; |
| 574 Completer _closeCompleter = new Completer(); | 575 Completer _closeCompleter = new Completer(); |
| 575 Completer _completer; | 576 Completer _completer; |
| 576 | 577 |
| 577 _WebSocketConsumer(_WebSocketImpl this.webSocket, Socket this.socket); | 578 _WebSocketConsumer(_WebSocketImpl this.webSocket, Socket this.socket); |
| 578 | 579 |
| 579 void _onListen() { | 580 void _onListen() { |
| 580 if (_subscription != null) { | 581 if (_subscription != null) { |
| 581 _subscription.cancel(); | 582 _subscription.cancel(); |
| 582 } | 583 } |
| 583 } | 584 } |
| 584 | 585 |
| 586 void _onPause() { |
| 587 if (_subscription != null) { |
| 588 _subscription.pause(); |
| 589 } else { |
| 590 _issuedPause = true; |
| 591 } |
| 592 } |
| 593 |
| 594 void _onResume() { |
| 595 if (_subscription != null) { |
| 596 _subscription.resume(); |
| 597 } else { |
| 598 _issuedPause = false; |
| 599 } |
| 600 } |
| 601 |
| 585 _ensureController() { | 602 _ensureController() { |
| 586 if (_controller != null) return; | 603 if (_controller != null) return; |
| 587 _controller = new StreamController(sync: true, | 604 _controller = new StreamController(sync: true, |
| 588 onPause: () => _subscription.pause(), | 605 onPause: _onPause, |
| 589 onResume: () => _subscription.resume(), | 606 onResume: _onResume, |
| 590 onCancel: _onListen); | 607 onCancel: _onListen); |
| 591 var stream = _controller.stream.transform( | 608 var stream = _controller.stream.transform( |
| 592 new _WebSocketOutgoingTransformer(webSocket)); | 609 new _WebSocketOutgoingTransformer(webSocket)); |
| 593 socket.addStream(stream) | 610 socket.addStream(stream) |
| 594 .then((_) { | 611 .then((_) { |
| 595 _done(); | 612 _done(); |
| 596 _closeCompleter.complete(webSocket); | 613 _closeCompleter.complete(webSocket); |
| 597 }, | 614 }, |
| 598 onError: (error) { | 615 onError: (error) { |
| 599 if (!_done(error)) { | 616 if (!_done(error)) { |
| (...skipping 20 matching lines...) Expand all Loading... |
| 620 (data) { | 637 (data) { |
| 621 _controller.add(data); | 638 _controller.add(data); |
| 622 }, | 639 }, |
| 623 onDone: () { | 640 onDone: () { |
| 624 _done(); | 641 _done(); |
| 625 }, | 642 }, |
| 626 onError: (error) { | 643 onError: (error) { |
| 627 _done(error); | 644 _done(error); |
| 628 }, | 645 }, |
| 629 cancelOnError: true); | 646 cancelOnError: true); |
| 647 if (_issuedPause) { |
| 648 _subscription.pause(); |
| 649 _issuedPause = false; |
| 650 } |
| 630 return _completer.future; | 651 return _completer.future; |
| 631 } | 652 } |
| 632 | 653 |
| 633 Future close() { | 654 Future close() { |
| 634 _ensureController(); | 655 _ensureController(); |
| 635 Future closeSocket() { | 656 Future closeSocket() { |
| 636 return socket.close().then((_) => webSocket); | 657 return socket.close().then((_) => webSocket); |
| 637 } | 658 } |
| 638 _controller.close(); | 659 _controller.close(); |
| 639 return _closeCompleter.future.then((_) => closeSocket()); | 660 return _closeCompleter.future.then((_) => closeSocket()); |
| 640 } | 661 } |
| 641 | 662 |
| 642 void add(data) { | 663 void add(data) { |
| 643 _ensureController(); | 664 _ensureController(); |
| 644 _controller.add(data); | 665 _controller.add(data); |
| 645 } | 666 } |
| 646 } | 667 } |
| 647 | 668 |
| 648 | 669 |
| 649 class _WebSocketImpl extends Stream implements WebSocket { | 670 class _WebSocketImpl extends Stream implements WebSocket { |
| 650 final StreamController _controller = new StreamController(sync: true); | 671 final StreamController _controller = new StreamController(sync: true); |
| 651 StreamSink _sink; | 672 StreamSink _sink; |
| 652 | 673 |
| 653 final Socket _socket; | 674 final Socket _socket; |
| 654 final bool _serverSide; | 675 final bool _serverSide; |
| 655 int _readyState = WebSocket.CONNECTING; | 676 int _readyState = WebSocket.CONNECTING; |
| 656 bool _writeClosed = false; | 677 bool _writeClosed = false; |
| 657 int _closeCode; | 678 int _closeCode; |
| 658 String _closeReason; | 679 String _closeReason; |
| 680 Duration _pingInterval; |
| 681 Timer _pingTimer; |
| 682 _WebSocketConsumer _consumer; |
| 659 | 683 |
| 660 int _outCloseCode; | 684 int _outCloseCode; |
| 661 String _outCloseReason; | 685 String _outCloseReason; |
| 662 | 686 |
| 663 static final HttpClient _httpClient = new HttpClient(); | 687 static final HttpClient _httpClient = new HttpClient(); |
| 664 | 688 |
| 665 static Future<WebSocket> connect(String url, [protocols]) { | 689 static Future<WebSocket> connect(String url, [protocols]) { |
| 666 Uri uri = Uri.parse(url); | 690 Uri uri = Uri.parse(url); |
| 667 if (uri.scheme != "ws" && uri.scheme != "wss") { | 691 if (uri.scheme != "ws" && uri.scheme != "wss") { |
| 668 throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'"); | 692 throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'"); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 727 error("Bad response 'Sec-WebSocket-Accept' header"); | 751 error("Bad response 'Sec-WebSocket-Accept' header"); |
| 728 } | 752 } |
| 729 } | 753 } |
| 730 return response.detachSocket() | 754 return response.detachSocket() |
| 731 .then((socket) => new _WebSocketImpl._fromSocket(socket)); | 755 .then((socket) => new _WebSocketImpl._fromSocket(socket)); |
| 732 }); | 756 }); |
| 733 } | 757 } |
| 734 | 758 |
| 735 _WebSocketImpl._fromSocket(Socket this._socket, | 759 _WebSocketImpl._fromSocket(Socket this._socket, |
| 736 [bool this._serverSide = false]) { | 760 [bool this._serverSide = false]) { |
| 737 var consumer = new _WebSocketConsumer(this, _socket); | 761 _consumer = new _WebSocketConsumer(this, _socket); |
| 738 _sink = new _StreamSinkImpl(consumer); | 762 _sink = new _StreamSinkImpl(_consumer); |
| 739 _readyState = WebSocket.OPEN; | 763 _readyState = WebSocket.OPEN; |
| 740 | 764 |
| 741 var transformer = new _WebSocketProtocolTransformer(_serverSide); | 765 var transformer = new _WebSocketProtocolTransformer(_serverSide); |
| 742 _socket.transform(transformer).listen( | 766 _socket.transform(transformer).listen( |
| 743 (data) { | 767 (data) { |
| 744 if (data is _WebSocketPing) { | 768 if (data is _WebSocketPing) { |
| 745 consumer.add(new _WebSocketPong(data.payload)); | 769 _consumer.add(new _WebSocketPong(data.payload)); |
| 746 } else if (data is _WebSocketPong) { | 770 } else if (data is _WebSocketPong) { |
| 747 // TODO(ajohnsen): Notify pong? | 771 // Simply set pingInterval, as it'll cancel any timers. |
| 772 pingInterval = _pingInterval; |
| 748 } else { | 773 } else { |
| 749 _controller.add(data); | 774 _controller.add(data); |
| 750 } | 775 } |
| 751 }, | 776 }, |
| 752 onError: (error) { | 777 onError: (error) { |
| 753 if (error is ArgumentError) { | 778 if (error is ArgumentError) { |
| 754 close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); | 779 close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); |
| 755 } else { | 780 } else { |
| 756 close(WebSocketStatus.PROTOCOL_ERROR); | 781 close(WebSocketStatus.PROTOCOL_ERROR); |
| 757 } | 782 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 778 StreamSubscription listen(void onData(message), | 803 StreamSubscription listen(void onData(message), |
| 779 {void onError(error), | 804 {void onError(error), |
| 780 void onDone(), | 805 void onDone(), |
| 781 bool cancelOnError}) { | 806 bool cancelOnError}) { |
| 782 return _controller.stream.listen(onData, | 807 return _controller.stream.listen(onData, |
| 783 onError: onError, | 808 onError: onError, |
| 784 onDone: onDone, | 809 onDone: onDone, |
| 785 cancelOnError: cancelOnError); | 810 cancelOnError: cancelOnError); |
| 786 } | 811 } |
| 787 | 812 |
| 813 Duration get pingnterval => _pingInterval; |
| 814 |
| 815 void set pingInterval(Duration interval) { |
| 816 if (_writeClosed) return; |
| 817 if (_pingTimer != null) _pingTimer.cancel(); |
| 818 _pingInterval = interval; |
| 819 |
| 820 if (_pingInterval == null) return; |
| 821 |
| 822 _pingTimer = new Timer(_pingInterval, () { |
| 823 if (_writeClosed) return; |
| 824 _consumer.add(new _WebSocketPing()); |
| 825 _pingTimer = new Timer(_pingInterval, () { |
| 826 // No pong received. |
| 827 close(WebSocketStatus.GOING_AWAY); |
| 828 }); |
| 829 }); |
| 830 } |
| 831 |
| 788 int get readyState => _readyState; | 832 int get readyState => _readyState; |
| 789 | 833 |
| 790 String get extensions => null; | 834 String get extensions => null; |
| 791 String get protocol => null; | 835 String get protocol => null; |
| 792 int get closeCode => _closeCode; | 836 int get closeCode => _closeCode; |
| 793 String get closeReason => _closeReason; | 837 String get closeReason => _closeReason; |
| 794 | 838 |
| 795 void add(data) => _sink.add(data); | 839 void add(data) => _sink.add(data); |
| 796 void addError(error) => _sink.addError(error); | 840 void addError(error) => _sink.addError(error); |
| 797 Future addStream(Stream stream) => _sink.addStream(stream); | 841 Future addStream(Stream stream) => _sink.addStream(stream); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 814 (code < WebSocketStatus.NORMAL_CLOSURE || | 858 (code < WebSocketStatus.NORMAL_CLOSURE || |
| 815 code == WebSocketStatus.RESERVED_1004 || | 859 code == WebSocketStatus.RESERVED_1004 || |
| 816 code == WebSocketStatus.NO_STATUS_RECEIVED || | 860 code == WebSocketStatus.NO_STATUS_RECEIVED || |
| 817 code == WebSocketStatus.ABNORMAL_CLOSURE || | 861 code == WebSocketStatus.ABNORMAL_CLOSURE || |
| 818 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && | 862 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && |
| 819 code < WebSocketStatus.RESERVED_1015) || | 863 code < WebSocketStatus.RESERVED_1015) || |
| 820 (code >= WebSocketStatus.RESERVED_1015 && | 864 (code >= WebSocketStatus.RESERVED_1015 && |
| 821 code < 3000)); | 865 code < 3000)); |
| 822 } | 866 } |
| 823 } | 867 } |
| OLD | NEW |