Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(79)

Side by Side Diff: runtime/observatory/lib/service_common.dart

Issue 1093043004: Do not JSON encode the 'result' of a service rpc. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: edits Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
49 'chrome': chrome, 49 'chrome': chrome,
50 'name': name, 50 'name': name,
51 'networkAddress': networkAddress, 51 'networkAddress': networkAddress,
52 }; 52 };
53 } 53 }
54 } 54 }
55 55
56 class _WebSocketRequest { 56 class _WebSocketRequest {
57 final String method; 57 final String method;
58 final Map params; 58 final Map params;
59 final Completer<String> completer; 59 final Completer<Map> completer;
60 60
61 _WebSocketRequest(this.method, this.params) 61 _WebSocketRequest(this.method, this.params)
62 : completer = new Completer<String>(); 62 : completer = new Completer<Map>();
63 } 63 }
64 64
65 /// Minimal common interface for 'WebSocket' in [dart:io] and [dart:html]. 65 /// Minimal common interface for 'WebSocket' in [dart:io] and [dart:html].
66 abstract class CommonWebSocket { 66 abstract class CommonWebSocket {
67 void connect(String address, 67 void connect(String address,
68 void onOpen(), 68 void onOpen(),
69 void onMessage(dynamic data), 69 void onMessage(dynamic data),
70 void onError(), 70 void onError(),
71 void onClose()); 71 void onClose());
72 bool get isOpen; 72 bool get isOpen;
73 void send(dynamic data); 73 void send(dynamic data);
74 void close(); 74 void close();
75 Future<ByteData> nonStringToByteData(dynamic data); 75 Future<ByteData> nonStringToByteData(dynamic data);
76 } 76 }
77 77
78 /// A [CommonWebSocketVM] communicates with a Dart VM over a CommonWebSocket. 78 /// A [CommonWebSocketVM] communicates with a Dart VM over a CommonWebSocket.
79 /// The Dart VM can be embedded in Chromium or standalone. In the case of 79 /// The Dart VM can be embedded in Chromium or standalone. In the case of
80 /// Chromium, we make the service requests via the Chrome Remote Debugging 80 /// Chromium, we make the service requests via the Chrome Remote Debugging
81 /// Protocol. 81 /// Protocol.
82 abstract class CommonWebSocketVM extends VM { 82 abstract class CommonWebSocketVM extends VM {
83 final Completer _connected = new Completer(); 83 final Completer _connected = new Completer();
84 final Completer _disconnected = new Completer(); 84 final Completer _disconnected = new Completer<String>();
85 final WebSocketVMTarget target; 85 final WebSocketVMTarget target;
86 final Map<String, _WebSocketRequest> _delayedRequests = 86 final Map<String, _WebSocketRequest> _delayedRequests =
87 new Map<String, _WebSocketRequest>(); 87 new Map<String, _WebSocketRequest>();
88 final Map<String, _WebSocketRequest> _pendingRequests = 88 final Map<String, _WebSocketRequest> _pendingRequests =
89 new Map<String, _WebSocketRequest>(); 89 new Map<String, _WebSocketRequest>();
90 int _requestSerial = 0; 90 int _requestSerial = 0;
91 bool _hasInitiatedConnect = false; 91 bool _hasInitiatedConnect = false;
92 bool _hasFinishedConnect = false; 92 bool _hasFinishedConnect = false;
93 Utf8Decoder _utf8Decoder = new Utf8Decoder(); 93 Utf8Decoder _utf8Decoder = new Utf8Decoder();
94 94
95 CommonWebSocket _webSocket; 95 CommonWebSocket _webSocket;
96 96
97 CommonWebSocketVM(this.target, this._webSocket) { 97 CommonWebSocketVM(this.target, this._webSocket) {
98 assert(target != null); 98 assert(target != null);
99 } 99 }
100 100
101 void _notifyConnect() { 101 void _notifyConnect() {
102 _hasFinishedConnect = true; 102 _hasFinishedConnect = true;
103 if (!_connected.isCompleted) { 103 if (!_connected.isCompleted) {
104 Logger.root.info('WebSocketVM connection opened: ${target.networkAddress}' ); 104 Logger.root.info('WebSocketVM connection opened: ${target.networkAddress}' );
105 _connected.complete(this); 105 _connected.complete(this);
106 } 106 }
107 } 107 }
108 Future get onConnect => _connected.future; 108 Future get onConnect => _connected.future;
109 void _notifyDisconnect() { 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(this); 115 _disconnected.complete(reason);
116 } 116 }
117 } 117 }
118 Future get onDisconnect => _disconnected.future; 118 Future get onDisconnect => _disconnected.future;
119 119
120 void disconnect() { 120 void disconnect({String reason : 'WebSocket closed'}) {
121 if (_hasInitiatedConnect) { 121 if (_hasInitiatedConnect) {
122 _webSocket.close(); 122 _webSocket.close();
123 } 123 }
124 _cancelAllRequests(); 124 // We don't need to cancel requests and notify here. These
125 _notifyDisconnect(); 125 // functions will be called again when the onClose callback
126 // fires. However, we may have a better 'reason' string now, so
127 // let's take care of business.
128 _cancelAllRequests(reason);
129 _notifyDisconnect(reason);
126 } 130 }
127 131
128 Future<String> invokeRpcRaw(String method, Map params) { 132 Future<Map> invokeRpcRaw(String method, Map params) {
129 if (!_hasInitiatedConnect) { 133 if (!_hasInitiatedConnect) {
130 _hasInitiatedConnect = true; 134 _hasInitiatedConnect = true;
131 _webSocket.connect( 135 _webSocket.connect(
132 target.networkAddress, _onOpen, _onMessage, _onError, _onClose); 136 target.networkAddress, _onOpen, _onMessage, _onError, _onClose);
133 } 137 }
134 String serial = (_requestSerial++).toString(); 138 String serial = (_requestSerial++).toString();
135 var request = new _WebSocketRequest(method, params); 139 var request = new _WebSocketRequest(method, params);
136 if (_webSocket.isOpen) { 140 if (_webSocket.isOpen) {
137 // Already connected, send request immediately. 141 // Already connected, send request immediately.
138 _sendRequest(serial, request); 142 _sendRequest(serial, request);
139 } else { 143 } else {
140 // Not connected yet, add to delayed requests. 144 // Not connected yet, add to delayed requests.
141 _delayedRequests[serial] = request; 145 _delayedRequests[serial] = request;
142 } 146 }
143 return request.completer.future; 147 return request.completer.future;
144 } 148 }
145 149
146 void _onClose() { 150 void _onClose() {
147 _cancelAllRequests(); 151 _cancelAllRequests('WebSocket closed');
148 _notifyDisconnect(); 152 _notifyDisconnect('WebSocket closed');
149 } 153 }
150 154
151 // WebSocket error event handler. 155 // WebSocket error event handler.
152 void _onError() { 156 void _onError() {
153 _cancelAllRequests(); 157 // TODO(turnidge): The implementors of CommonWebSocket have more
154 _notifyDisconnect(); 158 // error information available. Consider providing that here.
159 _cancelAllRequests('WebSocket closed due to error');
160 _notifyDisconnect('WebSocket closed due to error');
155 } 161 }
156 162
157 // WebSocket open event handler. 163 // WebSocket open event handler.
158 void _onOpen() { 164 void _onOpen() {
159 target.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch; 165 target.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch;
160 _sendAllDelayedRequests(); 166 _sendAllDelayedRequests();
161 _notifyConnect(); 167 _notifyConnect();
162 } 168 }
163 169
170 Map _parseJSON(String message) {
171 var map;
172 try {
173 map = JSON.decode(message);
174 } catch (e, st) {
175 Logger.root.severe('Disconnecting: Error decoding message: $e\n$st');
176 disconnect(reason:'Error decoding JSON message: $e');
177 return null;
178 }
179 if (map == null) {
180 Logger.root.severe("Disconnecting: Unable to decode 'null' message");
181 disconnect(reason:"Unable to decode 'null' message");
182 return null;
183 }
184 return map;
185 }
186
164 void _onBinaryMessage(dynamic data) { 187 void _onBinaryMessage(dynamic data) {
165 _webSocket.nonStringToByteData(data).then((ByteData bytes) { 188 _webSocket.nonStringToByteData(data).then((ByteData bytes) {
166 // See format spec. in VMs Service::SendEvent. 189 // See format spec. in VMs Service::SendEvent.
167 int offset = 0; 190 int offset = 0;
168 // Dart2JS workaround (no getUint64). Limit to 4 GB metadata. 191 // Dart2JS workaround (no getUint64). Limit to 4 GB metadata.
169 assert(bytes.getUint32(offset, Endianness.BIG_ENDIAN) == 0); 192 assert(bytes.getUint32(offset, Endianness.BIG_ENDIAN) == 0);
170 int metaSize = bytes.getUint32(offset + 4, Endianness.BIG_ENDIAN); 193 int metaSize = bytes.getUint32(offset + 4, Endianness.BIG_ENDIAN);
171 offset += 8; 194 offset += 8;
172 var meta = _utf8Decoder.convert(new Uint8List.view( 195 var meta = _utf8Decoder.convert(new Uint8List.view(
173 bytes.buffer, bytes.offsetInBytes + offset, metaSize)); 196 bytes.buffer, bytes.offsetInBytes + offset, metaSize));
174 offset += metaSize; 197 offset += metaSize;
175 var data = new ByteData.view( 198 var data = new ByteData.view(
176 bytes.buffer, 199 bytes.buffer,
177 bytes.offsetInBytes + offset, 200 bytes.offsetInBytes + offset,
178 bytes.lengthInBytes - offset); 201 bytes.lengthInBytes - offset);
179 postServiceEvent(meta, data); 202 var map = _parseJSON(meta);
203 if (map == null) {
204 return;
205 }
206 var event = map['event'];
207 postServiceEvent(event, data);
180 }); 208 });
181 } 209 }
182 210
183 void _onStringMessage(String data) { 211 void _onStringMessage(String data) {
184 var map = JSON.decode(data); 212 var map = _parseJSON(data);
185 if (map == null) { 213 if (map == null) {
186 Logger.root.severe('WebSocketVM got empty message');
187 return; 214 return;
188 } 215 }
189 // Extract serial and result. 216 var event = map['event'];
190 var serial; 217 if (event != null) {
191 var result; 218 postServiceEvent(event, null);
192 serial = map['id'];
193 result = map['result'];
194 if (serial == null) {
195 // Messages without serial numbers are asynchronous events
196 // from the vm.
197 postServiceEvent(result, null);
198 return; 219 return;
199 } 220 }
221
222 // Extract serial and result.
223 var serial = map['id'];
224 var result = map['result'];
225
200 // Complete request. 226 // Complete request.
201 var request = _pendingRequests.remove(serial); 227 var request = _pendingRequests.remove(serial);
202 if (request == null) { 228 if (request == null) {
203 Logger.root.severe('Received unexpected message: ${map}'); 229 Logger.root.severe('Received unexpected message: ${map}');
204 return; 230 return;
205 } 231 }
206 if (request.method != 'getTagProfile' && 232 if (request.method != 'getTagProfile' &&
207 request.method != 'getIsolateMetric' && 233 request.method != 'getIsolateMetric' &&
208 request.method != 'getVMMetric') { 234 request.method != 'getVMMetric') {
209 Logger.root.info( 235 Logger.root.info(
210 'RESPONSE [${serial}] ${request.method}'); 236 'RESPONSE [${serial}] ${request.method}');
211 } 237 }
212 request.completer.complete(result); 238 request.completer.complete(result);
213 } 239 }
214 240
215 // WebSocket message event handler. 241 // WebSocket message event handler.
216 void _onMessage(dynamic data) { 242 void _onMessage(dynamic data) {
217 if (data is! String) { 243 if (data is! String) {
218 _onBinaryMessage(data); 244 _onBinaryMessage(data);
219 } else { 245 } else {
220 _onStringMessage(data); 246 _onStringMessage(data);
221 } 247 }
222 } 248 }
223 249
224 String _generateNetworkError(String userMessage) { 250 Map _generateNetworkError(String userMessage) {
225 return JSON.encode({ 251 var response = {
226 'type': 'ServiceException', 252 'type': 'ServiceException',
227 'id': '', 253 'kind': 'ConnectionClosed',
228 'kind': 'NetworkException', 254 'message': userMessage,
229 'message': userMessage 255 };
230 }); 256 return response;
231 } 257 }
232 258
233 void _cancelRequests(Map<String, _WebSocketRequest> requests) { 259 void _cancelRequests(Map<String,_WebSocketRequest> requests,
234 requests.forEach((String serial, _WebSocketRequest request) { 260 String reason) {
261 requests.forEach((_, _WebSocketRequest request) {
235 request.completer.complete( 262 request.completer.complete(
236 _generateNetworkError('WebSocket disconnected')); 263 _generateNetworkError(reason));
237 }); 264 });
238 requests.clear(); 265 requests.clear();
239 } 266 }
240 267
241 /// Cancel all pending and delayed requests by completing them with an error. 268 /// Cancel all pending and delayed requests by completing them with an error.
242 void _cancelAllRequests() { 269 void _cancelAllRequests(String reason) {
243 if (_pendingRequests.length > 0) { 270 if (_pendingRequests.length > 0) {
244 Logger.root.info('Cancelling all pending requests.'); 271 Logger.root.info('Cancelling all pending requests.');
245 _cancelRequests(_pendingRequests); 272 _cancelRequests(_pendingRequests, reason);
246 } 273 }
247 if (_delayedRequests.length > 0) { 274 if (_delayedRequests.length > 0) {
248 Logger.root.info('Cancelling all delayed requests.'); 275 Logger.root.info('Cancelling all delayed requests.');
249 _cancelRequests(_delayedRequests); 276 _cancelRequests(_delayedRequests, reason);
250 } 277 }
251 } 278 }
252 279
253 /// Send all delayed requests. 280 /// Send all delayed requests.
254 void _sendAllDelayedRequests() { 281 void _sendAllDelayedRequests() {
255 assert(_webSocket.isOpen); 282 assert(_webSocket.isOpen);
256 if (_delayedRequests.length == 0) { 283 if (_delayedRequests.length == 0) {
257 return; 284 return;
258 } 285 }
259 Logger.root.info('Sending all delayed requests.'); 286 Logger.root.info('Sending all delayed requests.');
(...skipping 28 matching lines...) Expand all
288 if (request.method != 'getTagProfile' && 315 if (request.method != 'getTagProfile' &&
289 request.method != 'getIsolateMetric' && 316 request.method != 'getIsolateMetric' &&
290 request.method != 'getVMMetric') { 317 request.method != 'getVMMetric') {
291 Logger.root.info( 318 Logger.root.info(
292 'GET [${serial}] ${request.method} from ${target.networkAddress}'); 319 'GET [${serial}] ${request.method} from ${target.networkAddress}');
293 } 320 }
294 // Send message. 321 // Send message.
295 _webSocket.send(message); 322 _webSocket.send(message);
296 } 323 }
297 } 324 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698