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 |