| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library service_common; | 5 library service_common; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:typed_data'; | 9 import 'dart:typed_data'; |
| 10 | 10 |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 109 void _notifyDisconnect(String reason) { | 109 void _notifyDisconnect(String reason) { |
| 110 if (!_hasFinishedConnect) { | 110 if (!_hasFinishedConnect) { |
| 111 return; | 111 return; |
| 112 } | 112 } |
| 113 if (!_disconnected.isCompleted) { | 113 if (!_disconnected.isCompleted) { |
| 114 Logger.root.info('WebSocketVM connection error: ${target.networkAddress}')
; | 114 Logger.root.info('WebSocketVM connection error: ${target.networkAddress}')
; |
| 115 _disconnected.complete(reason); | 115 _disconnected.complete(reason); |
| 116 } | 116 } |
| 117 } | 117 } |
| 118 Future get onDisconnect => _disconnected.future; | 118 Future get onDisconnect => _disconnected.future; |
| 119 bool get isDisconnected => _disconnected.isCompleted; |
| 119 | 120 |
| 120 void disconnect({String reason : 'WebSocket closed'}) { | 121 void disconnect({String reason : 'WebSocket closed'}) { |
| 121 if (_hasInitiatedConnect) { | 122 if (_hasInitiatedConnect) { |
| 122 _webSocket.close(); | 123 _webSocket.close(); |
| 123 } | 124 } |
| 124 // We don't need to cancel requests and notify here. These | 125 // We don't need to cancel requests and notify here. These |
| 125 // functions will be called again when the onClose callback | 126 // functions will be called again when the onClose callback |
| 126 // fires. However, we may have a better 'reason' string now, so | 127 // fires. However, we may have a better 'reason' string now, so |
| 127 // let's take care of business. | 128 // let's take care of business. |
| 128 _cancelAllRequests(reason); | 129 _cancelAllRequests(reason); |
| 129 _notifyDisconnect(reason); | 130 _notifyDisconnect(reason); |
| 130 } | 131 } |
| 131 | 132 |
| 132 Future<Map> invokeRpcRaw(String method, Map params) { | 133 Future<Map> invokeRpcRaw(String method, Map params) { |
| 133 if (!_hasInitiatedConnect) { | 134 if (!_hasInitiatedConnect) { |
| 134 _hasInitiatedConnect = true; | 135 _hasInitiatedConnect = true; |
| 135 _webSocket.connect( | 136 _webSocket.connect( |
| 136 target.networkAddress, _onOpen, _onMessage, _onError, _onClose); | 137 target.networkAddress, _onOpen, _onMessage, _onError, _onClose); |
| 137 } | 138 } |
| 139 if (_disconnected.isCompleted) { |
| 140 // This connection was closed already. |
| 141 var exception = new NetworkRpcException('WebSocket closed'); |
| 142 return new Future.error(exception); |
| 143 } |
| 138 String serial = (_requestSerial++).toString(); | 144 String serial = (_requestSerial++).toString(); |
| 139 var request = new _WebSocketRequest(method, params); | 145 var request = new _WebSocketRequest(method, params); |
| 140 if (_webSocket.isOpen) { | 146 if (_webSocket.isOpen) { |
| 141 // Already connected, send request immediately. | 147 // Already connected, send request immediately. |
| 142 _sendRequest(serial, request); | 148 _sendRequest(serial, request); |
| 143 } else { | 149 } else { |
| 144 // Not connected yet, add to delayed requests. | 150 // Not connected yet, add to delayed requests. |
| 145 _delayedRequests[serial] = request; | 151 _delayedRequests[serial] = request; |
| 146 } | 152 } |
| 147 return request.completer.future; | 153 return request.completer.future; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 166 _sendAllDelayedRequests(); | 172 _sendAllDelayedRequests(); |
| 167 _notifyConnect(); | 173 _notifyConnect(); |
| 168 } | 174 } |
| 169 | 175 |
| 170 Map _parseJSON(String message) { | 176 Map _parseJSON(String message) { |
| 171 var map; | 177 var map; |
| 172 try { | 178 try { |
| 173 map = JSON.decode(message); | 179 map = JSON.decode(message); |
| 174 } catch (e, st) { | 180 } catch (e, st) { |
| 175 Logger.root.severe('Disconnecting: Error decoding message: $e\n$st'); | 181 Logger.root.severe('Disconnecting: Error decoding message: $e\n$st'); |
| 176 disconnect(reason:'Error decoding JSON message: $e'); | 182 disconnect(reason:'Connection saw corrupt JSON message: $e'); |
| 177 return null; | 183 return null; |
| 178 } | 184 } |
| 179 if (map == null) { | 185 if (map == null) { |
| 180 Logger.root.severe("Disconnecting: Unable to decode 'null' message"); | 186 Logger.root.severe("Disconnecting: Unable to decode 'null' message"); |
| 181 disconnect(reason:"Unable to decode 'null' message"); | 187 disconnect(reason:"Connection saw 'null' message"); |
| 182 return null; | 188 return null; |
| 183 } | 189 } |
| 184 return map; | 190 return map; |
| 185 } | 191 } |
| 186 | 192 |
| 187 void _onBinaryMessage(dynamic data) { | 193 void _onBinaryMessage(dynamic data) { |
| 188 _webSocket.nonStringToByteData(data).then((ByteData bytes) { | 194 _webSocket.nonStringToByteData(data).then((ByteData bytes) { |
| 189 // See format spec. in VMs Service::SendEvent. | 195 // See format spec. in VMs Service::SendEvent. |
| 190 int offset = 0; | 196 int offset = 0; |
| 191 // Dart2JS workaround (no getUint64). Limit to 4 GB metadata. | 197 // Dart2JS workaround (no getUint64). Limit to 4 GB metadata. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 214 return; | 220 return; |
| 215 } | 221 } |
| 216 var event = map['event']; | 222 var event = map['event']; |
| 217 if (event != null) { | 223 if (event != null) { |
| 218 postServiceEvent(event, null); | 224 postServiceEvent(event, null); |
| 219 return; | 225 return; |
| 220 } | 226 } |
| 221 | 227 |
| 222 // Extract serial and result. | 228 // Extract serial and result. |
| 223 var serial = map['id']; | 229 var serial = map['id']; |
| 224 var result = map['result']; | |
| 225 | 230 |
| 226 // Complete request. | 231 // Complete request. |
| 227 var request = _pendingRequests.remove(serial); | 232 var request = _pendingRequests.remove(serial); |
| 228 if (request == null) { | 233 if (request == null) { |
| 229 Logger.root.severe('Received unexpected message: ${map}'); | 234 Logger.root.severe('Received unexpected message: ${map}'); |
| 230 return; | 235 return; |
| 231 } | 236 } |
| 232 if (request.method != 'getTagProfile' && | 237 if (request.method != 'getTagProfile' && |
| 233 request.method != 'getIsolateMetric' && | 238 request.method != 'getIsolateMetric' && |
| 234 request.method != 'getVMMetric') { | 239 request.method != 'getVMMetric') { |
| 235 Logger.root.info( | 240 Logger.root.info( |
| 236 'RESPONSE [${serial}] ${request.method}'); | 241 'RESPONSE [${serial}] ${request.method}'); |
| 237 } | 242 } |
| 238 request.completer.complete(result); | 243 |
| 244 var result = map['result']; |
| 245 if (result != null) { |
| 246 request.completer.complete(result); |
| 247 } else { |
| 248 var exception = new ServerRpcException.fromMap(map['error']); |
| 249 request.completer.completeError(exception); |
| 250 } |
| 239 } | 251 } |
| 240 | 252 |
| 241 // WebSocket message event handler. | 253 // WebSocket message event handler. |
| 242 void _onMessage(dynamic data) { | 254 void _onMessage(dynamic data) { |
| 243 if (data is! String) { | 255 if (data is! String) { |
| 244 _onBinaryMessage(data); | 256 _onBinaryMessage(data); |
| 245 } else { | 257 } else { |
| 246 _onStringMessage(data); | 258 _onStringMessage(data); |
| 247 } | 259 } |
| 248 } | 260 } |
| 249 | 261 |
| 250 Map _generateNetworkError(String userMessage) { | |
| 251 var response = { | |
| 252 'type': 'ServiceException', | |
| 253 'kind': 'ConnectionClosed', | |
| 254 'message': userMessage, | |
| 255 }; | |
| 256 return response; | |
| 257 } | |
| 258 | |
| 259 void _cancelRequests(Map<String,_WebSocketRequest> requests, | 262 void _cancelRequests(Map<String,_WebSocketRequest> requests, |
| 260 String reason) { | 263 String message) { |
| 261 requests.forEach((_, _WebSocketRequest request) { | 264 requests.forEach((_, _WebSocketRequest request) { |
| 262 request.completer.complete( | 265 var exception = new NetworkRpcException(message); |
| 263 _generateNetworkError(reason)); | 266 request.completer.completeError(exception); |
| 264 }); | 267 }); |
| 265 requests.clear(); | 268 requests.clear(); |
| 266 } | 269 } |
| 267 | 270 |
| 268 /// Cancel all pending and delayed requests by completing them with an error. | 271 /// Cancel all pending and delayed requests by completing them with an error. |
| 269 void _cancelAllRequests(String reason) { | 272 void _cancelAllRequests(String reason) { |
| 273 String message = 'Canceling request: $reason'; |
| 270 if (_pendingRequests.length > 0) { | 274 if (_pendingRequests.length > 0) { |
| 271 Logger.root.info('Cancelling all pending requests.'); | 275 Logger.root.info('Canceling all pending requests.'); |
| 272 _cancelRequests(_pendingRequests, reason); | 276 _cancelRequests(_pendingRequests, message); |
| 273 } | 277 } |
| 274 if (_delayedRequests.length > 0) { | 278 if (_delayedRequests.length > 0) { |
| 275 Logger.root.info('Cancelling all delayed requests.'); | 279 Logger.root.info('Canceling all delayed requests.'); |
| 276 _cancelRequests(_delayedRequests, reason); | 280 _cancelRequests(_delayedRequests, message); |
| 277 } | 281 } |
| 278 } | 282 } |
| 279 | 283 |
| 280 /// Send all delayed requests. | 284 /// Send all delayed requests. |
| 281 void _sendAllDelayedRequests() { | 285 void _sendAllDelayedRequests() { |
| 282 assert(_webSocket.isOpen); | 286 assert(_webSocket.isOpen); |
| 283 if (_delayedRequests.length == 0) { | 287 if (_delayedRequests.length == 0) { |
| 284 return; | 288 return; |
| 285 } | 289 } |
| 286 Logger.root.info('Sending all delayed requests.'); | 290 Logger.root.info('Sending all delayed requests.'); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 315 if (request.method != 'getTagProfile' && | 319 if (request.method != 'getTagProfile' && |
| 316 request.method != 'getIsolateMetric' && | 320 request.method != 'getIsolateMetric' && |
| 317 request.method != 'getVMMetric') { | 321 request.method != 'getVMMetric') { |
| 318 Logger.root.info( | 322 Logger.root.info( |
| 319 'GET [${serial}] ${request.method}(${request.params}) from ${target.ne
tworkAddress}'); | 323 'GET [${serial}] ${request.method}(${request.params}) from ${target.ne
tworkAddress}'); |
| 320 } | 324 } |
| 321 // Send message. | 325 // Send message. |
| 322 _webSocket.send(message); | 326 _webSocket.send(message); |
| 323 } | 327 } |
| 324 } | 328 } |
| OLD | NEW |