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