Add support for package spec to testing HttpServer.

BUG=https://github.com/dart-lang/sdk/issues/27065
R=terry@google.com

Review URL: https://codereview.chromium.org/2347423003 .
This commit is contained in:
William Hesse 2016-09-19 18:43:16 +02:00
parent 57f1193c01
commit 88a64b77bf
3 changed files with 95 additions and 102 deletions

View file

@ -1,9 +0,0 @@
# The test runner logic depends on `package:yaml`, because it is not structured
# as a pub package, we generated this file manually to contain all of yaml's
# transitive dependencies.
charcode:../third_party/pkg/charcode/lib/
collection:../third_party/pkg/collection/lib/
path:../third_party/pkg/path/lib/
source_span:../third_party/pkg/source_span/lib/
string_scanner:../third_party/pkg/string_scanner/lib/
yaml:../third_party/pkg/yaml/lib/

View file

@ -9,13 +9,10 @@ import 'dart:io';
import 'dart:convert' show HtmlEscape; import 'dart:convert' show HtmlEscape;
import 'path.dart';
import 'test_suite.dart'; // For TestUtils. 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 'vendored_pkg/args/args.dart';
import 'utils.dart'; import 'utils.dart';
import 'package:package_resolver/package_resolver.dart';
class DispatchingServer { class DispatchingServer {
HttpServer server; HttpServer server;
@ -52,9 +49,10 @@ class DispatchingServer {
/// directory (i.e. '$DartDirectory/X'). /// directory (i.e. '$DartDirectory/X').
/// /root_build/X: This will serve the corresponding file from the build /// /root_build/X: This will serve the corresponding file from the build
/// directory (i.e. '$BuildDirectory/X'). /// directory (i.e. '$BuildDirectory/X').
/// /FOO/packages/BAR: This will serve the corresponding file from the packages /// /FOO/packages/PAZ/BAR: This will serve files from the packages listed in
/// directory (i.e. '$BuildDirectory/packages/BAR') or the /// the package spec .packages. Supports a package
/// passed-in package root /// root or custom package spec, and uses [dart_dir]/.packages
/// as the default. This will serve file lib/BAR from the package PAZ.
/// /ws: This will upgrade the connection to a WebSocket connection and echo /// /ws: This will upgrade the connection to a WebSocket connection and echo
/// all data back to the client. /// all data back to the client.
/// ///
@ -64,10 +62,6 @@ class DispatchingServer {
const PREFIX_BUILDDIR = 'root_build'; const PREFIX_BUILDDIR = 'root_build';
const PREFIX_DARTDIR = 'root_dart'; 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) { main(List<String> arguments) {
// This script is in [dart]/tools/testing/dart. // This script is in [dart]/tools/testing/dart.
TestUtils.setDartDirUri(Platform.script.resolve('../../..')); TestUtils.setDartDirUri(Platform.script.resolve('../../..'));
@ -97,7 +91,7 @@ main(List<String> arguments) {
if (args['help']) { if (args['help']) {
print(parser.getUsage()); print(parser.getUsage());
} else { } else {
var servers = new TestingServers(new Path(args['build-directory']), var servers = new TestingServers(args['build-directory'],
args['csp'], args['runtime'], null, args['package-root'], args['csp'], args['runtime'], null, args['package-root'],
args['packages']); args['packages']);
var port = int.parse(args['port']); var port = int.parse(args['port']);
@ -129,26 +123,35 @@ class TestingServers {
]; ];
List _serverList = []; List _serverList = [];
Path _buildDirectory = null; Uri _buildDirectory = null;
Path _dartDirectory = null; Uri _dartDirectory = null;
Path _packageRoot; Uri _packageRoot;
Path _packages; Uri _packages;
final bool useContentSecurityPolicy; final bool useContentSecurityPolicy;
final String runtime; final String runtime;
DispatchingServer _server; DispatchingServer _server;
SyncPackageResolver _resolver;
TestingServers(Path buildDirectory, this.useContentSecurityPolicy, TestingServers(String buildDirectory, this.useContentSecurityPolicy,
[String this.runtime = 'none', [String this.runtime = 'none',
String dartDirectory, String dartDirectory,
String packageRoot, String packageRoot,
String packages]) { String packages]) {
_buildDirectory = TestUtils.absolutePath(buildDirectory); _buildDirectory = Uri.base.resolveUri(new Uri.directory(buildDirectory));
_dartDirectory = if (dartDirectory == null) {
dartDirectory == null ? TestUtils.dartDir : new Path(dartDirectory); _dartDirectory = TestUtils.dartDirUri;
_packageRoot = packageRoot == null } else {
? (packages == null ? _buildDirectory.append('packages') : null) _dartDirectory = Uri.base.resolveUri(new Uri.directory(dartDirectory));
: new Path(packageRoot); }
_packages = packages == null ? null : new Path(packages); if (packageRoot == null ) {
if (packages == null ) {
_packages = _dartDirectory.resolve('.packages');
} else {
_packages = new Uri.file(packages);
}
} else {
_packageRoot = new Uri.directory(packageRoot);
}
} }
int get port => _serverList[0].port; int get port => _serverList[0].port;
@ -163,23 +166,37 @@ class TestingServers {
* "Access-Control-Allow-Origin: client:port1 * "Access-Control-Allow-Origin: client:port1
* "Access-Control-Allow-Credentials: true" * "Access-Control-Allow-Credentials: true"
*/ */
Future startServers(String host, {int port: 0, int crossOriginPort: 0}) { Future startServers(String host,
return _startHttpServer(host, port: port).then((server) { {int port: 0,
_server = server; int crossOriginPort: 0}) async {
return _startHttpServer(host, if (_packages != null) {
port: crossOriginPort, allowedPort: _serverList[0].port); _resolver = await SyncPackageResolver.loadConfig(_packages);
}); } else {
_resolver = new SyncPackageResolver.root(_packageRoot);
}
_server = await _startHttpServer(host, port: port);
await _startHttpServer(host, port: crossOriginPort,
allowedPort: _serverList[0].port);
} }
String httpServerCommandline() { String httpServerCommandline() {
var dart = Platform.resolvedExecutable; var dart = Platform.resolvedExecutable;
var dartDir = TestUtils.dartDir; var script = _dartDirectory.resolve('tools/testing/dart/http_server.dart');
var script = dartDir.join(new Path("tools/testing/dart/http_server.dart")); var buildDirectory = _buildDirectory.toFilePath();
var buildDirectory = _buildDirectory.toNativePath(); var command = [dart, script.toFilePath(),
var csp = useContentSecurityPolicy ? '--csp ' : ''; '-p', port,
return '$dart $script -p $port -c $crossOriginPort $csp' '-c', crossOriginPort,
'--build-directory=$buildDirectory --runtime=$runtime ' '--build-directory=$buildDirectory',
'--package-root=$_packageRoot'; '--runtime=$runtime'];
if (useContentSecurityPolicy) {
command.add('--csp');
}
if (_packages != null) {
command.add('--packages=${_packages.toFilePath()}');
} else if (_packageRoot != null) {
command.add('--package-root=${_packageRoot.toFilePath()}');
}
return command.join(' ');
} }
void stopServers() { void stopServers() {
@ -208,30 +225,24 @@ class TestingServers {
}); });
} }
void _handleFileOrDirectoryRequest(HttpRequest request, int allowedPort) { _handleFileOrDirectoryRequest(HttpRequest request,
int allowedPort) async {
// Enable browsers to cache file/directory responses. // Enable browsers to cache file/directory responses.
var response = request.response; var response = request.response;
response.headers response.headers
.set("Cache-Control", "max-age=$_CACHE_EXPIRATION_IN_SECONDS"); .set("Cache-Control", "max-age=$_CACHE_EXPIRATION_IN_SECONDS");
var path = _getFilePathFromRequestPath(request.uri.path); var path = _getFileUriFromRequestUri(request.uri);
if (path != null) { if (path != null) {
var file = new File(path.toNativePath()); var file = new File.fromUri(path);
file.exists().then((exists) { var directory = new Directory.fromUri(path);
if (exists) { if (await file.exists()){
_sendFileContent(request, response, allowedPort, path, file); _sendFileContent(request, response, allowedPort, file);
} else { } else if (await directory.exists()) {
var directory = new Directory(path.toNativePath()); _sendDirectoryListing(
directory.exists().then((exists) { await _listDirectory(directory), request, response);
if (exists) { } else {
_listDirectory(directory).then((entries) { _sendNotFound(request);
_sendDirectoryListing(entries, request, response); }
});
} else {
_sendNotFound(request);
}
});
}
});
} else { } else {
if (request.uri.path == '/') { if (request.uri.path == '/') {
var entries = [ var entries = [
@ -277,33 +288,21 @@ class TestingServers {
}); });
} }
Path _getFilePathFromRequestPath(String urlRequestPath) { Uri _getFileUriFromRequestUri(Uri request) {
// Go to the top of the file to see an explanation of the URL path scheme. // Go to the top of the file to see an explanation of the URL path scheme.
var requestPath = new Path(urlRequestPath.substring(1)).canonicalize(); List<String> pathSegments = request.normalizePath().pathSegments;
var pathSegments = requestPath.segments(); if (pathSegments.length == 0) return null;
if (pathSegments.length > 0) { int packagesIndex = pathSegments.indexOf('packages');
var basePath; if (packagesIndex != -1) {
var relativePath; var packageUri = new Uri(scheme: 'package',
if (pathSegments[0] == PREFIX_BUILDDIR) { pathSegments: pathSegments.skip(packagesIndex + 1));
basePath = _buildDirectory; return _resolver.resolveUri(packageUri);
relativePath = new Path(pathSegments.skip(1).join('/')); }
} else if (pathSegments[0] == PREFIX_DARTDIR) { if (pathSegments[0] == PREFIX_BUILDDIR) {
basePath = _dartDirectory; return _buildDirectory.resolve(pathSegments.skip(1).join('/'));
relativePath = new Path(pathSegments.skip(1).join('/')); }
} if (pathSegments[0] == PREFIX_DARTDIR) {
var packagesIndex = pathSegments.indexOf('packages'); return _dartDirectory.resolve(pathSegments.skip(1).join('/'));
if (packagesIndex != -1) {
if (_packages != null) {
// TODO(27065): Package spec file not supported by http server yet
return null;
}
var start = packagesIndex + 1;
basePath = _packageRoot;
relativePath = new Path(pathSegments.skip(start).join('/'));
}
if (basePath != null && relativePath != null) {
return basePath.join(relativePath);
}
} }
return null; return null;
} }
@ -313,11 +312,13 @@ class TestingServers {
var entries = []; var entries = [];
directory.list().listen((FileSystemEntity fse) { directory.list().listen((FileSystemEntity fse) {
var filename = new Path(fse.path).filename; var segments = fse.uri.pathSegments;
if (fse is File) { if (fse is File) {
var filename = segments.last;
entries.add(new _Entry(filename, filename)); entries.add(new _Entry(filename, filename));
} else if (fse is Directory) { } else if (fse is Directory) {
entries.add(new _Entry(filename, '$filename/')); var dirname = segments[segments.length - 2];
entries.add(new _Entry(dirname, '$dirname/'));
} }
}, onDone: () { }, onDone: () {
completer.complete(entries); completer.complete(entries);
@ -348,7 +349,7 @@ class TestingServers {
response.write(header); response.write(header);
for (var entry in entries) { for (var entry in entries) {
response.write( response.write(
'<li><a href="${new Path(request.uri.path).append(entry.name)}">' '<li><a href="${request.uri}/${entry.name}">'
'${entry.displayName}</a></li>'); '${entry.displayName}</a></li>');
} }
response.write(footer); response.write(footer);
@ -360,7 +361,7 @@ class TestingServers {
} }
void _sendFileContent(HttpRequest request, HttpResponse response, void _sendFileContent(HttpRequest request, HttpResponse response,
int allowedPort, Path path, File file) { int allowedPort, File file) {
if (allowedPort != -1) { if (allowedPort != -1) {
var headerOrigin = request.headers.value('Origin'); var headerOrigin = request.headers.value('Origin');
var allowedOrigin; var allowedOrigin;
@ -396,15 +397,15 @@ class TestingServers {
response.headers.set("X-WebKit-CSP", content_header_value); response.headers.set("X-WebKit-CSP", content_header_value);
} }
} }
if (path.filename.endsWith('.html')) { if (file.path.endsWith('.html')) {
response.headers.set('Content-Type', 'text/html'); response.headers.set('Content-Type', 'text/html');
} else if (path.filename.endsWith('.js')) { } else if (file.path.endsWith('.js')) {
response.headers.set('Content-Type', 'application/javascript'); response.headers.set('Content-Type', 'application/javascript');
} else if (path.filename.endsWith('.dart')) { } else if (file.path.endsWith('.dart')) {
response.headers.set('Content-Type', 'application/dart'); response.headers.set('Content-Type', 'application/dart');
} else if (path.filename.endsWith('.css')) { } else if (file.path.endsWith('.css')) {
response.headers.set('Content-Type', 'text/css'); response.headers.set('Content-Type', 'text/css');
} else if (path.filename.endsWith('.xml')) { } else if (file.path.endsWith('.xml')) {
response.headers.set('Content-Type', 'text/xml'); response.headers.set('Content-Type', 'text/xml');
} }
response.headers.removeAll("X-Frame-Options"); response.headers.removeAll("X-Frame-Options");

View file

@ -132,11 +132,12 @@ Future testConfigurations(List<Map> configurations) async {
// server for cross-domain tests can be found by calling // server for cross-domain tests can be found by calling
// getCrossOriginPortNumber(). // getCrossOriginPortNumber().
var servers = new TestingServers( var servers = new TestingServers(
new Path(TestUtils.buildDir(conf)), TestUtils.buildDir(conf),
useContentSecurityPolicy, useContentSecurityPolicy,
conf['runtime'], conf['runtime'],
null, null,
conf['package_root']); conf['package_root'],
conf['packages']);
serverFutures.add(servers.startServers(conf['local_ip'], serverFutures.add(servers.startServers(conf['local_ip'],
port: conf['test_server_port'], port: conf['test_server_port'],
crossOriginPort: conf['test_server_cross_origin_port'])); crossOriginPort: conf['test_server_cross_origin_port']));