OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:convert' show HtmlEscape; | 6 import 'dart:convert' show HtmlEscape; |
7 import 'dart:io'; | 7 import 'dart:io'; |
8 | 8 |
9 import 'package:package_resolver/package_resolver.dart'; | |
10 | |
11 import 'configuration.dart'; | 9 import 'configuration.dart'; |
12 import 'vendored_pkg/args/args.dart'; | 10 import 'vendored_pkg/args/args.dart'; |
13 import 'utils.dart'; | 11 import 'utils.dart'; |
14 | 12 |
15 class DispatchingServer { | 13 class DispatchingServer { |
16 HttpServer server; | 14 HttpServer server; |
17 Map<String, Function> _handlers = new Map<String, Function>(); | 15 Map<String, Function> _handlers = new Map<String, Function>(); |
18 Function _notFound; | 16 Function _notFound; |
19 | 17 |
20 DispatchingServer( | 18 DispatchingServer( |
(...skipping 19 matching lines...) Expand all Loading... |
40 } | 38 } |
41 | 39 |
42 /// Interface of the HTTP server: | 40 /// Interface of the HTTP server: |
43 /// | 41 /// |
44 /// /echo: This will stream the data received in the request stream back | 42 /// /echo: This will stream the data received in the request stream back |
45 /// to the client. | 43 /// to the client. |
46 /// /root_dart/X: This will serve the corresponding file from the dart | 44 /// /root_dart/X: This will serve the corresponding file from the dart |
47 /// directory (i.e. '$DartDirectory/X'). | 45 /// directory (i.e. '$DartDirectory/X'). |
48 /// /root_build/X: This will serve the corresponding file from the build | 46 /// /root_build/X: This will serve the corresponding file from the build |
49 /// directory (i.e. '$BuildDirectory/X'). | 47 /// directory (i.e. '$BuildDirectory/X'). |
50 /// /FOO/packages/PAZ/BAR: This will serve files from the packages listed in | |
51 /// the package spec .packages. Supports a package | |
52 /// root or custom package spec, and uses [dart_dir]/.packages | |
53 /// as the default. This will serve file lib/BAR from the package PAZ. | |
54 /// /ws: This will upgrade the connection to a WebSocket connection and echo | 48 /// /ws: This will upgrade the connection to a WebSocket connection and echo |
55 /// all data back to the client. | 49 /// all data back to the client. |
56 /// | 50 /// |
57 /// In case a path does not refer to a file but rather to a directory, a | 51 /// In case a path does not refer to a file but rather to a directory, a |
58 /// directory listing will be displayed. | 52 /// directory listing will be displayed. |
59 | 53 |
60 const PREFIX_BUILDDIR = 'root_build'; | 54 const PREFIX_BUILDDIR = 'root_build'; |
61 const PREFIX_DARTDIR = 'root_dart'; | 55 const PREFIX_DARTDIR = 'root_dart'; |
62 | 56 |
63 void main(List<String> arguments) { | 57 void main(List<String> arguments) { |
64 // This script is in [dart]/tools/testing/dart. | 58 // This script is in [dart]/tools/testing/dart. |
65 TestUtils.setDartDirUri(Platform.script.resolve('../../..')); | 59 TestUtils.setDartDirUri(Platform.script.resolve('../../..')); |
66 /** Convenience method for local testing. */ | 60 /** Convenience method for local testing. */ |
67 var parser = new ArgParser(); | 61 var parser = new ArgParser(); |
68 parser.addOption('port', | 62 parser.addOption('port', |
69 abbr: 'p', | 63 abbr: 'p', |
70 help: 'The main server port we wish to respond to requests.', | 64 help: 'The main server port we wish to respond to requests.', |
71 defaultsTo: '0'); | 65 defaultsTo: '0'); |
72 parser.addOption('crossOriginPort', | 66 parser.addOption('crossOriginPort', |
73 abbr: 'c', | 67 abbr: 'c', |
74 help: 'A different port that accepts request from the main server port.', | 68 help: 'A different port that accepts request from the main server port.', |
75 defaultsTo: '0'); | 69 defaultsTo: '0'); |
76 parser.addFlag('help', | 70 parser.addFlag('help', |
77 abbr: 'h', negatable: false, help: 'Print this usage information.'); | 71 abbr: 'h', negatable: false, help: 'Print this usage information.'); |
78 parser.addOption('build-directory', help: 'The build directory to use.'); | 72 parser.addOption('build-directory', help: 'The build directory to use.'); |
79 parser.addOption('package-root', help: 'The package root to use.'); | 73 parser.addOption('package-root', help: 'Obsolete unsupported option'); |
80 parser.addOption('packages', help: 'The package spec file to use.'); | 74 parser.addOption('packages', help: 'Obsolete unsupported option'); |
81 parser.addOption('network', | 75 parser.addOption('network', |
82 help: 'The network interface to use.', defaultsTo: '0.0.0.0'); | 76 help: 'The network interface to use.', defaultsTo: '0.0.0.0'); |
83 parser.addFlag('csp', | 77 parser.addFlag('csp', |
84 help: 'Use Content Security Policy restrictions.', defaultsTo: false); | 78 help: 'Use Content Security Policy restrictions.', defaultsTo: false); |
85 parser.addOption('runtime', | 79 parser.addOption('runtime', |
86 help: 'The runtime we are using (for csp flags).', defaultsTo: 'none'); | 80 help: 'The runtime we are using (for csp flags).', defaultsTo: 'none'); |
87 | 81 |
88 var args = parser.parse(arguments); | 82 var args = parser.parse(arguments); |
89 if (args['help'] as bool) { | 83 if (args['help'] as bool) { |
90 print(parser.getUsage()); | 84 print(parser.getUsage()); |
91 } else { | 85 } else { |
92 var servers = new TestingServers( | 86 var servers = new TestingServers(args['build-directory'] as String, |
93 args['build-directory'] as String, | 87 args['csp'] as bool, Runtime.find(args['runtime'] as String), null); |
94 args['csp'] as bool, | |
95 Runtime.find(args['runtime'] as String), | |
96 null, | |
97 args['package-root'] as String, | |
98 args['packages'] as String); | |
99 var port = int.parse(args['port'] as String); | 88 var port = int.parse(args['port'] as String); |
100 var crossOriginPort = int.parse(args['crossOriginPort'] as String); | 89 var crossOriginPort = int.parse(args['crossOriginPort'] as String); |
101 servers | 90 servers |
102 .startServers(args['network'] as String, | 91 .startServers(args['network'] as String, |
103 port: port, crossOriginPort: crossOriginPort) | 92 port: port, crossOriginPort: crossOriginPort) |
104 .then((_) { | 93 .then((_) { |
105 DebugLogger.info('Server listening on port ${servers.port}'); | 94 DebugLogger.info('Server listening on port ${servers.port}'); |
106 DebugLogger.info('Server listening on port ${servers.crossOriginPort}'); | 95 DebugLogger.info('Server listening on port ${servers.crossOriginPort}'); |
107 }); | 96 }); |
108 } | 97 } |
109 } | 98 } |
110 | 99 |
111 /** | 100 /** |
112 * Runs a set of servers that are initialized specifically for the needs of our | 101 * Runs a set of servers that are initialized specifically for the needs of our |
113 * test framework, such as dealing with package-root. | 102 * test framework. |
114 */ | 103 */ |
115 class TestingServers { | 104 class TestingServers { |
116 static final _CACHE_EXPIRATION_IN_SECONDS = 30; | 105 static final _CACHE_EXPIRATION_IN_SECONDS = 30; |
117 static final _HARMLESS_REQUEST_PATH_ENDINGS = [ | 106 static final _HARMLESS_REQUEST_PATH_ENDINGS = [ |
118 "/apple-touch-icon.png", | 107 "/apple-touch-icon.png", |
119 "/apple-touch-icon-precomposed.png", | 108 "/apple-touch-icon-precomposed.png", |
120 "/favicon.ico", | 109 "/favicon.ico", |
121 "/foo", | 110 "/foo", |
122 "/bar", | 111 "/bar", |
123 "/NonExistingFile", | 112 "/NonExistingFile", |
124 "IntentionallyMissingFile", | 113 "IntentionallyMissingFile", |
125 ]; | 114 ]; |
126 | 115 |
127 final List<HttpServer> _serverList = []; | 116 final List<HttpServer> _serverList = []; |
128 Uri _buildDirectory; | 117 Uri _buildDirectory; |
129 Uri _dartDirectory; | 118 Uri _dartDirectory; |
130 Uri _packageRoot; | |
131 Uri _packages; | |
132 final bool useContentSecurityPolicy; | 119 final bool useContentSecurityPolicy; |
133 final Runtime runtime; | 120 final Runtime runtime; |
134 DispatchingServer _server; | 121 DispatchingServer _server; |
135 SyncPackageResolver _resolver; | |
136 | 122 |
137 TestingServers(String buildDirectory, this.useContentSecurityPolicy, | 123 TestingServers(String buildDirectory, this.useContentSecurityPolicy, |
138 [this.runtime = Runtime.none, | 124 [this.runtime = Runtime.none, String dartDirectory]) { |
139 String dartDirectory, | |
140 String packageRoot, | |
141 String packages]) { | |
142 _buildDirectory = Uri.base.resolveUri(new Uri.directory(buildDirectory)); | 125 _buildDirectory = Uri.base.resolveUri(new Uri.directory(buildDirectory)); |
143 if (dartDirectory == null) { | 126 if (dartDirectory == null) { |
144 _dartDirectory = TestUtils.dartDirUri; | 127 _dartDirectory = TestUtils.dartDirUri; |
145 } else { | 128 } else { |
146 _dartDirectory = Uri.base.resolveUri(new Uri.directory(dartDirectory)); | 129 _dartDirectory = Uri.base.resolveUri(new Uri.directory(dartDirectory)); |
147 } | 130 } |
148 if (packageRoot == null) { | |
149 if (packages == null) { | |
150 _packages = _dartDirectory.resolve('.packages'); | |
151 } else { | |
152 _packages = new Uri.file(packages); | |
153 } | |
154 } else { | |
155 _packageRoot = new Uri.directory(packageRoot); | |
156 } | |
157 } | 131 } |
158 | 132 |
159 int get port => _serverList[0].port; | 133 int get port => _serverList[0].port; |
160 int get crossOriginPort => _serverList[1].port; | 134 int get crossOriginPort => _serverList[1].port; |
161 DispatchingServer get server => _server; | 135 DispatchingServer get server => _server; |
162 | 136 |
163 /** | 137 /** |
164 * [startServers] will start two Http servers. | 138 * [startServers] will start two Http servers. |
165 * The first server listens on [port] and sets | 139 * The first server listens on [port] and sets |
166 * "Access-Control-Allow-Origin: *" | 140 * "Access-Control-Allow-Origin: *" |
167 * The second server listens on [crossOriginPort] and sets | 141 * The second server listens on [crossOriginPort] and sets |
168 * "Access-Control-Allow-Origin: client:port1 | 142 * "Access-Control-Allow-Origin: client:port1 |
169 * "Access-Control-Allow-Credentials: true" | 143 * "Access-Control-Allow-Credentials: true" |
170 */ | 144 */ |
171 Future startServers(String host, | 145 Future startServers(String host, |
172 {int port: 0, int crossOriginPort: 0}) async { | 146 {int port: 0, int crossOriginPort: 0}) async { |
173 if (_packages != null) { | |
174 _resolver = await SyncPackageResolver.loadConfig(_packages); | |
175 } else { | |
176 _resolver = new SyncPackageResolver.root(_packageRoot); | |
177 } | |
178 _server = await _startHttpServer(host, port: port); | 147 _server = await _startHttpServer(host, port: port); |
179 await _startHttpServer(host, | 148 await _startHttpServer(host, |
180 port: crossOriginPort, allowedPort: _serverList[0].port); | 149 port: crossOriginPort, allowedPort: _serverList[0].port); |
181 } | 150 } |
182 | 151 |
183 String httpServerCommandLine() { | 152 String httpServerCommandLine() { |
184 var dart = Platform.resolvedExecutable; | 153 var dart = Platform.resolvedExecutable; |
185 var script = _dartDirectory.resolve('tools/testing/dart/http_server.dart'); | 154 var script = _dartDirectory.resolve('tools/testing/dart/http_server.dart'); |
186 var buildDirectory = _buildDirectory.toFilePath(); | 155 var buildDirectory = _buildDirectory.toFilePath(); |
187 var command = [ | 156 var command = [ |
188 dart, | 157 dart, |
189 script.toFilePath(), | 158 script.toFilePath(), |
190 '-p', | 159 '-p', |
191 port, | 160 port, |
192 '-c', | 161 '-c', |
193 crossOriginPort, | 162 crossOriginPort, |
194 '--build-directory=$buildDirectory', | 163 '--build-directory=$buildDirectory', |
195 '--runtime=${runtime.name}' | 164 '--runtime=${runtime.name}' |
196 ]; | 165 ]; |
197 if (useContentSecurityPolicy) { | 166 if (useContentSecurityPolicy) { |
198 command.add('--csp'); | 167 command.add('--csp'); |
199 } | 168 } |
200 if (_packages != null) { | |
201 command.add('--packages=${_packages.toFilePath()}'); | |
202 } else if (_packageRoot != null) { | |
203 command.add('--package-root=${_packageRoot.toFilePath()}'); | |
204 } | |
205 return command.join(' '); | 169 return command.join(' '); |
206 } | 170 } |
207 | 171 |
208 void stopServers() { | 172 void stopServers() { |
209 for (var server in _serverList) { | 173 for (var server in _serverList) { |
210 server.close(); | 174 server.close(); |
211 } | 175 } |
212 } | 176 } |
213 | 177 |
214 void _onError(e) { | 178 void _onError(e) { |
215 DebugLogger.error('HttpServer: an error occured', e); | 179 DebugLogger.error('HttpServer: an error occured', e); |
216 } | 180 } |
217 | 181 |
218 Future<DispatchingServer> _startHttpServer(String host, | 182 Future<DispatchingServer> _startHttpServer(String host, |
219 {int port: 0, int allowedPort: -1}) { | 183 {int port: 0, int allowedPort: -1}) { |
220 return HttpServer.bind(host, port).then((HttpServer httpServer) { | 184 return HttpServer.bind(host, port).then((HttpServer httpServer) { |
221 var server = new DispatchingServer(httpServer, _onError, _sendNotFound); | 185 var server = new DispatchingServer(httpServer, _onError, _sendNotFound); |
222 server.addHandler('/echo', _handleEchoRequest); | 186 server.addHandler('/echo', _handleEchoRequest); |
223 server.addHandler('/ws', _handleWebSocketRequest); | 187 server.addHandler('/ws', _handleWebSocketRequest); |
224 fileHandler(HttpRequest request) { | 188 fileHandler(HttpRequest request) { |
225 _handleFileOrDirectoryRequest(request, allowedPort); | 189 _handleFileOrDirectoryRequest(request, allowedPort); |
226 } | 190 } |
227 | 191 |
228 server.addHandler('/$PREFIX_BUILDDIR', fileHandler); | 192 server.addHandler('/$PREFIX_BUILDDIR', fileHandler); |
229 server.addHandler('/$PREFIX_DARTDIR', fileHandler); | 193 server.addHandler('/$PREFIX_DARTDIR', fileHandler); |
230 server.addHandler('/packages', fileHandler); | |
231 _serverList.add(httpServer); | 194 _serverList.add(httpServer); |
232 return server; | 195 return server; |
233 }); | 196 }); |
234 } | 197 } |
235 | 198 |
236 Future _handleFileOrDirectoryRequest( | 199 Future _handleFileOrDirectoryRequest( |
237 HttpRequest request, int allowedPort) async { | 200 HttpRequest request, int allowedPort) async { |
238 // Enable browsers to cache file/directory responses. | 201 // Enable browsers to cache file/directory responses. |
239 var response = request.response; | 202 var response = request.response; |
240 response.headers | 203 response.headers |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
293 }).catchError((e) { | 256 }).catchError((e) { |
294 DebugLogger.warning( | 257 DebugLogger.warning( |
295 'HttpServer: error while transforming to WebSocket', e); | 258 'HttpServer: error while transforming to WebSocket', e); |
296 }); | 259 }); |
297 } | 260 } |
298 | 261 |
299 Uri _getFileUriFromRequestUri(Uri request) { | 262 Uri _getFileUriFromRequestUri(Uri request) { |
300 // Go to the top of the file to see an explanation of the URL path scheme. | 263 // Go to the top of the file to see an explanation of the URL path scheme. |
301 List<String> pathSegments = request.normalizePath().pathSegments; | 264 List<String> pathSegments = request.normalizePath().pathSegments; |
302 if (pathSegments.length == 0) return null; | 265 if (pathSegments.length == 0) return null; |
303 int packagesIndex = pathSegments.indexOf('packages'); | |
304 if (packagesIndex != -1) { | |
305 var packageUri = new Uri( | |
306 scheme: 'package', | |
307 pathSegments: pathSegments.skip(packagesIndex + 1)); | |
308 return _resolver.resolveUri(packageUri); | |
309 } | |
310 if (pathSegments[0] == PREFIX_BUILDDIR) { | 266 if (pathSegments[0] == PREFIX_BUILDDIR) { |
311 return _buildDirectory.resolve(pathSegments.skip(1).join('/')); | 267 return _buildDirectory.resolve(pathSegments.skip(1).join('/')); |
312 } | 268 } |
313 if (pathSegments[0] == PREFIX_DARTDIR) { | 269 if (pathSegments[0] == PREFIX_DARTDIR) { |
314 return _dartDirectory.resolve(pathSegments.skip(1).join('/')); | 270 return _dartDirectory.resolve(pathSegments.skip(1).join('/')); |
315 } | 271 } |
316 return null; | 272 return null; |
317 } | 273 } |
318 | 274 |
319 Future<List<_Entry>> _listDirectory(Directory directory) { | 275 Future<List<_Entry>> _listDirectory(Directory directory) { |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
468 class _Entry implements Comparable<_Entry> { | 424 class _Entry implements Comparable<_Entry> { |
469 final String name; | 425 final String name; |
470 final String displayName; | 426 final String displayName; |
471 | 427 |
472 _Entry(this.name, this.displayName); | 428 _Entry(this.name, this.displayName); |
473 | 429 |
474 int compareTo(_Entry other) { | 430 int compareTo(_Entry other) { |
475 return name.compareTo(other.name); | 431 return name.compareTo(other.name); |
476 } | 432 } |
477 } | 433 } |
OLD | NEW |