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 |