| 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 |