| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 part of observatory; | |
| 6 | |
| 7 /// A request response interceptor is called for each response. | |
| 8 typedef void RequestResponseInterceptor(); | |
| 9 | |
| 10 abstract class RequestManager extends Observable { | |
| 11 ObservatoryApplication _application; | |
| 12 ObservatoryApplication get application => _application; | |
| 13 RequestResponseInterceptor interceptor; | |
| 14 | |
| 15 /// The default request prefix is 127.0.0.1 on port 8181. | |
| 16 @observable String prefix = 'http://127.0.0.1:8181'; | |
| 17 /// List of responses. | |
| 18 @observable List<Map> responses = toObservable([]); | |
| 19 | |
| 20 /// Decode [response] into a map. | |
| 21 Map decodeResponse(String response) { | |
| 22 var m; | |
| 23 try { | |
| 24 m = JSON.decode(response); | |
| 25 } catch (e, st) { | |
| 26 setResponseError('$e $st'); | |
| 27 }; | |
| 28 return m; | |
| 29 } | |
| 30 | |
| 31 /// Parse | |
| 32 void parseResponses(String responseString) { | |
| 33 var r = decodeResponse(responseString); | |
| 34 if (r == null) { | |
| 35 return; | |
| 36 } | |
| 37 if (r is Map) { | |
| 38 setResponses([r]); | |
| 39 } else { | |
| 40 setResponses(r); | |
| 41 } | |
| 42 } | |
| 43 | |
| 44 void setResponses(List<Map> r) { | |
| 45 responses = toObservable(r); | |
| 46 if (interceptor != null) { | |
| 47 interceptor(); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 void setResponseRequestError(HttpRequest request) { | |
| 52 String error = '${request.status} ${request.statusText}'; | |
| 53 if (request.status == 0) { | |
| 54 error = 'No service found. Did you run with --enable-vm-service ?'; | |
| 55 } | |
| 56 setResponses([{ | |
| 57 'type': 'Error', | |
| 58 'errorType': 'RequestError', | |
| 59 'text': error | |
| 60 }]); | |
| 61 } | |
| 62 | |
| 63 void setResponseError(String message) { | |
| 64 setResponses([{ | |
| 65 'type': 'Error', | |
| 66 'errorType': 'ResponseError', | |
| 67 'text': message | |
| 68 }]); | |
| 69 Logger.root.severe(message); | |
| 70 } | |
| 71 | |
| 72 static final RegExp _codeMatcher = new RegExp(r'/isolates/\d+/code/'); | |
| 73 static bool isCodeRequest(url) => _codeMatcher.hasMatch(url); | |
| 74 static int codeAddressFromRequest(String url) { | |
| 75 Match m = _codeMatcher.matchAsPrefix(url); | |
| 76 if (m == null) { | |
| 77 return 0; | |
| 78 } | |
| 79 try { | |
| 80 var a = int.parse(m.input.substring(m.end), radix: 16); | |
| 81 return a; | |
| 82 } catch (e) { | |
| 83 return 0; | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 static final RegExp _isolateMatcher = new RegExp(r"/isolates/\d+"); | |
| 88 static String isolatePrefixFromRequest(String url) { | |
| 89 Match m = _isolateMatcher.matchAsPrefix(url); | |
| 90 if (m == null) { | |
| 91 return null; | |
| 92 } | |
| 93 return m.input.substring(m.start, m.end); | |
| 94 } | |
| 95 | |
| 96 static String isolateIdFromRequest(String url) { | |
| 97 var prefix = isolatePrefixFromRequest(url); | |
| 98 if (prefix == null) { | |
| 99 return null; | |
| 100 } | |
| 101 // Chop off the '/'. | |
| 102 return prefix.substring(1); | |
| 103 } | |
| 104 | |
| 105 static final RegExp _scriptMatcher = new RegExp(r'/isolates/\d+/scripts/.+'); | |
| 106 static bool isScriptRequest(url) => _scriptMatcher.hasMatch(url); | |
| 107 static final RegExp _scriptPrefixMatcher = | |
| 108 new RegExp(r'/isolates/\d+/'); | |
| 109 static String scriptUrlFromRequest(String url) { | |
| 110 var m = _scriptPrefixMatcher.matchAsPrefix(url); | |
| 111 if (m == null) { | |
| 112 return null; | |
| 113 } | |
| 114 return m.input.substring(m.end); | |
| 115 } | |
| 116 | |
| 117 void _setModelResponse(String type, String modelName, dynamic model) { | |
| 118 var response = { | |
| 119 'type': type, | |
| 120 modelName: model | |
| 121 }; | |
| 122 setResponses([response]); | |
| 123 } | |
| 124 | |
| 125 /// Handle 'Code' requests | |
| 126 void _getCode(String requestString) { | |
| 127 var isolateId = isolateIdFromRequest(requestString); | |
| 128 if (isolateId == null) { | |
| 129 setResponseError('$isolateId is not an isolate id.'); | |
| 130 return; | |
| 131 } | |
| 132 var isolate = _application.isolateManager.getIsolate(isolateId); | |
| 133 if (isolate == null) { | |
| 134 setResponseError('$isolateId could not be found.'); | |
| 135 return; | |
| 136 } | |
| 137 var address = codeAddressFromRequest(requestString); | |
| 138 if (address == 0) { | |
| 139 setResponseError('$requestString is not a valid code request.'); | |
| 140 return; | |
| 141 } | |
| 142 var code = isolate.findCodeByAddress(address); | |
| 143 if (code != null) { | |
| 144 Logger.root.info( | |
| 145 'Found code with 0x${address.toRadixString(16)} in isolate.'); | |
| 146 _setModelResponse('Code', 'code', code); | |
| 147 return; | |
| 148 } | |
| 149 request(requestString).then((responseString) { | |
| 150 var map = decodeResponse(responseString); | |
| 151 if (map == null) { | |
| 152 return; | |
| 153 } | |
| 154 assert(map['type'] == 'Code'); | |
| 155 var code = new Code.fromMap(map); | |
| 156 Logger.root.info( | |
| 157 'Added code with 0x${address.toRadixString(16)} to isolate.'); | |
| 158 isolate.codes.add(code); | |
| 159 _setModelResponse('Code', 'code', code); | |
| 160 }).catchError(_requestCatchError); | |
| 161 } | |
| 162 | |
| 163 void _getScript(String requestString) { | |
| 164 var isolateId = isolateIdFromRequest(requestString); | |
| 165 if (isolateId == null) { | |
| 166 setResponseError('$isolateId is not an isolate id.'); | |
| 167 return; | |
| 168 } | |
| 169 var isolate = _application.isolateManager.getIsolate(isolateId); | |
| 170 if (isolate == null) { | |
| 171 setResponseError('$isolateId could not be found.'); | |
| 172 return; | |
| 173 } | |
| 174 var url = scriptUrlFromRequest(requestString); | |
| 175 if (url == null) { | |
| 176 setResponseError('$requestString is not a valid script request.'); | |
| 177 return; | |
| 178 } | |
| 179 var script = isolate.scripts[url]; | |
| 180 if ((script != null) && !script.needsSource) { | |
| 181 Logger.root.info('Found script ${script.scriptRef['name']} in isolate'); | |
| 182 _setModelResponse('Script', 'script', script); | |
| 183 return; | |
| 184 } | |
| 185 if (script != null) { | |
| 186 // The isolate has the script but no script source code. | |
| 187 requestMap(requestString).then((response) { | |
| 188 assert(response['type'] == 'Script'); | |
| 189 script._processSource(response['source']); | |
| 190 Logger.root.info( | |
| 191 'Grabbed script ${script.scriptRef['name']} source.'); | |
| 192 _setModelResponse('Script', 'script', script); | |
| 193 }); | |
| 194 return; | |
| 195 } | |
| 196 // New script. | |
| 197 requestMap(requestString).then((response) { | |
| 198 assert(response['type'] == 'Script'); | |
| 199 var script = new Script.fromMap(response); | |
| 200 Logger.root.info( | |
| 201 'Added script ${script.scriptRef['name']} to isolate.'); | |
| 202 _setModelResponse('Script', 'script', script); | |
| 203 isolate.scripts[url] = script; | |
| 204 }); | |
| 205 } | |
| 206 | |
| 207 void _requestCatchError(e, st) { | |
| 208 if (e is ProgressEvent) { | |
| 209 setResponseRequestError(e.target); | |
| 210 } else { | |
| 211 setResponseError('$e $st'); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 /// Request [request] from the VM service. Updates [responses]. | |
| 216 /// Will trigger [interceptor] if one is set. | |
| 217 void get(String requestString) { | |
| 218 if (isCodeRequest(requestString)) { | |
| 219 _getCode(requestString); | |
| 220 return; | |
| 221 } | |
| 222 if (isScriptRequest(requestString)) { | |
| 223 _getScript(requestString); | |
| 224 return; | |
| 225 } | |
| 226 request(requestString).then((responseString) { | |
| 227 parseResponses(responseString); | |
| 228 }).catchError(_requestCatchError); | |
| 229 } | |
| 230 | |
| 231 /// Abstract method. Given the [requestString], return a String in the | |
| 232 /// future which contains the reply from the VM service. | |
| 233 Future<String> request(String requestString); | |
| 234 | |
| 235 Future<ObservableMap> requestMap(String requestString) { | |
| 236 if (requestString.startsWith('#')) { | |
| 237 requestString = requestString.substring(1); | |
| 238 } | |
| 239 return request(requestString).then((response) { | |
| 240 try { | |
| 241 var m = JSON.decode(response); | |
| 242 return toObservable(m); | |
| 243 } catch (e) { } | |
| 244 return null; | |
| 245 }); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 | |
| 250 class HttpRequestManager extends RequestManager { | |
| 251 Future<String> request(String requestString) { | |
| 252 Logger.root.info('Requesting $requestString'); | |
| 253 return HttpRequest.getString(prefix + requestString); | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 class PostMessageRequestManager extends RequestManager { | |
| 258 final Map _outstandingRequests = new Map(); | |
| 259 int _requestSerial = 0; | |
| 260 PostMessageRequestManager() { | |
| 261 window.onMessage.listen(_messageHandler); | |
| 262 } | |
| 263 | |
| 264 void _messageHandler(msg) { | |
| 265 var id = msg.data['id']; | |
| 266 var name = msg.data['name']; | |
| 267 var data = msg.data['data']; | |
| 268 if (name != 'observatoryData') { | |
| 269 return; | |
| 270 } | |
| 271 var completer = _outstandingRequests[id]; | |
| 272 if (completer != null) { | |
| 273 _outstandingRequests.remove(id); | |
| 274 print('Completing $id'); | |
| 275 completer.complete(data); | |
| 276 } else { | |
| 277 print('Could not find completer for $id'); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 Future<String> request(String requestString) { | |
| 282 var idString = '$_requestSerial'; | |
| 283 Map message = {}; | |
| 284 message['id'] = idString; | |
| 285 message['method'] = 'observatoryQuery'; | |
| 286 message['query'] = requestString; | |
| 287 _requestSerial++; | |
| 288 | |
| 289 var completer = new Completer(); | |
| 290 _outstandingRequests[idString] = completer; | |
| 291 | |
| 292 window.parent.postMessage(JSON.encode(message), '*'); | |
| 293 return completer.future; | |
| 294 } | |
| 295 } | |
| OLD | NEW |