| Index: tools/testing/dart/http_server.dart
|
| diff --git a/tools/testing/dart/http_server.dart b/tools/testing/dart/http_server.dart
|
| deleted file mode 100644
|
| index 7f5fae75bc4dee64baf58831ba2bf50a50a5af10..0000000000000000000000000000000000000000
|
| --- a/tools/testing/dart/http_server.dart
|
| +++ /dev/null
|
| @@ -1,465 +0,0 @@
|
| -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library http_server;
|
| -
|
| -import 'dart:async';
|
| -import 'dart:io';
|
| -
|
| -import 'dart:convert' show
|
| - HtmlEscape;
|
| -
|
| -import 'path.dart';
|
| -import 'test_suite.dart'; // For TestUtils.
|
| -// TODO(efortuna): Rewrite to not use the args library and simply take an
|
| -// expected number of arguments, so test.dart doesn't rely on the args library?
|
| -// See discussion on https://codereview.chromium.org/11931025/.
|
| -import 'vendored_pkg/args/args.dart';
|
| -import 'utils.dart';
|
| -
|
| -class DispatchingServer {
|
| - HttpServer server;
|
| - Map<String, Function> _handlers = new Map<String, Function>();
|
| - Function _notFound;
|
| -
|
| - DispatchingServer(this.server,
|
| - void onError(e),
|
| - void this._notFound(HttpRequest request)) {
|
| - server.listen(_dispatchRequest, onError: onError);
|
| - }
|
| -
|
| - void addHandler(String prefix, void handler(HttpRequest request)) {
|
| - _handlers[prefix] = handler;
|
| - }
|
| -
|
| - void _dispatchRequest(HttpRequest request) {
|
| - // If the request path matches a prefix in _handlers, send it to that
|
| - // handler. Otherwise, run the notFound handler.
|
| - for (String prefix in _handlers.keys) {
|
| - if (request.uri.path.startsWith(prefix)) {
|
| - _handlers[prefix](request);
|
| - return;
|
| - }
|
| - }
|
| - _notFound(request);
|
| - }
|
| -}
|
| -
|
| -
|
| -
|
| -/// Interface of the HTTP server:
|
| -///
|
| -/// /echo: This will stream the data received in the request stream back
|
| -/// to the client.
|
| -/// /root_dart/X: This will serve the corresponding file from the dart
|
| -/// directory (i.e. '$DartDirectory/X').
|
| -/// /root_build/X: This will serve the corresponding file from the build
|
| -/// directory (i.e. '$BuildDirectory/X').
|
| -/// /FOO/packages/BAR: This will serve the corresponding file from the packages
|
| -/// directory (i.e. '$BuildDirectory/packages/BAR') or the
|
| -/// passed-in package root
|
| -/// /ws: This will upgrade the connection to a WebSocket connection and echo
|
| -/// all data back to the client.
|
| -///
|
| -/// In case a path does not refer to a file but rather to a directory, a
|
| -/// directory listing will be displayed.
|
| -
|
| -const PREFIX_BUILDDIR = 'root_build';
|
| -const PREFIX_DARTDIR = 'root_dart';
|
| -
|
| -// TODO(kustermann,ricow): We could change this to the following scheme:
|
| -// http://host:port/root_packages/X -> $BuildDir/packages/X
|
| -// Issue: 8368
|
| -
|
| -main(List<String> arguments) {
|
| - // This script is in [dart]/tools/testing/dart.
|
| - TestUtils.setDartDirUri(Platform.script.resolve('../../..'));
|
| - /** Convenience method for local testing. */
|
| - var parser = new ArgParser();
|
| - parser.addOption('port', abbr: 'p',
|
| - help: 'The main server port we wish to respond to requests.',
|
| - defaultsTo: '0');
|
| - parser.addOption('crossOriginPort', abbr: 'c',
|
| - help: 'A different port that accepts request from the main server port.',
|
| - defaultsTo: '0');
|
| - parser.addFlag('help', abbr: 'h', negatable: false,
|
| - help: 'Print this usage information.');
|
| - parser.addOption('build-directory', help: 'The build directory to use.');
|
| - parser.addOption('package-root', help: 'The package root to use.');
|
| - parser.addOption('network', help: 'The network interface to use.',
|
| - defaultsTo: '0.0.0.0');
|
| - parser.addFlag('csp', help: 'Use Content Security Policy restrictions.',
|
| - defaultsTo: false);
|
| - parser.addOption('runtime', help: 'The runtime we are using (for csp flags).',
|
| - defaultsTo: 'none');
|
| -
|
| - var args = parser.parse(arguments);
|
| - if (args['help']) {
|
| - print(parser.getUsage());
|
| - } else {
|
| - var servers = new TestingServers(new Path(args['build-directory']),
|
| - args['csp'],
|
| - args['runtime'],
|
| - null,
|
| - args['package-root']);
|
| - var port = int.parse(args['port']);
|
| - var crossOriginPort = int.parse(args['crossOriginPort']);
|
| - servers.startServers(args['network'],
|
| - port: port,
|
| - crossOriginPort: crossOriginPort).then((_) {
|
| - DebugLogger.info('Server listening on port ${servers.port}');
|
| - DebugLogger.info('Server listening on port ${servers.crossOriginPort}');
|
| - });
|
| - }
|
| -}
|
| -
|
| -/**
|
| - * Runs a set of servers that are initialized specifically for the needs of our
|
| - * test framework, such as dealing with package-root.
|
| - */
|
| -class TestingServers {
|
| - static final _CACHE_EXPIRATION_IN_SECONDS = 30;
|
| - static final _HARMLESS_REQUEST_PATH_ENDINGS = [
|
| - "/apple-touch-icon.png",
|
| - "/apple-touch-icon-precomposed.png",
|
| - "/favicon.ico",
|
| - "/foo",
|
| - "/bar",
|
| - "/NonExistingFile",
|
| - "IntentionallyMissingFile",
|
| - ];
|
| -
|
| - List _serverList = [];
|
| - Path _buildDirectory = null;
|
| - Path _dartDirectory = null;
|
| - Path _packageRoot;
|
| - final bool useContentSecurityPolicy;
|
| - final String runtime;
|
| - DispatchingServer _server;
|
| -
|
| - TestingServers(Path buildDirectory,
|
| - this.useContentSecurityPolicy,
|
| - [String this.runtime = 'none', String dartDirectory,
|
| - String packageRoot]) {
|
| - _buildDirectory = TestUtils.absolutePath(buildDirectory);
|
| - _dartDirectory = dartDirectory == null ? TestUtils.dartDir
|
| - : new Path(dartDirectory);
|
| - _packageRoot = packageRoot == null ?
|
| - _buildDirectory.append('packages') :
|
| - new Path(packageRoot);
|
| - }
|
| -
|
| - int get port => _serverList[0].port;
|
| - int get crossOriginPort => _serverList[1].port;
|
| - DispatchingServer get server => _server;
|
| -
|
| - /**
|
| - * [startServers] will start two Http servers.
|
| - * The first server listens on [port] and sets
|
| - * "Access-Control-Allow-Origin: *"
|
| - * The second server listens on [crossOriginPort] and sets
|
| - * "Access-Control-Allow-Origin: client:port1
|
| - * "Access-Control-Allow-Credentials: true"
|
| - */
|
| - Future startServers(String host, {int port: 0, int crossOriginPort: 0}) {
|
| - return _startHttpServer(host, port: port).then((server) {
|
| - _server = server;
|
| - return _startHttpServer(host,
|
| - port: crossOriginPort,
|
| - allowedPort:_serverList[0].port);
|
| - });
|
| - }
|
| -
|
| - String httpServerCommandline() {
|
| - var dart = TestUtils.dartTestExecutable.toNativePath();
|
| - var dartDir = TestUtils.dartDir;
|
| - var script = dartDir.join(new Path("tools/testing/dart/http_server.dart"));
|
| - var buildDirectory = _buildDirectory.toNativePath();
|
| - var csp = useContentSecurityPolicy ? '--csp ' : '';
|
| - return '$dart $script -p $port -c $crossOriginPort $csp'
|
| - '--build-directory=$buildDirectory --runtime=$runtime '
|
| - '--package-root=$_packageRoot';
|
| - }
|
| -
|
| - void stopServers() {
|
| - for (var server in _serverList) {
|
| - server.close();
|
| - }
|
| - }
|
| -
|
| - void _onError(e) {
|
| - DebugLogger.error('HttpServer: an error occured', e);
|
| - }
|
| -
|
| - Future _startHttpServer(String host, {int port: 0, int allowedPort: -1}) {
|
| - return HttpServer.bind(host, port).then((HttpServer httpServer) {
|
| - var server = new DispatchingServer(httpServer, _onError, _sendNotFound);
|
| - server.addHandler('/echo', _handleEchoRequest);
|
| - server.addHandler('/ws', _handleWebSocketRequest);
|
| - fileHandler(request) {
|
| - _handleFileOrDirectoryRequest(request, allowedPort);
|
| - }
|
| - server.addHandler('/$PREFIX_BUILDDIR', fileHandler);
|
| - server.addHandler('/$PREFIX_DARTDIR', fileHandler);
|
| - server.addHandler('/packages', fileHandler);
|
| - _serverList.add(httpServer);
|
| - return server;
|
| - });
|
| - }
|
| -
|
| - void _handleFileOrDirectoryRequest(HttpRequest request,
|
| - int allowedPort) {
|
| - // Enable browsers to cache file/directory responses.
|
| - var response = request.response;
|
| - response.headers.set("Cache-Control",
|
| - "max-age=$_CACHE_EXPIRATION_IN_SECONDS");
|
| - var path = _getFilePathFromRequestPath(request.uri.path);
|
| - if (path != null) {
|
| - var file = new File(path.toNativePath());
|
| - file.exists().then((exists) {
|
| - if (exists) {
|
| - _sendFileContent(request, response, allowedPort, path, file);
|
| - } else {
|
| - var directory = new Directory(path.toNativePath());
|
| - directory.exists().then((exists) {
|
| - if (exists) {
|
| - _listDirectory(directory).then((entries) {
|
| - _sendDirectoryListing(entries, request, response);
|
| - });
|
| - } else {
|
| - _sendNotFound(request);
|
| - }
|
| - });
|
| - }
|
| - });
|
| - } else {
|
| - if (request.uri.path == '/') {
|
| - var entries = [new _Entry('root_dart', 'root_dart/'),
|
| - new _Entry('root_build', 'root_build/'),
|
| - new _Entry('echo', 'echo')];
|
| - _sendDirectoryListing(entries, request, response);
|
| - } else {
|
| - _sendNotFound(request);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void _handleEchoRequest(HttpRequest request) {
|
| - request.response.headers.set("Access-Control-Allow-Origin", "*");
|
| - request.pipe(request.response).catchError((e) {
|
| - DebugLogger.warning(
|
| - 'HttpServer: error while closing the response stream', e);
|
| - });
|
| - }
|
| -
|
| - void _handleWebSocketRequest(HttpRequest request) {
|
| - WebSocketTransformer.upgrade(request).then((websocket) {
|
| - // We ignore failures to write to the socket, this happens if the browser
|
| - // closes the connection.
|
| - websocket.done.catchError((_) {});
|
| - websocket.listen((data) {
|
| - websocket.add(data);
|
| - websocket.close();
|
| - }, onError: (e) {
|
| - DebugLogger.warning('HttpServer: error while echoing to WebSocket', e);
|
| - });
|
| - }).catchError((e) {
|
| - DebugLogger.warning(
|
| - 'HttpServer: error while transforming to WebSocket', e);
|
| - });
|
| - }
|
| -
|
| - Path _getFilePathFromRequestPath(String urlRequestPath) {
|
| - // Go to the top of the file to see an explanation of the URL path scheme.
|
| - var requestPath = new Path(urlRequestPath.substring(1)).canonicalize();
|
| - var pathSegments = requestPath.segments();
|
| - if (pathSegments.length > 0) {
|
| - var basePath;
|
| - var relativePath;
|
| - if (pathSegments[0] == PREFIX_BUILDDIR) {
|
| - basePath = _buildDirectory;
|
| - relativePath = new Path(
|
| - pathSegments.skip(1).join('/'));
|
| - } else if (pathSegments[0] == PREFIX_DARTDIR) {
|
| - basePath = _dartDirectory;
|
| - relativePath = new Path(
|
| - pathSegments.skip(1).join('/'));
|
| - }
|
| - var packagesIndex = pathSegments.indexOf('packages');
|
| - if (packagesIndex != -1) {
|
| - var start = packagesIndex + 1;
|
| - basePath = _packageRoot;
|
| - relativePath = new Path(pathSegments.skip(start).join('/'));
|
| - }
|
| - if (basePath != null && relativePath != null) {
|
| - return basePath.join(relativePath);
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - Future<List<_Entry>> _listDirectory(Directory directory) {
|
| - var completer = new Completer();
|
| - var entries = [];
|
| -
|
| - directory.list().listen(
|
| - (FileSystemEntity fse) {
|
| - var filename = new Path(fse.path).filename;
|
| - if (fse is File) {
|
| - entries.add(new _Entry(filename, filename));
|
| - } else if (fse is Directory) {
|
| - entries.add(new _Entry(filename, '$filename/'));
|
| - }
|
| - },
|
| - onDone: () {
|
| - completer.complete(entries);
|
| - });
|
| - return completer.future;
|
| - }
|
| -
|
| - void _sendDirectoryListing(List<_Entry> entries,
|
| - HttpRequest request,
|
| - HttpResponse response) {
|
| - response.headers.set('Content-Type', 'text/html');
|
| - var header = '''<!DOCTYPE html>
|
| - <html>
|
| - <head>
|
| - <title>${request.uri.path}</title>
|
| - </head>
|
| - <body>
|
| - <code>
|
| - <div>${request.uri.path}</div>
|
| - <hr/>
|
| - <ul>''';
|
| - var footer = '''
|
| - </ul>
|
| - </code>
|
| - </body>
|
| - </html>''';
|
| -
|
| -
|
| - entries.sort();
|
| - response.write(header);
|
| - for (var entry in entries) {
|
| - response.write(
|
| - '<li><a href="${new Path(request.uri.path).append(entry.name)}">'
|
| - '${entry.displayName}</a></li>');
|
| - }
|
| - response.write(footer);
|
| - response.close();
|
| - response.done.catchError((e) {
|
| - DebugLogger.warning(
|
| - 'HttpServer: error while closing the response stream', e);
|
| - });
|
| - }
|
| -
|
| - void _sendFileContent(HttpRequest request,
|
| - HttpResponse response,
|
| - int allowedPort,
|
| - Path path,
|
| - File file) {
|
| - if (allowedPort != -1) {
|
| - var headerOrigin = request.headers.value('Origin');
|
| - var allowedOrigin;
|
| - if (headerOrigin != null) {
|
| - var origin = Uri.parse(headerOrigin);
|
| - // Allow loading from http://*:$allowedPort in browsers.
|
| - allowedOrigin =
|
| - '${origin.scheme}://${origin.host}:${allowedPort}';
|
| - } else {
|
| - // IE10 appears to be bugged and is not sending the Origin header
|
| - // when making CORS requests to the same domain but different port.
|
| - allowedOrigin = '*';
|
| - }
|
| -
|
| -
|
| - response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
|
| - response.headers.set('Access-Control-Allow-Credentials', 'true');
|
| - } else {
|
| - // No allowedPort specified. Allow from anywhere (but cross-origin
|
| - // requests *with credentials* will fail because you can't use "*").
|
| - response.headers.set("Access-Control-Allow-Origin", "*");
|
| - }
|
| - if (useContentSecurityPolicy) {
|
| - // Chrome respects the standardized Content-Security-Policy header,
|
| - // whereas Firefox and IE10 use X-Content-Security-Policy. Safari
|
| - // still uses the WebKit- prefixed version.
|
| - var content_header_value = "script-src 'self'; object-src 'self'";
|
| - for (var header in ["Content-Security-Policy",
|
| - "X-Content-Security-Policy"]) {
|
| - response.headers.set(header, content_header_value);
|
| - }
|
| - if (const ["safari"].contains(runtime)) {
|
| - response.headers.set("X-WebKit-CSP", content_header_value);
|
| - }
|
| - }
|
| - if (path.filename.endsWith('.html')) {
|
| - response.headers.set('Content-Type', 'text/html');
|
| - } else if (path.filename.endsWith('.js')) {
|
| - response.headers.set('Content-Type', 'application/javascript');
|
| - } else if (path.filename.endsWith('.dart')) {
|
| - response.headers.set('Content-Type', 'application/dart');
|
| - } else if (path.filename.endsWith('.css')) {
|
| - response.headers.set('Content-Type', 'text/css');
|
| - } else if (path.filename.endsWith('.xml')) {
|
| - response.headers.set('Content-Type', 'text/xml');
|
| - }
|
| - response.headers.removeAll("X-Frame-Options");
|
| - file.openRead().pipe(response).catchError((e) {
|
| - DebugLogger.warning(
|
| - 'HttpServer: error while closing the response stream', e);
|
| - });
|
| - }
|
| -
|
| - void _sendNotFound(HttpRequest request) {
|
| - bool isHarmlessPath(String path) {
|
| - return _HARMLESS_REQUEST_PATH_ENDINGS.any((pattern) {
|
| - return path.contains(pattern);
|
| - });
|
| - }
|
| - if (!isHarmlessPath(request.uri.path)) {
|
| - DebugLogger.warning('HttpServer: could not find file for request path: '
|
| - '"${request.uri.path}"');
|
| - }
|
| - var response = request.response;
|
| - response.statusCode = HttpStatus.NOT_FOUND;
|
| -
|
| - // Send a nice HTML page detailing the error message. Most browsers expect
|
| - // this, for example, Chrome will simply display a blank page if you don't
|
| - // provide any information. A nice side effect of this is to work around
|
| - // Firefox bug 1016313
|
| - // (https://bugzilla.mozilla.org/show_bug.cgi?id=1016313).
|
| - response.headers.set(HttpHeaders.CONTENT_TYPE, 'text/html');
|
| - String escapedPath = const HtmlEscape().convert(request.uri.path);
|
| - response.write("""
|
| -<!DOCTYPE html>
|
| -<html lang='en'>
|
| -<head>
|
| -<title>Not Found</title>
|
| -</head>
|
| -<body>
|
| -<h1>Not Found</h1>
|
| -<p style='white-space:pre'>The file '$escapedPath\' could not be found.</p>
|
| -</body>
|
| -</html>
|
| -""");
|
| - response.close();
|
| - response.done.catchError((e) {
|
| - DebugLogger.warning(
|
| - 'HttpServer: error while closing the response stream', e);
|
| - });
|
| - }
|
| -}
|
| -
|
| -// Helper class for displaying directory listings.
|
| -class _Entry implements Comparable {
|
| - final String name;
|
| - final String displayName;
|
| -
|
| - _Entry(this.name, this.displayName);
|
| -
|
| - int compareTo(_Entry other) {
|
| - return name.compareTo(other.name);
|
| - }
|
| -}
|
|
|