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 library http_server; | 5 library http_server; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 | 9 |
10 import 'dart:convert' show HtmlEscape; | 10 import 'dart:convert' show HtmlEscape; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 help: 'The network interface to use.', defaultsTo: '0.0.0.0'); | 84 help: 'The network interface to use.', defaultsTo: '0.0.0.0'); |
85 parser.addFlag('csp', | 85 parser.addFlag('csp', |
86 help: 'Use Content Security Policy restrictions.', defaultsTo: false); | 86 help: 'Use Content Security Policy restrictions.', defaultsTo: false); |
87 parser.addOption('runtime', | 87 parser.addOption('runtime', |
88 help: 'The runtime we are using (for csp flags).', defaultsTo: 'none'); | 88 help: 'The runtime we are using (for csp flags).', defaultsTo: 'none'); |
89 | 89 |
90 var args = parser.parse(arguments); | 90 var args = parser.parse(arguments); |
91 if (args['help']) { | 91 if (args['help']) { |
92 print(parser.getUsage()); | 92 print(parser.getUsage()); |
93 } else { | 93 } else { |
94 var servers = new TestingServers(args['build-directory'], | 94 var servers = new TestingServers(args['build-directory'], args['csp'], |
95 args['csp'], args['runtime'], null, args['package-root'], | 95 args['runtime'], null, args['package-root'], args['packages']); |
96 args['packages']); | |
97 var port = int.parse(args['port']); | 96 var port = int.parse(args['port']); |
98 var crossOriginPort = int.parse(args['crossOriginPort']); | 97 var crossOriginPort = int.parse(args['crossOriginPort']); |
99 servers | 98 servers |
100 .startServers(args['network'], | 99 .startServers(args['network'], |
101 port: port, crossOriginPort: crossOriginPort) | 100 port: port, crossOriginPort: crossOriginPort) |
102 .then((_) { | 101 .then((_) { |
103 DebugLogger.info('Server listening on port ${servers.port}'); | 102 DebugLogger.info('Server listening on port ${servers.port}'); |
104 DebugLogger.info('Server listening on port ${servers.crossOriginPort}'); | 103 DebugLogger.info('Server listening on port ${servers.crossOriginPort}'); |
105 }); | 104 }); |
106 } | 105 } |
(...skipping 29 matching lines...) Expand all Loading... |
136 [String this.runtime = 'none', | 135 [String this.runtime = 'none', |
137 String dartDirectory, | 136 String dartDirectory, |
138 String packageRoot, | 137 String packageRoot, |
139 String packages]) { | 138 String packages]) { |
140 _buildDirectory = Uri.base.resolveUri(new Uri.directory(buildDirectory)); | 139 _buildDirectory = Uri.base.resolveUri(new Uri.directory(buildDirectory)); |
141 if (dartDirectory == null) { | 140 if (dartDirectory == null) { |
142 _dartDirectory = TestUtils.dartDirUri; | 141 _dartDirectory = TestUtils.dartDirUri; |
143 } else { | 142 } else { |
144 _dartDirectory = Uri.base.resolveUri(new Uri.directory(dartDirectory)); | 143 _dartDirectory = Uri.base.resolveUri(new Uri.directory(dartDirectory)); |
145 } | 144 } |
146 if (packageRoot == null ) { | 145 if (packageRoot == null) { |
147 if (packages == null ) { | 146 if (packages == null) { |
148 _packages = _dartDirectory.resolve('.packages'); | 147 _packages = _dartDirectory.resolve('.packages'); |
149 } else { | 148 } else { |
150 _packages = new Uri.file(packages); | 149 _packages = new Uri.file(packages); |
151 } | 150 } |
152 } else { | 151 } else { |
153 _packageRoot = new Uri.directory(packageRoot); | 152 _packageRoot = new Uri.directory(packageRoot); |
154 } | 153 } |
155 } | 154 } |
156 | 155 |
157 int get port => _serverList[0].port; | 156 int get port => _serverList[0].port; |
158 int get crossOriginPort => _serverList[1].port; | 157 int get crossOriginPort => _serverList[1].port; |
159 DispatchingServer get server => _server; | 158 DispatchingServer get server => _server; |
160 | 159 |
161 /** | 160 /** |
162 * [startServers] will start two Http servers. | 161 * [startServers] will start two Http servers. |
163 * The first server listens on [port] and sets | 162 * The first server listens on [port] and sets |
164 * "Access-Control-Allow-Origin: *" | 163 * "Access-Control-Allow-Origin: *" |
165 * The second server listens on [crossOriginPort] and sets | 164 * The second server listens on [crossOriginPort] and sets |
166 * "Access-Control-Allow-Origin: client:port1 | 165 * "Access-Control-Allow-Origin: client:port1 |
167 * "Access-Control-Allow-Credentials: true" | 166 * "Access-Control-Allow-Credentials: true" |
168 */ | 167 */ |
169 Future startServers(String host, | 168 Future startServers(String host, |
170 {int port: 0, | 169 {int port: 0, int crossOriginPort: 0}) async { |
171 int crossOriginPort: 0}) async { | |
172 if (_packages != null) { | 170 if (_packages != null) { |
173 _resolver = await SyncPackageResolver.loadConfig(_packages); | 171 _resolver = await SyncPackageResolver.loadConfig(_packages); |
174 } else { | 172 } else { |
175 _resolver = new SyncPackageResolver.root(_packageRoot); | 173 _resolver = new SyncPackageResolver.root(_packageRoot); |
176 } | 174 } |
177 _server = await _startHttpServer(host, port: port); | 175 _server = await _startHttpServer(host, port: port); |
178 await _startHttpServer(host, port: crossOriginPort, | 176 await _startHttpServer(host, |
179 allowedPort: _serverList[0].port); | 177 port: crossOriginPort, allowedPort: _serverList[0].port); |
180 } | 178 } |
181 | 179 |
182 String httpServerCommandline() { | 180 String httpServerCommandline() { |
183 var dart = Platform.resolvedExecutable; | 181 var dart = Platform.resolvedExecutable; |
184 var script = _dartDirectory.resolve('tools/testing/dart/http_server.dart'); | 182 var script = _dartDirectory.resolve('tools/testing/dart/http_server.dart'); |
185 var buildDirectory = _buildDirectory.toFilePath(); | 183 var buildDirectory = _buildDirectory.toFilePath(); |
186 var command = [dart, script.toFilePath(), | 184 var command = [ |
187 '-p', port, | 185 dart, |
188 '-c', crossOriginPort, | 186 script.toFilePath(), |
| 187 '-p', |
| 188 port, |
| 189 '-c', |
| 190 crossOriginPort, |
189 '--build-directory=$buildDirectory', | 191 '--build-directory=$buildDirectory', |
190 '--runtime=$runtime']; | 192 '--runtime=$runtime' |
| 193 ]; |
191 if (useContentSecurityPolicy) { | 194 if (useContentSecurityPolicy) { |
192 command.add('--csp'); | 195 command.add('--csp'); |
193 } | 196 } |
194 if (_packages != null) { | 197 if (_packages != null) { |
195 command.add('--packages=${_packages.toFilePath()}'); | 198 command.add('--packages=${_packages.toFilePath()}'); |
196 } else if (_packageRoot != null) { | 199 } else if (_packageRoot != null) { |
197 command.add('--package-root=${_packageRoot.toFilePath()}'); | 200 command.add('--package-root=${_packageRoot.toFilePath()}'); |
198 } | 201 } |
199 return command.join(' '); | 202 return command.join(' '); |
200 } | 203 } |
201 | 204 |
202 void stopServers() { | 205 void stopServers() { |
203 for (var server in _serverList) { | 206 for (var server in _serverList) { |
204 server.close(); | 207 server.close(); |
205 } | 208 } |
206 } | 209 } |
207 | 210 |
208 void _onError(e) { | 211 void _onError(e) { |
209 DebugLogger.error('HttpServer: an error occured', e); | 212 DebugLogger.error('HttpServer: an error occured', e); |
210 } | 213 } |
211 | 214 |
212 Future _startHttpServer(String host, {int port: 0, int allowedPort: -1}) { | 215 Future _startHttpServer(String host, {int port: 0, int allowedPort: -1}) { |
213 return HttpServer.bind(host, port).then((HttpServer httpServer) { | 216 return HttpServer.bind(host, port).then((HttpServer httpServer) { |
214 var server = new DispatchingServer(httpServer, _onError, _sendNotFound); | 217 var server = new DispatchingServer(httpServer, _onError, _sendNotFound); |
215 server.addHandler('/echo', _handleEchoRequest); | 218 server.addHandler('/echo', _handleEchoRequest); |
216 server.addHandler('/ws', _handleWebSocketRequest); | 219 server.addHandler('/ws', _handleWebSocketRequest); |
217 fileHandler(request) { | 220 fileHandler(request) { |
218 _handleFileOrDirectoryRequest(request, allowedPort); | 221 _handleFileOrDirectoryRequest(request, allowedPort); |
219 } | 222 } |
| 223 |
220 server.addHandler('/$PREFIX_BUILDDIR', fileHandler); | 224 server.addHandler('/$PREFIX_BUILDDIR', fileHandler); |
221 server.addHandler('/$PREFIX_DARTDIR', fileHandler); | 225 server.addHandler('/$PREFIX_DARTDIR', fileHandler); |
222 server.addHandler('/packages', fileHandler); | 226 server.addHandler('/packages', fileHandler); |
223 _serverList.add(httpServer); | 227 _serverList.add(httpServer); |
224 return server; | 228 return server; |
225 }); | 229 }); |
226 } | 230 } |
227 | 231 |
228 _handleFileOrDirectoryRequest(HttpRequest request, | 232 _handleFileOrDirectoryRequest(HttpRequest request, int allowedPort) async { |
229 int allowedPort) async { | |
230 // Enable browsers to cache file/directory responses. | 233 // Enable browsers to cache file/directory responses. |
231 var response = request.response; | 234 var response = request.response; |
232 response.headers | 235 response.headers |
233 .set("Cache-Control", "max-age=$_CACHE_EXPIRATION_IN_SECONDS"); | 236 .set("Cache-Control", "max-age=$_CACHE_EXPIRATION_IN_SECONDS"); |
234 var path = _getFileUriFromRequestUri(request.uri); | 237 var path = _getFileUriFromRequestUri(request.uri); |
235 if (path != null) { | 238 if (path != null) { |
236 var file = new File.fromUri(path); | 239 var file = new File.fromUri(path); |
237 var directory = new Directory.fromUri(path); | 240 var directory = new Directory.fromUri(path); |
238 if (await file.exists()){ | 241 if (await file.exists()) { |
239 _sendFileContent(request, response, allowedPort, file); | 242 _sendFileContent(request, response, allowedPort, file); |
240 } else if (await directory.exists()) { | 243 } else if (await directory.exists()) { |
241 _sendDirectoryListing( | 244 _sendDirectoryListing( |
242 await _listDirectory(directory), request, response); | 245 await _listDirectory(directory), request, response); |
243 } else { | 246 } else { |
244 _sendNotFound(request); | 247 _sendNotFound(request); |
245 } | 248 } |
246 } else { | 249 } else { |
247 if (request.uri.path == '/') { | 250 if (request.uri.path == '/') { |
248 var entries = [ | 251 var entries = [ |
249 new _Entry('root_dart', 'root_dart/'), | 252 new _Entry('root_dart', 'root_dart/'), |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
287 'HttpServer: error while transforming to WebSocket', e); | 290 'HttpServer: error while transforming to WebSocket', e); |
288 }); | 291 }); |
289 } | 292 } |
290 | 293 |
291 Uri _getFileUriFromRequestUri(Uri request) { | 294 Uri _getFileUriFromRequestUri(Uri request) { |
292 // Go to the top of the file to see an explanation of the URL path scheme. | 295 // Go to the top of the file to see an explanation of the URL path scheme. |
293 List<String> pathSegments = request.normalizePath().pathSegments; | 296 List<String> pathSegments = request.normalizePath().pathSegments; |
294 if (pathSegments.length == 0) return null; | 297 if (pathSegments.length == 0) return null; |
295 int packagesIndex = pathSegments.indexOf('packages'); | 298 int packagesIndex = pathSegments.indexOf('packages'); |
296 if (packagesIndex != -1) { | 299 if (packagesIndex != -1) { |
297 var packageUri = new Uri(scheme: 'package', | 300 var packageUri = new Uri( |
| 301 scheme: 'package', |
298 pathSegments: pathSegments.skip(packagesIndex + 1)); | 302 pathSegments: pathSegments.skip(packagesIndex + 1)); |
299 return _resolver.resolveUri(packageUri); | 303 return _resolver.resolveUri(packageUri); |
300 } | 304 } |
301 if (pathSegments[0] == PREFIX_BUILDDIR) { | 305 if (pathSegments[0] == PREFIX_BUILDDIR) { |
302 return _buildDirectory.resolve(pathSegments.skip(1).join('/')); | 306 return _buildDirectory.resolve(pathSegments.skip(1).join('/')); |
303 } | 307 } |
304 if (pathSegments[0] == PREFIX_DARTDIR) { | 308 if (pathSegments[0] == PREFIX_DARTDIR) { |
305 return _dartDirectory.resolve(pathSegments.skip(1).join('/')); | 309 return _dartDirectory.resolve(pathSegments.skip(1).join('/')); |
306 } | 310 } |
307 return null; | 311 return null; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 <ul>'''; | 345 <ul>'''; |
342 var footer = ''' | 346 var footer = ''' |
343 </ul> | 347 </ul> |
344 </code> | 348 </code> |
345 </body> | 349 </body> |
346 </html>'''; | 350 </html>'''; |
347 | 351 |
348 entries.sort(); | 352 entries.sort(); |
349 response.write(header); | 353 response.write(header); |
350 for (var entry in entries) { | 354 for (var entry in entries) { |
351 response.write( | 355 response.write('<li><a href="${request.uri}/${entry.name}">' |
352 '<li><a href="${request.uri}/${entry.name}">' | |
353 '${entry.displayName}</a></li>'); | 356 '${entry.displayName}</a></li>'); |
354 } | 357 } |
355 response.write(footer); | 358 response.write(footer); |
356 response.close(); | 359 response.close(); |
357 response.done.catchError((e) { | 360 response.done.catchError((e) { |
358 DebugLogger.warning( | 361 DebugLogger.warning( |
359 'HttpServer: error while closing the response stream', e); | 362 'HttpServer: error while closing the response stream', e); |
360 }); | 363 }); |
361 } | 364 } |
362 | 365 |
363 void _sendFileContent(HttpRequest request, HttpResponse response, | 366 void _sendFileContent( |
364 int allowedPort, File file) { | 367 HttpRequest request, HttpResponse response, int allowedPort, File file) { |
365 if (allowedPort != -1) { | 368 if (allowedPort != -1) { |
366 var headerOrigin = request.headers.value('Origin'); | 369 var headerOrigin = request.headers.value('Origin'); |
367 var allowedOrigin; | 370 var allowedOrigin; |
368 if (headerOrigin != null) { | 371 if (headerOrigin != null) { |
369 var origin = Uri.parse(headerOrigin); | 372 var origin = Uri.parse(headerOrigin); |
370 // Allow loading from http://*:$allowedPort in browsers. | 373 // Allow loading from http://*:$allowedPort in browsers. |
371 allowedOrigin = '${origin.scheme}://${origin.host}:${allowedPort}'; | 374 allowedOrigin = '${origin.scheme}://${origin.host}:${allowedPort}'; |
372 } else { | 375 } else { |
373 // IE10 appears to be bugged and is not sending the Origin header | 376 // IE10 appears to be bugged and is not sending the Origin header |
374 // when making CORS requests to the same domain but different port. | 377 // when making CORS requests to the same domain but different port. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
414 'HttpServer: error while closing the response stream', e); | 417 'HttpServer: error while closing the response stream', e); |
415 }); | 418 }); |
416 } | 419 } |
417 | 420 |
418 void _sendNotFound(HttpRequest request) { | 421 void _sendNotFound(HttpRequest request) { |
419 bool isHarmlessPath(String path) { | 422 bool isHarmlessPath(String path) { |
420 return _HARMLESS_REQUEST_PATH_ENDINGS.any((pattern) { | 423 return _HARMLESS_REQUEST_PATH_ENDINGS.any((pattern) { |
421 return path.contains(pattern); | 424 return path.contains(pattern); |
422 }); | 425 }); |
423 } | 426 } |
| 427 |
424 if (!isHarmlessPath(request.uri.path)) { | 428 if (!isHarmlessPath(request.uri.path)) { |
425 DebugLogger.warning('HttpServer: could not find file for request path: ' | 429 DebugLogger.warning('HttpServer: could not find file for request path: ' |
426 '"${request.uri.path}"'); | 430 '"${request.uri.path}"'); |
427 } | 431 } |
428 var response = request.response; | 432 var response = request.response; |
429 response.statusCode = HttpStatus.NOT_FOUND; | 433 response.statusCode = HttpStatus.NOT_FOUND; |
430 | 434 |
431 // Send a nice HTML page detailing the error message. Most browsers expect | 435 // Send a nice HTML page detailing the error message. Most browsers expect |
432 // this, for example, Chrome will simply display a blank page if you don't | 436 // this, for example, Chrome will simply display a blank page if you don't |
433 // provide any information. A nice side effect of this is to work around | 437 // provide any information. A nice side effect of this is to work around |
(...skipping 25 matching lines...) Expand all Loading... |
459 class _Entry implements Comparable { | 463 class _Entry implements Comparable { |
460 final String name; | 464 final String name; |
461 final String displayName; | 465 final String displayName; |
462 | 466 |
463 _Entry(this.name, this.displayName); | 467 _Entry(this.name, this.displayName); |
464 | 468 |
465 int compareTo(_Entry other) { | 469 int compareTo(_Entry other) { |
466 return name.compareTo(other.name); | 470 return name.compareTo(other.name); |
467 } | 471 } |
468 } | 472 } |
OLD | NEW |