diff --git a/CHANGELOG.md b/CHANGELOG.md index 82389a05055..d4b441cd518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,13 @@ [package spec proposal]: https://github.com/lrhn/dep-pkgspec +## 1.11.2 + +### Core library changes + +* Fix a bug where `WebSocket.close()` would crash if called after + `WebSocket.cancel()`. + ## 1.11.1 ### Tool changes diff --git a/sdk/lib/io/websocket_impl.dart b/sdk/lib/io/websocket_impl.dart index 26576069092..9693873edb5 100644 --- a/sdk/lib/io/websocket_impl.dart +++ b/sdk/lib/io/websocket_impl.dart @@ -903,6 +903,10 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket { _subscription.pause(); _controller = new StreamController(sync: true, onListen: _subscription.resume, + onCancel: () { + _subscription.cancel(); + _subscription = null; + }, onPause: _subscription.pause, onResume: _subscription.resume); @@ -965,7 +969,7 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket { // processed if received. // 2) set a timer terminate the connection if a close frame is // not received. - if (!_controller.hasListener) { + if (!_controller.hasListener && _subscription != null) { _controller.stream.drain().catchError((_) => {}); } if (_closeTimer == null) { @@ -974,7 +978,7 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket { // Reuse code and reason from the local close. _closeCode = _outCloseCode; _closeReason = _outCloseReason; - _subscription.cancel(); + if (_subscription != null) _subscription.cancel(); _controller.close(); _webSockets.remove(_serviceId); }); diff --git a/tests/standalone/io/web_socket_test.dart b/tests/standalone/io/web_socket_test.dart index f3213e67257..9ae175815ac 100644 --- a/tests/standalone/io/web_socket_test.dart +++ b/tests/standalone/io/web_socket_test.dart @@ -187,6 +187,36 @@ class SecurityConfiguration { } + void testCancelThenClose() { + createServer().then((server) { + server.transform(new WebSocketTransformer()).listen((webSocket) { + webSocket.listen(null).cancel(); + webSocket.close(); + server.close(); + }); + + createClient(server.port).then((webSocket) { + webSocket.close(); + }); + }); + } + + void testCloseThenCancel() { + createServer().then((server) { + server.transform(new WebSocketTransformer()).listen((webSocket) { + var subscription = webSocket.listen(null); + webSocket.close(); + subscription.cancel(); + server.close(); + }); + + createClient(server.port).then((webSocket) { + webSocket.close(); + }); + }); + } + + void testListenAfterClose() { createServer().then((server) { server.transform(new WebSocketTransformer()).listen((webSocket) { @@ -543,6 +573,8 @@ class SecurityConfiguration { testMessageLength(65535); testMessageLength(65536); testCloseNoListen(); + testCancelThenClose(); + testCloseThenCancel(); testListenAfterClose(); testDoubleCloseClient(); testDoubleCloseServer();