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