Fix a WebSocket crash.

Previously, a WebSocket would crash if it was closed after its
StreamSubscription was canceled. Now, it tracks whether the subscription
was canceled by canceling and nulling out its own internal subscription.

Fixes #23845

R=ajohnsen@google.com

Review URL: https://codereview.chromium.org//1234163002 .
This commit is contained in:
Natalie Weizenbaum 2015-07-15 13:30:06 -07:00
parent 92fa3d7e51
commit bdd5803006
3 changed files with 45 additions and 2 deletions

View file

@ -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

View file

@ -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);
});

View file

@ -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();