| Index: runtime/observatory/lib/service_common.dart
|
| diff --git a/runtime/observatory/lib/service_common.dart b/runtime/observatory/lib/service_common.dart
|
| index c47d2b37c02df05e9f9008aa89f19a17c57a85ae..1aff16c4ccc9ede2c5b21798faa5bb5b9e8d4bd6 100644
|
| --- a/runtime/observatory/lib/service_common.dart
|
| +++ b/runtime/observatory/lib/service_common.dart
|
| @@ -56,10 +56,10 @@ class WebSocketVMTarget {
|
| class _WebSocketRequest {
|
| final String method;
|
| final Map params;
|
| - final Completer<String> completer;
|
| + final Completer<Map> completer;
|
|
|
| _WebSocketRequest(this.method, this.params)
|
| - : completer = new Completer<String>();
|
| + : completer = new Completer<Map>();
|
| }
|
|
|
| /// Minimal common interface for 'WebSocket' in [dart:io] and [dart:html].
|
| @@ -81,7 +81,7 @@ abstract class CommonWebSocket {
|
| /// Protocol.
|
| abstract class CommonWebSocketVM extends VM {
|
| final Completer _connected = new Completer();
|
| - final Completer _disconnected = new Completer();
|
| + final Completer _disconnected = new Completer<String>();
|
| final WebSocketVMTarget target;
|
| final Map<String, _WebSocketRequest> _delayedRequests =
|
| new Map<String, _WebSocketRequest>();
|
| @@ -106,26 +106,30 @@ abstract class CommonWebSocketVM extends VM {
|
| }
|
| }
|
| Future get onConnect => _connected.future;
|
| - void _notifyDisconnect() {
|
| + void _notifyDisconnect(String reason) {
|
| if (!_hasFinishedConnect) {
|
| return;
|
| }
|
| if (!_disconnected.isCompleted) {
|
| Logger.root.info('WebSocketVM connection error: ${target.networkAddress}');
|
| - _disconnected.complete(this);
|
| + _disconnected.complete(reason);
|
| }
|
| }
|
| Future get onDisconnect => _disconnected.future;
|
|
|
| - void disconnect() {
|
| + void disconnect({String reason : 'WebSocket closed'}) {
|
| if (_hasInitiatedConnect) {
|
| _webSocket.close();
|
| }
|
| - _cancelAllRequests();
|
| - _notifyDisconnect();
|
| + // We don't need to cancel requests and notify here. These
|
| + // functions will be called again when the onClose callback
|
| + // fires. However, we may have a better 'reason' string now, so
|
| + // let's take care of business.
|
| + _cancelAllRequests(reason);
|
| + _notifyDisconnect(reason);
|
| }
|
|
|
| - Future<String> invokeRpcRaw(String method, Map params) {
|
| + Future<Map> invokeRpcRaw(String method, Map params) {
|
| if (!_hasInitiatedConnect) {
|
| _hasInitiatedConnect = true;
|
| _webSocket.connect(
|
| @@ -144,14 +148,16 @@ abstract class CommonWebSocketVM extends VM {
|
| }
|
|
|
| void _onClose() {
|
| - _cancelAllRequests();
|
| - _notifyDisconnect();
|
| + _cancelAllRequests('WebSocket closed');
|
| + _notifyDisconnect('WebSocket closed');
|
| }
|
|
|
| // WebSocket error event handler.
|
| void _onError() {
|
| - _cancelAllRequests();
|
| - _notifyDisconnect();
|
| + // TODO(turnidge): The implementors of CommonWebSocket have more
|
| + // error information available. Consider providing that here.
|
| + _cancelAllRequests('WebSocket closed due to error');
|
| + _notifyDisconnect('WebSocket closed due to error');
|
| }
|
|
|
| // WebSocket open event handler.
|
| @@ -161,6 +167,23 @@ abstract class CommonWebSocketVM extends VM {
|
| _notifyConnect();
|
| }
|
|
|
| + Map _parseJSON(String message) {
|
| + var map;
|
| + try {
|
| + map = JSON.decode(message);
|
| + } catch (e, st) {
|
| + Logger.root.severe('Disconnecting: Error decoding message: $e\n$st');
|
| + disconnect(reason:'Error decoding JSON message: $e');
|
| + return null;
|
| + }
|
| + if (map == null) {
|
| + Logger.root.severe("Disconnecting: Unable to decode 'null' message");
|
| + disconnect(reason:"Unable to decode 'null' message");
|
| + return null;
|
| + }
|
| + return map;
|
| + }
|
| +
|
| void _onBinaryMessage(dynamic data) {
|
| _webSocket.nonStringToByteData(data).then((ByteData bytes) {
|
| // See format spec. in VMs Service::SendEvent.
|
| @@ -176,27 +199,30 @@ abstract class CommonWebSocketVM extends VM {
|
| bytes.buffer,
|
| bytes.offsetInBytes + offset,
|
| bytes.lengthInBytes - offset);
|
| - postServiceEvent(meta, data);
|
| + var map = _parseJSON(meta);
|
| + if (map == null) {
|
| + return;
|
| + }
|
| + var event = map['event'];
|
| + postServiceEvent(event, data);
|
| });
|
| }
|
|
|
| void _onStringMessage(String data) {
|
| - var map = JSON.decode(data);
|
| + var map = _parseJSON(data);
|
| if (map == null) {
|
| - Logger.root.severe('WebSocketVM got empty message');
|
| return;
|
| }
|
| - // Extract serial and result.
|
| - var serial;
|
| - var result;
|
| - serial = map['id'];
|
| - result = map['result'];
|
| - if (serial == null) {
|
| - // Messages without serial numbers are asynchronous events
|
| - // from the vm.
|
| - postServiceEvent(result, null);
|
| + var event = map['event'];
|
| + if (event != null) {
|
| + postServiceEvent(event, null);
|
| return;
|
| }
|
| +
|
| + // Extract serial and result.
|
| + var serial = map['id'];
|
| + var result = map['result'];
|
| +
|
| // Complete request.
|
| var request = _pendingRequests.remove(serial);
|
| if (request == null) {
|
| @@ -221,32 +247,33 @@ abstract class CommonWebSocketVM extends VM {
|
| }
|
| }
|
|
|
| - String _generateNetworkError(String userMessage) {
|
| - return JSON.encode({
|
| + Map _generateNetworkError(String userMessage) {
|
| + var response = {
|
| 'type': 'ServiceException',
|
| - 'id': '',
|
| - 'kind': 'NetworkException',
|
| - 'message': userMessage
|
| - });
|
| + 'kind': 'ConnectionClosed',
|
| + 'message': userMessage,
|
| + };
|
| + return response;
|
| }
|
|
|
| - void _cancelRequests(Map<String, _WebSocketRequest> requests) {
|
| - requests.forEach((String serial, _WebSocketRequest request) {
|
| + void _cancelRequests(Map<String,_WebSocketRequest> requests,
|
| + String reason) {
|
| + requests.forEach((_, _WebSocketRequest request) {
|
| request.completer.complete(
|
| - _generateNetworkError('WebSocket disconnected'));
|
| + _generateNetworkError(reason));
|
| });
|
| requests.clear();
|
| }
|
|
|
| /// Cancel all pending and delayed requests by completing them with an error.
|
| - void _cancelAllRequests() {
|
| + void _cancelAllRequests(String reason) {
|
| if (_pendingRequests.length > 0) {
|
| Logger.root.info('Cancelling all pending requests.');
|
| - _cancelRequests(_pendingRequests);
|
| + _cancelRequests(_pendingRequests, reason);
|
| }
|
| if (_delayedRequests.length > 0) {
|
| Logger.root.info('Cancelling all delayed requests.');
|
| - _cancelRequests(_delayedRequests);
|
| + _cancelRequests(_delayedRequests, reason);
|
| }
|
| }
|
|
|
|
|