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() { |