Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(609)

Unified Diff: lib/src/runner/browser/server.dart

Issue 1076803003: Change the URL-space exposed by the browser server. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Changelog + pubspec Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: lib/src/runner/browser/server.dart
diff --git a/lib/src/runner/browser/server.dart b/lib/src/runner/browser/server.dart
index a98bcb08dc55c96864391ad7faba476f6b07299e..1c2ee38ead6e8f418b1373894b3b58dde7b9e145 100644
--- a/lib/src/runner/browser/server.dart
+++ b/lib/src/runner/browser/server.dart
@@ -18,6 +18,7 @@ import 'package:shelf_web_socket/shelf_web_socket.dart';
import '../../backend/suite.dart';
import '../../backend/test_platform.dart';
import '../../util/io.dart';
+import '../../util/path_handler.dart';
import '../../util/one_off_handler.dart';
import '../../utils.dart';
import '../load_exception.dart';
@@ -33,6 +34,9 @@ import 'firefox.dart';
class BrowserServer {
/// Starts the server.
///
+ /// [root] is the root directory that the server should serve. It defaults to
+ /// the working directory.
+ ///
/// If [packageRoot] is passed, it's used for all package imports when
/// compiling tests to JS. Otherwise, the package root is inferred from the
/// location of the source file.
@@ -41,25 +45,35 @@ class BrowserServer {
/// instance at that URL rather than from the filesystem.
///
/// If [color] is true, console colors will be used when compiling Dart.
- static Future<BrowserServer> start({String packageRoot, Uri pubServeUrl,
- bool color: false}) {
- var server = new BrowserServer._(packageRoot, pubServeUrl, color);
+ static Future<BrowserServer> start({String root, String packageRoot,
+ Uri pubServeUrl, bool color: false}) {
+ var server = new BrowserServer._(root, packageRoot, pubServeUrl, color);
return server._load().then((_) => server);
}
/// The underlying HTTP server.
HttpServer _server;
+ /// A randomly-generated secret.
+ ///
+ /// This is used to ensure that other users on the same system can't snoop
+ /// on data being served through this server.
+ final _secret = randomBase64(24, urlSafe: true);
+
/// The URL for this server.
- Uri get url => baseUrlForAddress(_server.address, _server.port);
+ Uri get url => baseUrlForAddress(_server.address, _server.port)
+ .resolve(_secret + "/");
- /// a [OneOffHandler] for servicing WebSocket connections for
+ /// A [OneOffHandler] for servicing WebSocket connections for
/// [BrowserManager]s.
///
/// This is one-off because each [BrowserManager] can only connect to a single
/// WebSocket,
final _webSocketHandler = new OneOffHandler();
+ /// A [PathHandler] used to serve compiled JS.
+ final _jsHandler = new PathHandler();
+
/// The [CompilerPool] managing active instances of `dart2js`.
///
/// This is `null` if tests are loaded from `pub serve`.
@@ -68,6 +82,9 @@ class BrowserServer {
/// The temporary directory in which compiled JS is emitted.
final String _compiledDir;
+ /// The root directory served statically by this server.
+ final String _root;
+
/// The package root which is passed to `dart2js`.
final String _packageRoot;
@@ -109,8 +126,9 @@ class BrowserServer {
/// per run, rather than one per browser per run.
final _compileFutures = new Map<String, Future>();
- BrowserServer._(this._packageRoot, Uri pubServeUrl, bool color)
- : _pubServeUrl = pubServeUrl,
+ BrowserServer._(String root, this._packageRoot, Uri pubServeUrl, bool color)
+ : _root = root == null ? p.current : root,
+ _pubServeUrl = pubServeUrl,
_compiledDir = pubServeUrl == null ? createTempDir() : null,
_http = pubServeUrl == null ? null : new HttpClient(),
_compilers = new CompilerPool(color: color);
@@ -121,19 +139,70 @@ class BrowserServer {
.add(_webSocketHandler.handler);
if (_pubServeUrl == null) {
- var staticPath = p.join(libDir(packageRoot: _packageRoot),
- 'src/runner/browser/static');
cascade = cascade
- .add(createStaticHandler(staticPath, defaultDocument: 'index.html'))
- .add(createStaticHandler(_compiledDir,
- defaultDocument: 'index.html'));
+ .add(_createPackagesHandler())
+ .add(_jsHandler.handler)
+ .add(_wrapperHandler)
+ .add(createStaticHandler(_root));
}
- return shelf_io.serve(cascade.handler, 'localhost', 0).then((server) {
+ var pipeline = new shelf.Pipeline()
+ .addMiddleware(nestingMiddleware(_secret))
+ .addHandler(cascade.handler);
+
+ return shelf_io.serve(pipeline, 'localhost', 0).then((server) {
_server = server;
});
}
+ /// Returns a handler that serves the contents of the "packages/" directory
+ /// for any URL that contains "packages/".
+ ///
+ /// This is a factory so it can wrap a static handler.
+ shelf.Handler _createPackagesHandler() {
+ var packageRoot = _packageRoot == null
+ ? p.join(_root, 'packages')
+ : _packageRoot;
+ var staticHandler =
+ createStaticHandler(packageRoot, serveFilesOutsidePath: true);
+
+ return (request) {
+ var segments = p.url.split(shelfUrl(request).path);
+
+ for (var i = 0; i < segments.length; i++) {
+ if (segments[i] != "packages") continue;
+ return staticHandler(
+ shelfChange(request, path: p.url.joinAll(segments.take(i + 1))));
+ }
+
+ return new shelf.Response.notFound("Not found.");
+ };
+ }
+
+ /// A handler that serves wrapper HTML to bootstrap tests.
+ shelf.Response _wrapperHandler(shelf.Request request) {
+ var path = p.fromUri(shelfUrl(request));
+ var withoutExtensions = p.withoutExtension(p.withoutExtension(path));
+ var base = p.basename(withoutExtensions);
+
+ if (path.endsWith(".browser_test.html")) {
+ // TODO(nweiz): support user-authored HTML files.
+ return new shelf.Response.ok('''
+<!DOCTYPE html>
+<html>
+<head>
+ <title>${HTML_ESCAPE.convert(base)}.dart Test</title>
+ <script type="application/javascript"
+ src="${HTML_ESCAPE.convert(base)}.browser_test.dart.js">
+ </script>
+</head>
+</html>
+''', headers: {'Content-Type': 'text/html'});
+ }
+
+ return new shelf.Response.notFound('Not found.');
+ }
+
/// Loads the test suite at [path] on the browser [browser].
///
/// This will start a browser to load the suite if one isn't already running.
@@ -145,22 +214,18 @@ class BrowserServer {
return new Future.sync(() {
if (_pubServeUrl != null) {
- var suitePrefix = p.withoutExtension(p.relative(path, from: 'test')) +
+ var suitePrefix = p.relative(path, from: p.join(_root, 'test')) +
'.browser_test';
var jsUrl = _pubServeUrl.resolve('$suitePrefix.dart.js');
- return _pubServeSuite(path, jsUrl)
- .then((_) => _pubServeUrl.resolve('$suitePrefix.html'));
- } else {
- return _compileSuite(path).then((dir) {
- if (_closed) return null;
-
- // Add a trailing slash because at least on Chrome, the iframe's
- // window.location.href will do so automatically, and if that differs
- // from the original URL communication will fail.
- return url.resolve(
- "/" + p.toUri(p.relative(dir, from: _compiledDir)).path + "/");
- });
+ return _pubServeSuite(path, jsUrl).then((_) =>
+ _pubServeUrl.resolve('$suitePrefix.html'));
}
+
+ return _compileSuite(path).then((_) {
+ if (_closed) return null;
+ return url.resolveUri(
+ p.toUri(p.relative(path, from: _root) + ".browser_test.html"));
+ });
}).then((suiteUrl) {
if (_closed) return null;
@@ -210,27 +275,24 @@ class BrowserServer {
/// Compile the test suite at [dartPath] to JavaScript.
///
- /// Returns a [Future] that completes to the path to the JavaScript.
- Future<String> _compileSuite(String dartPath) {
+ /// Once the suite has been compiled, it's added to [_jsHandler] so it can be
+ /// served.
+ Future _compileSuite(String dartPath) {
return _compileFutures.putIfAbsent(dartPath, () {
var dir = new Directory(_compiledDir).createTempSync('test_').path;
var jsPath = p.join(dir, p.basename(dartPath) + ".js");
+
return _compilers.compile(dartPath, jsPath,
packageRoot: packageRootFor(dartPath, _packageRoot))
.then((_) {
- if (_closed) return null;
+ if (_closed) return;
- // TODO(nweiz): support user-authored HTML files.
- new File(p.join(dir, "index.html")).writeAsStringSync('''
-<!DOCTYPE html>
-<html>
-<head>
- <title>${HTML_ESCAPE.convert(dartPath)} Test</title>
- <script src="${HTML_ESCAPE.convert(p.basename(jsPath))}"></script>
-</head>
-</html>
-''');
- return dir;
+ _jsHandler.add(
+ p.relative(dartPath, from: _root) + '.browser_test.dart.js',
+ (request) {
+ return new shelf.Response.ok(new File(jsPath).readAsStringSync(),
+ headers: {'Content-Type': 'application/javascript'});
+ });
});
});
}
@@ -248,13 +310,10 @@ class BrowserServer {
completer.complete(new BrowserManager(webSocket));
}));
- var webSocketUrl = url.replace(scheme: 'ws', path: '/$path');
+ var webSocketUrl = url.replace(scheme: 'ws').resolve(path);
- var hostUrl = url;
- if (_pubServeUrl != null) {
- hostUrl = _pubServeUrl.resolve(
- '/packages/test/src/runner/browser/static/');
- }
+ var hostUrl = (_pubServeUrl == null ? url : _pubServeUrl)
+ .resolve('packages/test/src/runner/browser/static/index.html');
var browser = _newBrowser(hostUrl.replace(queryParameters: {
'managerUrl': webSocketUrl.toString()
« no previous file with comments | « lib/pub_serve.dart ('k') | lib/src/runner/loader.dart » ('j') | test/runner/loader_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698