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

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

Issue 837723004: Build Observatory as part of runtime (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library service_common;
6
7 import 'dart:async';
8 import 'dart:convert';
9 import 'dart:typed_data';
10
11 import 'package:logging/logging.dart';
12 import 'package:observatory/service.dart';
13
14 // Export the service library.
15 export 'package:observatory/service.dart';
16
17 /// Description of a VM target.
18 class WebSocketVMTarget {
19 // Last time this VM has been connected to.
20 int lastConnectionTime = 0;
21 bool get hasEverConnected => lastConnectionTime > 0;
22
23 // Chrome VM or standalone;
24 bool chrome = false;
25 bool get standalone => !chrome;
26
27 // User defined name.
28 String name;
29 // Network address of VM.
30 String networkAddress;
31
32 WebSocketVMTarget(this.networkAddress) {
33 name = networkAddress;
34 }
35
36 WebSocketVMTarget.fromMap(Map json) {
37 lastConnectionTime = json['lastConnectionTime'];
38 chrome = json['chrome'];
39 name = json['name'];
40 networkAddress = json['networkAddress'];
41 if (name == null) {
42 name = networkAddress;
43 }
44 }
45
46 Map toJson() {
47 return {
48 'lastConnectionTime': lastConnectionTime,
49 'chrome': chrome,
50 'name': name,
51 'networkAddress': networkAddress,
52 };
53 }
54 }
55
56 class _WebSocketRequest {
57 final String id;
58 final Completer<String> completer;
59 _WebSocketRequest(this.id)
60 : completer = new Completer<String>();
61 }
62
63 /// Minimal common interface for 'WebSocket' in [dart:io] and [dart:html].
64 abstract class CommonWebSocket {
65 void connect(String address,
66 void onOpen(),
67 void onMessage(dynamic data),
68 void onError(),
69 void onClose());
70 bool get isOpen;
71 void send(dynamic data);
72 void close();
73 Future<ByteData> nonStringToByteData(dynamic data);
74 }
75
76 /// A [CommonWebSocketVM] communicates with a Dart VM over a CommonWebSocket.
77 /// The Dart VM can be embedded in Chromium or standalone. In the case of
78 /// Chromium, we make the service requests via the Chrome Remote Debugging
79 /// Protocol.
80 abstract class CommonWebSocketVM extends VM {
81 final Completer _connected = new Completer();
82 final Completer _disconnected = new Completer();
83 final WebSocketVMTarget target;
84 final Map<String, _WebSocketRequest> _delayedRequests =
85 new Map<String, _WebSocketRequest>();
86 final Map<String, _WebSocketRequest> _pendingRequests =
87 new Map<String, _WebSocketRequest>();
88 int _requestSerial = 0;
89 bool _hasInitiatedConnect = false;
90 bool _hasFinishedConnect = false;
91 Utf8Decoder _utf8Decoder = new Utf8Decoder();
92
93 CommonWebSocket _webSocket;
94
95 CommonWebSocketVM(this.target, this._webSocket) {
96 assert(target != null);
97 }
98
99 void _notifyConnect() {
100 _hasFinishedConnect = true;
101 if (!_connected.isCompleted) {
102 Logger.root.info('WebSocketVM connection opened: ${target.networkAddress}' );
103 _connected.complete(this);
104 }
105 }
106 Future get onConnect => _connected.future;
107 void _notifyDisconnect() {
108 if (!_hasFinishedConnect) {
109 return;
110 }
111 if (!_disconnected.isCompleted) {
112 Logger.root.info('WebSocketVM connection error: ${target.networkAddress}') ;
113 _disconnected.complete(this);
114 }
115 }
116 Future get onDisconnect => _disconnected.future;
117
118 void disconnect() {
119 if (_hasInitiatedConnect) {
120 _webSocket.close();
121 }
122 _cancelAllRequests();
123 _notifyDisconnect();
124 }
125
126 Future<String> getString(String id) {
127 if (!_hasInitiatedConnect) {
128 _hasInitiatedConnect = true;
129 _webSocket.connect(
130 target.networkAddress, _onOpen, _onMessage, _onError, _onClose);
131 }
132 return _makeRequest(id);
133 }
134
135 /// Add a request for [id] to pending requests.
136 Future<String> _makeRequest(String id) {
137 assert(_hasInitiatedConnect);
138 // Create request.
139 String serial = (_requestSerial++).toString();
140 var request = new _WebSocketRequest(id);
141 if (_webSocket.isOpen) {
142 // Already connected, send request immediately.
143 _sendRequest(serial, request);
144 } else {
145 // Not connected yet, add to delayed requests.
146 _delayedRequests[serial] = request;
147 }
148 return request.completer.future;
149 }
150
151 void _onClose() {
152 _cancelAllRequests();
153 _notifyDisconnect();
154 }
155
156 // WebSocket error event handler.
157 void _onError() {
158 _cancelAllRequests();
159 _notifyDisconnect();
160 }
161
162 // WebSocket open event handler.
163 void _onOpen() {
164 target.lastConnectionTime = new DateTime.now().millisecondsSinceEpoch;
165 _sendAllDelayedRequests();
166 _notifyConnect();
167 }
168
169 // WebSocket message event handler.
170 void _onMessage(dynamic data) {
171 if (data is! String) {
172 _webSocket.nonStringToByteData(data).then((ByteData bytes) {
173 // See format spec. in VMs Service::SendEvent.
174 int offset = 0;
175 int metaSize = bytes.getUint64(offset, Endianness.BIG_ENDIAN);
176 offset += 8;
177 var meta = _utf8Decoder.convert(new Uint8List.view(
178 bytes.buffer, bytes.offsetInBytes + offset, metaSize));
179 offset += metaSize;
180 var data = new ByteData.view(
181 bytes.buffer,
182 bytes.offsetInBytes + offset,
183 bytes.lengthInBytes - offset);
184 postEventMessage(meta, data);
185 });
186 return;
187 }
188 var map = JSON.decode(data);
189 if (map == null) {
190 Logger.root.severe('WebSocketVM got empty message');
191 return;
192 }
193 // Extract serial and response.
194 var serial;
195 var response;
196 if (target.chrome) {
197 if (map['method'] != 'Dart.observatoryData') {
198 // ignore devtools protocol spam.
199 return;
200 }
201 serial = map['params']['id'].toString();
202 response = map['params']['data'];
203 } else {
204 serial = map['seq'];
205 response = map['response'];
206 }
207 if (serial == null) {
208 // Messages without sequence numbers are asynchronous events
209 // from the vm.
210 postEventMessage(response);
211 return;
212 }
213 // Complete request.
214 var request = _pendingRequests.remove(serial);
215 if (request == null) {
216 Logger.root.severe('Received unexpected message: ${map}');
217 return;
218 }
219 request.completer.complete(response);
220 }
221
222 String _generateNetworkError(String userMessage) {
223 return JSON.encode({
224 'type': 'ServiceException',
225 'id': '',
226 'kind': 'NetworkException',
227 'message': userMessage
228 });
229 }
230
231 void _cancelRequests(Map<String, _WebSocketRequest> requests) {
232 requests.forEach((String serial, _WebSocketRequest request) {
233 request.completer.complete(
234 _generateNetworkError('WebSocket disconnected'));
235 });
236 requests.clear();
237 }
238
239 /// Cancel all pending and delayed requests by completing them with an error.
240 void _cancelAllRequests() {
241 if (_pendingRequests.length > 0) {
242 Logger.root.info('Cancelling all pending requests.');
243 _cancelRequests(_pendingRequests);
244 }
245 if (_delayedRequests.length > 0) {
246 Logger.root.info('Cancelling all delayed requests.');
247 _cancelRequests(_delayedRequests);
248 }
249 }
250
251 /// Send all delayed requests.
252 void _sendAllDelayedRequests() {
253 assert(_webSocket.isOpen);
254 if (_delayedRequests.length == 0) {
255 return;
256 }
257 Logger.root.info('Sending all delayed requests.');
258 // Send all delayed requests.
259 _delayedRequests.forEach(_sendRequest);
260 // Clear all delayed requests.
261 _delayedRequests.clear();
262 }
263
264 /// Send the request over WebSocket.
265 void _sendRequest(String serial, _WebSocketRequest request) {
266 assert (_webSocket.isOpen);
267 if (!request.id.endsWith('/profile/tag')) {
268 Logger.root.info('GET ${request.id} from ${target.networkAddress}');
269 }
270 // Mark request as pending.
271 assert(_pendingRequests.containsKey(serial) == false);
272 _pendingRequests[serial] = request;
273 var message;
274 // Encode message.
275 if (target.chrome) {
276 message = JSON.encode({
277 'id': int.parse(serial),
278 'method': 'Dart.observatoryQuery',
279 'params': {
280 'id': serial,
281 'query': request.id
282 }
283 });
284 } else {
285 message = JSON.encode({'seq': serial, 'request': request.id});
286 }
287 // Send message.
288 _webSocket.send(message);
289 }
290 }
OLDNEW
« no previous file with comments | « runtime/bin/vmservice/observatory/lib/service.dart ('k') | runtime/bin/vmservice/observatory/lib/service_html.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698