| OLD | NEW |
| (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 observatory_server; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:convert'; | |
| 9 import 'dart:io'; | |
| 10 | |
| 11 import 'package:args/args.dart'; | |
| 12 import 'package:logging/logging.dart'; | |
| 13 | |
| 14 final Logger logger = new Logger('ObsServe'); | |
| 15 | |
| 16 class ObservatoryServer { | |
| 17 static const _CHROME_PREFIX = '/crdptargets/'; | |
| 18 | |
| 19 // Is logging enabled? | |
| 20 bool log; | |
| 21 | |
| 22 // Host to listen on. | |
| 23 String host; | |
| 24 // Port to listen on. | |
| 25 int port; | |
| 26 | |
| 27 // Host that pub is listening on. | |
| 28 String pubHost; | |
| 29 // Port that pub is listening on. | |
| 30 int pubPort; | |
| 31 | |
| 32 HttpServer _server; | |
| 33 final HttpClient _client = new HttpClient(); | |
| 34 | |
| 35 ObservatoryServer(List<String> args) { | |
| 36 var parser = new ArgParser(); | |
| 37 parser.addFlag('log', help: 'Log activity.', defaultsTo: true); | |
| 38 parser.addOption('port', help: 'Specify port listen on', | |
| 39 defaultsTo: '9090'); | |
| 40 parser.addOption('host', | |
| 41 help: 'Specify host to listen on', | |
| 42 defaultsTo: '127.0.0.1'); | |
| 43 parser.addOption('pub-port', help: 'Specify port that pub is listening on', | |
| 44 defaultsTo: '9191'); | |
| 45 parser.addOption('pub-host', help: 'Specify host that pub is listening on', | |
| 46 defaultsTo: '127.0.0.1'); | |
| 47 var results = parser.parse(args); | |
| 48 host = results['host']; | |
| 49 port = int.parse(results['port']); | |
| 50 log = results['log']; | |
| 51 pubHost = results['pub-host']; | |
| 52 pubPort = int.parse(results['pub-port']); | |
| 53 } | |
| 54 | |
| 55 List<Map> _makeTargetList(List<Map> tabs) { | |
| 56 var r = <Map>[]; | |
| 57 tabs.forEach((tab) { | |
| 58 var uri = Uri.parse(tab['url']); | |
| 59 if (uri.host == 'devtools') { | |
| 60 // Ignore. | |
| 61 return; | |
| 62 } | |
| 63 var target = { | |
| 64 'lastConnectionTime': 0, | |
| 65 'chrome': true, | |
| 66 'name': tab['title'], | |
| 67 'networkAddress': tab['webSocketDebuggerUrl'], | |
| 68 }; | |
| 69 r.add(target); | |
| 70 }); | |
| 71 return r; | |
| 72 } | |
| 73 | |
| 74 void _getChromeTabs(HttpRequest request) { | |
| 75 var path = request.uri.path; | |
| 76 var method = request.method; | |
| 77 if (method != 'GET') { | |
| 78 return; | |
| 79 } | |
| 80 assert(path.startsWith(_CHROME_PREFIX)); | |
| 81 var networkAddress = path.substring(_CHROME_PREFIX.length); | |
| 82 if ((networkAddress == '') || (networkAddress == null)) { | |
| 83 request.response.write('[]'); | |
| 84 request.response.close(); | |
| 85 return; | |
| 86 } | |
| 87 networkAddress = Uri.decodeComponent(networkAddress); | |
| 88 var chunks = networkAddress.split(':'); | |
| 89 var chromeAddress = chunks[0]; | |
| 90 var chromePort = | |
| 91 (chunks[1] == null) || (chunks[1] == '') ? 9222 : int.parse(chunks[1]); | |
| 92 logger.info('tabs from $chromeAddress:$chromePort'); | |
| 93 _client.open(method, chromeAddress, chromePort, 'json') | |
| 94 .then((HttpClientRequest pubRequest) { | |
| 95 // Calling .close() on an HttpClientRequest sends the request to the | |
| 96 // server. The future completes to an HttpClientResponse when the | |
| 97 // server has responded. | |
| 98 return pubRequest.close(); | |
| 99 }).then((HttpClientResponse response) { | |
| 100 var respond = (contents) { | |
| 101 var tabs = JSON.decode(contents); | |
| 102 var targets = _makeTargetList(tabs); | |
| 103 request.response.write(JSON.encode(targets)); | |
| 104 request.response.close().catchError((e) { | |
| 105 logger.severe('tabs from $chromeAddress:$chromePort failed'); | |
| 106 logger.severe(e.toString()); | |
| 107 }); | |
| 108 }; | |
| 109 response.transform(UTF8.decoder).listen(respond); | |
| 110 }).catchError((e) { | |
| 111 logger.severe('tabs from $chromeAddress:$chromePort failed'); | |
| 112 logger.severe(e.toString()); | |
| 113 }); | |
| 114 } | |
| 115 | |
| 116 /// Forward [request] to pub. | |
| 117 void _forwardToPub(HttpRequest request) { | |
| 118 var path = request.uri.path; | |
| 119 var method = request.method; | |
| 120 logger.info('pub $method $path'); | |
| 121 _client.open(method, pubHost, pubPort, path) | |
| 122 .then((HttpClientRequest pubRequest) { | |
| 123 return pubRequest.close(); | |
| 124 }).then((HttpClientResponse response) { | |
| 125 return request.response.addStream(response); | |
| 126 }).then((_) => request.response.close()) | |
| 127 .catchError((e) { | |
| 128 logger.severe('pub $method $path failed.'); | |
| 129 logger.severe(e.toString()); | |
| 130 }); | |
| 131 } | |
| 132 | |
| 133 void _onHttpRequest(HttpRequest request) { | |
| 134 // Allow cross origin requests. | |
| 135 request.response.headers.add('Access-Control-Allow-Origin', '*'); | |
| 136 if (request.uri.path.startsWith(_CHROME_PREFIX)) { | |
| 137 _getChromeTabs(request); | |
| 138 } else { | |
| 139 _forwardToPub(request); | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 /// Future completes to [this] on successful startup. | |
| 144 Future start() { | |
| 145 return HttpServer.bind(host, port).then((s) { | |
| 146 _server = s; | |
| 147 _server.listen(_onHttpRequest); | |
| 148 print('ObsServe is running on ${_server.address}:${_server.port}'); | |
| 149 }); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 main(List<String> args) { | |
| 154 hierarchicalLoggingEnabled = true; | |
| 155 logger.level = Level.ALL; | |
| 156 logger.onRecord.listen(print); | |
| 157 new ObservatoryServer(args)..start(); | |
| 158 } | |
| 159 | |
| OLD | NEW |