| Index: tools/testing/dart/http_server.dart
|
| diff --git a/tools/testing/dart/http_server.dart b/tools/testing/dart/http_server.dart
|
| index 06b87da3960551c77cfe4386dad504c64c41d7e0..6ab495b7af70baa95076b66026ba5a2b75656377 100644
|
| --- a/tools/testing/dart/http_server.dart
|
| +++ b/tools/testing/dart/http_server.dart
|
| @@ -4,6 +4,7 @@
|
|
|
| library http_server;
|
|
|
| +import 'dart:async';
|
| import 'dart:io';
|
| import 'dart:isolate';
|
| import 'dart:uri';
|
| @@ -12,6 +13,30 @@ import 'test_suite.dart'; // For TestUtils.
|
| // 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';
|
| +
|
| +
|
| +/// 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')
|
| +///
|
| +/// 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() {
|
| /** Convenience method for local testing. */
|
| @@ -43,7 +68,11 @@ main() {
|
| .join(new Path('../../test.dart'))
|
| .canonicalize()
|
| .toNativePath();
|
| + // Note: args['package-root'] is always the build directory. We have the
|
| + // implicit assumption that it contains the 'packages' subdirectory.
|
| + // TODO: We should probably rename 'package-root' to 'build-directory'.
|
| TestingServerRunner._packageRootDir = new Path(args['package-root']);
|
| + TestingServerRunner._buildDirectory = new Path(args['package-root']);
|
| var network = args['network'];
|
| TestingServerRunner.startHttpServer(network,
|
| port: int.parse(args['port']));
|
| @@ -63,96 +92,127 @@ main() {
|
| class TestingServerRunner {
|
| static List serverList = [];
|
| static Path _packageRootDir = null;
|
| + static Path _buildDirectory = null;
|
|
|
| // Added as a getter so that the function will be called again each time the
|
| // default request handler closure is executed.
|
| static Path get packageRootDir => _packageRootDir;
|
| + static Path get buildDirectory => _buildDirectory;
|
|
|
| static setPackageRootDir(Map configuration) {
|
| - _packageRootDir = TestUtils.currentWorkingDirectory.join(
|
| + _packageRootDir = TestUtils.absolutePath(
|
| + new Path(TestUtils.buildDir(configuration)));
|
| + }
|
| +
|
| + static setBuildDir(Map configuration) {
|
| + _buildDirectory = TestUtils.absolutePath(
|
| new Path(TestUtils.buildDir(configuration)));
|
| }
|
|
|
| static startHttpServer(String host, {int allowedPort:-1, int port: 0}) {
|
| - var basePath = TestUtils.dartDir();
|
| var httpServer = new HttpServer();
|
| - var packagesDirName = 'packages';
|
| httpServer.onError = (e) {
|
| - // TODO(ricow): Once we have a debug log we should write this out there.
|
| - print('Test http server error: $e');
|
| + DebugLogger.error('HttpServer: an error occured: $e');
|
| };
|
| - httpServer.defaultRequestHandler = (request, resp) {
|
| - var requestPath = new Path(request.path.substring(1)).canonicalize();
|
| - var path = basePath.join(requestPath);
|
| - var file = new File(path.toNativePath());
|
| -
|
| - if (requestPath.segments().contains(packagesDirName)) {
|
| - // Essentially implement the packages path rewriting, so we don't have
|
| - // to pass environment variables to the browsers.
|
| - var requestPathStr = requestPath.toNativePath().substring(
|
| - requestPath.toNativePath().indexOf(packagesDirName));
|
| - path = packageRootDir.append(requestPathStr);
|
| - file = new File(path.toNativePath());
|
| - }
|
| + httpServer.defaultRequestHandler = (request, response) {
|
| + handleFileOrDirectoryRequest(request, response, allowedPort);
|
| + };
|
| + httpServer.addRequestHandler(
|
| + (req) => req.path == "/echo", handleEchoRequest);
|
| +
|
| + httpServer.listen(host, port);
|
| + serverList.add(httpServer);
|
| + }
|
| +
|
| +
|
| + static void handleFileOrDirectoryRequest(HttpRequest request,
|
| + HttpResponse response,
|
| + int allowedPort) {
|
| + var path = getFilePathFromRequestPath(request.path);
|
| + if (path != null) {
|
| + var file = new File.fromPath(path);
|
| file.exists().then((exists) {
|
| if (exists) {
|
| - if (allowedPort != -1) {
|
| - if (request.headers.value('Origin') != null) {
|
| - var origin = new Uri(request.headers.value('Origin'));
|
| - // Allow loading from http://*:$allowedPort in browsers.
|
| - var allowedOrigin =
|
| - '${origin.scheme}://${origin.domain}:${allowedPort}';
|
| - resp.headers.set("Access-Control-Allow-Origin", allowedOrigin);
|
| - resp.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 "*").
|
| - resp.headers.set("Access-Control-Allow-Origin", "*");
|
| - }
|
| - if (path.toNativePath().endsWith('.html')) {
|
| - resp.headers.set('Content-Type', 'text/html');
|
| - } else if (path.toNativePath().endsWith('.js')) {
|
| - resp.headers.set('Content-Type', 'application/javascript');
|
| - } else if (path.toNativePath().endsWith('.dart')) {
|
| - resp.headers.set('Content-Type', 'application/dart');
|
| - }
|
| - file.openInputStream().pipe(resp.outputStream);
|
| + sendFileContent(request, response, allowedPort, path, file);
|
| } else {
|
| var directory = new Directory.fromPath(path);
|
| directory.exists().then((exists) {
|
| - if (!exists) {
|
| - sendNotFound(resp);
|
| + if (exists) {
|
| + listDirectory(directory).then((entries) {
|
| + sendDirectoryListing(entries, request, response);
|
| + });
|
| } else {
|
| - sendDirectoryListing(directory, request, resp);
|
| + sendNotFound(request, response);
|
| }
|
| });
|
| }
|
| });
|
| - };
|
| -
|
| - // Echos back the contents of the request as the response data.
|
| - httpServer.addRequestHandler((req) => req.path == "/echo", (request, resp) {
|
| - resp.headers.set("Access-Control-Allow-Origin", "*");
|
| -
|
| - request.inputStream.pipe(resp.outputStream);
|
| - });
|
| + } else {
|
| + if (request.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, response);
|
| + }
|
| + }
|
| + }
|
|
|
| - httpServer.listen(host, port);
|
| - serverList.add(httpServer);
|
| + static void handleEchoRequest(HttpRequest request, HttpResponse response) {
|
| + response.headers.set("Access-Control-Allow-Origin", "*");
|
| + request.inputStream.pipe(response.outputStream);
|
| }
|
|
|
| - static void sendNotFound(HttpResponse response) {
|
| - response.statusCode = HttpStatus.NOT_FOUND;
|
| - try {
|
| - response.outputStream.close();
|
| - } catch (e) {
|
| - if (e is StreamException) {
|
| - print('Test http_server error closing the response stream: $e');
|
| - } else {
|
| - throw e;
|
| + static 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.getRange(1, pathSegments.length - 1).join('/'));
|
| + } else if (pathSegments[0] == PREFIX_DARTDIR) {
|
| + basePath = TestUtils.dartDir();
|
| + relativePath = new Path(
|
| + pathSegments.getRange(1, pathSegments.length - 1).join('/'));
|
| + }
|
| + var packagesDirName = 'packages';
|
| + var packagesIndex = pathSegments.indexOf(packagesDirName);
|
| + if (packagesIndex != -1) {
|
| + var start = packagesIndex + 1;
|
| + var length = pathSegments.length - start;
|
| + basePath = _packageRootDir.append(packagesDirName);
|
| + relativePath = new Path(
|
| + pathSegments.getRange(start, length).join('/'));
|
| + }
|
| + if (basePath != null && relativePath != null) {
|
| + return basePath.join(relativePath);
|
| }
|
| }
|
| + return null;
|
| + }
|
| +
|
| + static Future<List<_Entry>> listDirectory(Directory directory) {
|
| + var completer = new Completer();
|
| + var entries = [];
|
| +
|
| + directory.list()
|
| + ..onFile = (filepath) {
|
| + var filename = new Path(filepath).filename;
|
| + entries.add(new _Entry(filename, filename));
|
| + }
|
| + ..onDir = (dirpath) {
|
| + var filename = new Path(dirpath).filename;
|
| + entries.add(new _Entry(filename, '$filename/'));
|
| + }
|
| + ..onDone = (_) {
|
| + completer.complete(entries);
|
| + };
|
| + return completer.future;
|
| }
|
|
|
| /**
|
| @@ -162,8 +222,9 @@ class TestingServerRunner {
|
| * This is intended to make it easier to browse tests when manually running
|
| * tests against this test server.
|
| */
|
| - static void sendDirectoryListing(Directory directory, HttpRequest request,
|
| - HttpResponse response) {
|
| + static void sendDirectoryListing(entries,
|
| + HttpRequest request,
|
| + HttpResponse response) {
|
| response.headers.set('Content-Type', 'text/html');
|
| var header = '''<!DOCTYPE html>
|
| <html>
|
| @@ -181,30 +242,62 @@ class TestingServerRunner {
|
| </body>
|
| </html>''';
|
|
|
| - var entries = [];
|
|
|
| - directory.list()
|
| - ..onFile = (filepath) {
|
| - var filename = new Path(filepath).filename;
|
| - entries.add(new _Entry(filename, filename));
|
| - }
|
| - ..onDir = (dirpath) {
|
| - var filename = new Path(dirpath).filename;
|
| - entries.add(new _Entry(filename, '$filename/'));
|
| + entries.sort();
|
| + response.outputStream.writeString(header);
|
| + for (var entry in entries) {
|
| + response.outputStream.writeString(
|
| + '<li><a href="${new Path(request.path).append(entry.name)}">'
|
| + '${entry.displayName}</a></li>');
|
| + }
|
| + response.outputStream.writeString(footer);
|
| + response.outputStream.close();
|
| + }
|
| +
|
| + static void sendFileContent(HttpRequest request,
|
| + HttpResponse response,
|
| + int allowedPort,
|
| + Path path,
|
| + File file) {
|
| + if (allowedPort != -1) {
|
| + var origin = new Uri(request.headers.value('Origin'));
|
| + // Allow loading from http://*:$allowedPort in browsers.
|
| + var allowedOrigin =
|
| + '${origin.scheme}://${origin.domain}:${allowedPort}';
|
| + 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 (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');
|
| + }
|
| + file.openInputStream().pipe(response.outputStream);
|
| + }
|
| +
|
| + static void sendNotFound(HttpRequest request, HttpResponse response) {
|
| + // NOTE: Since some tests deliberately try to access non-existent files.
|
| + // We might want to remove this warning (otherwise it will show
|
| + // up in the debug.log every time).
|
| + DebugLogger.warning('HttpServer: could not find file for request path: '
|
| + '"${request.path}"');
|
| + response.statusCode = HttpStatus.NOT_FOUND;
|
| + try {
|
| + response.outputStream.close();
|
| + } catch (e) {
|
| + if (e is StreamException) {
|
| + DebugLogger.warning('HttpServer: error while closing the response '
|
| + 'stream: $e');
|
| + } else {
|
| + throw e;
|
| }
|
| - ..onDone = (_) {
|
| - var requestPath = new Path.raw(request.path);
|
| - entries.sort();
|
| -
|
| - response.outputStream.writeString(header);
|
| - for (var entry in entries) {
|
| - response.outputStream.writeString(
|
| - '<li><a href="${requestPath.append(entry.name)}">'
|
| - '${entry.displayName}</a></li>');
|
| - }
|
| - response.outputStream.writeString(footer);
|
| - response.outputStream.close();
|
| - };
|
| + }
|
| }
|
|
|
| static terminateHttpServers() {
|
|
|