| Index: dart/pkg/dart2js_incremental/lib/server.dart
|
| diff --git a/dart/pkg/dart2js_incremental/lib/server.dart b/dart/pkg/dart2js_incremental/lib/server.dart
|
| index 08c5d565425f206d777c404842ecc524397d19d4..a31c64d96400a511d1e8d276a3d349510653ed64 100644
|
| --- a/dart/pkg/dart2js_incremental/lib/server.dart
|
| +++ b/dart/pkg/dart2js_incremental/lib/server.dart
|
| @@ -2,7 +2,7 @@
|
| // 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 trydart.projectServer;
|
| +library dart2js_incremental.server;
|
|
|
| import 'dart:io';
|
|
|
| @@ -15,122 +15,17 @@ import 'dart:convert' show
|
| JSON,
|
| UTF8;
|
|
|
| -class WatchHandler {
|
| - final WebSocket socket;
|
| -
|
| - final Set<String> watchedFiles;
|
| -
|
| - static final Set<WatchHandler> handlers = new Set<WatchHandler>();
|
| -
|
| - static const Map<int, String> fsEventNames = const <int, String>{
|
| - FileSystemEvent.CREATE: 'create',
|
| - FileSystemEvent.DELETE: 'delete',
|
| - FileSystemEvent.MODIFY: 'modify',
|
| - FileSystemEvent.MOVE: 'move',
|
| - };
|
| -
|
| - WatchHandler(this.socket, Iterable<String> watchedFiles)
|
| - : this.watchedFiles = watchedFiles.toSet();
|
| -
|
| - handleFileSystemEvent(FileSystemEvent event) {
|
| - if (event.isDirectory) return;
|
| - String type = fsEventNames[event.type];
|
| - if (type == null) type = 'unknown';
|
| - String path = new Uri.file(event.path).pathSegments.last;
|
| - shouldIgnore(type, path).then((bool ignored) {
|
| - if (ignored) return;
|
| - socket.add(JSON.encode({type: [path]}));
|
| - });
|
| - }
|
| -
|
| - Future<bool> shouldIgnore(String type, String path) {
|
| - switch (type) {
|
| - case 'create':
|
| - return new Future<bool>.value(!watchedFiles.contains(path));
|
| - case 'delete':
|
| - return Conversation.listProjectFiles().then((List<String> files) {
|
| - watchedFiles
|
| - ..retainAll(files)
|
| - ..addAll(files);
|
| - return watchedFiles.contains(path);
|
| - });
|
| - case 'modify':
|
| - return new Future<bool>.value(false);
|
| - default:
|
| - print('Unhandled fs-event for $path ($type).');
|
| - return new Future<bool>.value(true);
|
| - }
|
| - }
|
| -
|
| - onData(_) {
|
| - // TODO(ahe): Move POST code here?
|
| - }
|
| -
|
| - onDone() {
|
| - handlers.remove(this);
|
| - }
|
| -
|
| - static handleWebSocket(WebSocket socket) {
|
| - Conversation.ensureProjectWatcher();
|
| - Conversation.listProjectFiles().then((List<String> files) {
|
| - socket.add(JSON.encode({'create': files}));
|
| - WatchHandler handler = new WatchHandler(socket, files);
|
| - handlers.add(handler);
|
| - socket.listen(
|
| - handler.onData, cancelOnError: true, onDone: handler.onDone);
|
| - });
|
| - }
|
| -
|
| - static onFileSystemEvent(FileSystemEvent event) {
|
| - for (WatchHandler handler in handlers) {
|
| - handler.handleFileSystemEvent(event);
|
| - }
|
| - }
|
| -}
|
| -
|
| -/// Represents a "project" command. These commands are accessed from the URL
|
| -/// "/project?name".
|
| -class ProjectCommand {
|
| - final String name;
|
| -
|
| - /// For each query parameter, this map describes rules for validating them.
|
| - final Map<String, String> rules;
|
| -
|
| - final Function handle;
|
| -
|
| - const ProjectCommand(this.name, this.rules, this.handle);
|
| -}
|
| -
|
| class Conversation {
|
| HttpRequest request;
|
| HttpResponse response;
|
|
|
| - static const String PROJECT_PATH = '/project';
|
| -
|
| static const String PACKAGES_PATH = '/packages';
|
|
|
| static const String CONTENT_TYPE = HttpHeaders.CONTENT_TYPE;
|
|
|
| - static const String GIT_TAG = 'try_dart_backup';
|
| -
|
| - static const String COMMIT_MESSAGE = """
|
| -Automated backup.
|
| -
|
| -It is safe to delete tag '$GIT_TAG' if you don't need the backup.""";
|
| -
|
| static Uri documentRoot = Uri.base;
|
|
|
| - static Uri projectRoot = Uri.base.resolve('site/try/src/');
|
| -
|
| - static Uri packageRoot = Uri.base.resolve('sdk/lib/_internal/');
|
| -
|
| - static const List<ProjectCommand> COMMANDS = const <ProjectCommand>[
|
| - const ProjectCommand('list', const {'list': null}, handleProjectList),
|
| - ];
|
| -
|
| - static Stream<FileSystemEvent> projectChanges;
|
| -
|
| - static final Map<String, String> gitEnv = computeGitEnv();
|
| + static Uri packageRoot = Uri.base.resolve('packages/');
|
|
|
| Conversation(this.request, this.response);
|
|
|
| @@ -162,59 +57,15 @@ It is safe to delete tag '$GIT_TAG' if you don't need the backup.""";
|
| response.close();
|
| }
|
|
|
| - bool validate(Map<String, String> parameters, Map<String, String> rules) {
|
| - Iterable<String> problems = rules.keys
|
| - .where((name) => !parameters.containsKey(name))
|
| - .map((name) => "Missing parameter: '$name'.");
|
| - if (!problems.isEmpty) {
|
| - badRequest(problems.first);
|
| - return false;
|
| - }
|
| - Set extra = new Set.from(parameters.keys)..removeAll(rules.keys);
|
| - if (extra.isEmpty) return true;
|
| - String extraString = (extra.toList()..sort()).join("', '");
|
| - badRequest("Extra parameters: '$extraString'.");
|
| - return false;
|
| - }
|
| -
|
| - static Future<List<String>> listProjectFiles() {
|
| - String nativeDir = projectRoot.toFilePath();
|
| - Directory dir = new Directory(nativeDir);
|
| - var future = dir.list(recursive: true, followLinks: false).toList();
|
| - return future.then((List<FileSystemEntity> entries) {
|
| - return entries
|
| - .map((e) => e.path)
|
| - .where((p) => p.endsWith('.dart') && p.startsWith(nativeDir))
|
| - .map((p) => p.substring(nativeDir.length))
|
| - .map((p) => new Uri.file(p).path).toList();
|
| - });
|
| - }
|
| -
|
| - static handleProjectList(Conversation self) {
|
| - listProjectFiles().then((List<String> files) {
|
| - self.response
|
| - ..write(JSON.encode(files))
|
| - ..close();
|
| - });
|
| - }
|
| -
|
| - handleProjectRequest() {
|
| - Map<String, String> parameters = request.uri.queryParameters;
|
| - for (ProjectCommand command in COMMANDS) {
|
| - if (parameters.containsKey(command.name)) {
|
| - if (validate(parameters, command.rules)) {
|
| - (command.handle)(this);
|
| - }
|
| - return;
|
| - }
|
| - }
|
| - String commands = COMMANDS.map((c) => c.name).join("', '");
|
| - badRequest("Valid commands are: '$commands'");
|
| - }
|
| -
|
| handleSocket() {
|
| - if (request.uri.path == '/ws/watch') {
|
| - WebSocketTransformer.upgrade(request).then(WatchHandler.handleWebSocket);
|
| + if (false && request.uri.path == '/ws/watch') {
|
| + WebSocketTransformer.upgrade(request).then((WebSocket socket) {
|
| + socket.add(JSON.encode({'create': []}));
|
| + // WatchHandler handler = new WatchHandler(socket, files);
|
| + // handlers.add(handler);
|
| + // socket.listen(
|
| + // handler.onData, cancelOnError: true, onDone: handler.onDone);
|
| + });
|
| } else {
|
| response.done
|
| .then(onClosed)
|
| @@ -229,29 +80,16 @@ It is safe to delete tag '$GIT_TAG' if you don't need the backup.""";
|
| .catchError(onError);
|
|
|
| Uri uri = request.uri;
|
| - if (uri.path == PROJECT_PATH) {
|
| - return handleProjectRequest();
|
| - }
|
| if (uri.path.endsWith('/')) {
|
| uri = uri.resolve('index.html');
|
| }
|
| - if (uri.path == '/css/fonts/fontawesome-webfont.woff') {
|
| - uri = uri.resolve('/fontawesome-webfont.woff');
|
| - }
|
| if (uri.path.contains('..') || uri.path.contains('%')) {
|
| return notFound(uri.path);
|
| }
|
| String path = uri.path;
|
| Uri root = documentRoot;
|
| String dartType = 'application/dart';
|
| - if (path.startsWith('/project/packages/')) {
|
| - root = packageRoot;
|
| - path = path.substring('/project/packages'.length);
|
| - } else if (path.startsWith('${PROJECT_PATH}/')) {
|
| - root = projectRoot;
|
| - path = path.substring(PROJECT_PATH.length);
|
| - dartType = 'text/plain';
|
| - } else if (path.startsWith('${PACKAGES_PATH}/')) {
|
| + if (path.startsWith('${PACKAGES_PATH}/')) {
|
| root = packageRoot;
|
| path = path.substring(PACKAGES_PATH.length);
|
| }
|
| @@ -260,8 +98,6 @@ It is safe to delete tag '$GIT_TAG' if you don't need the backup.""";
|
| switch (request.method) {
|
| case 'GET':
|
| return handleGet(filePath, dartType);
|
| - case 'POST':
|
| - return handlePost(filePath);
|
| default:
|
| String method = const HtmlEscape().convert(request.method);
|
| return badRequest("Unsupported method: '$method'");
|
| @@ -287,102 +123,6 @@ It is safe to delete tag '$GIT_TAG' if you don't need the backup.""";
|
| });
|
| }
|
|
|
| - handlePost(String path) {
|
| - // The data is sent using a dart:html HttpRequest (aka XMLHttpRequest).
|
| - // According to http://xhr.spec.whatwg.org/, strings are always encoded as
|
| - // UTF-8.
|
| - request.transform(UTF8.decoder).join().then((String data) {
|
| - // The rest of this method is synchronous. This guarantees that we don't
|
| - // make conflicting git changes in response to multiple POST requests.
|
| - try {
|
| - backup(path);
|
| - } catch (e, stack) {
|
| - return internalError(e, stack);
|
| - }
|
| -
|
| - new File(path).writeAsStringSync(data);
|
| -
|
| - response
|
| - ..statusCode = HttpStatus.OK
|
| - ..close();
|
| - });
|
| - }
|
| -
|
| - // Back up the file [path] using git.
|
| - static void backup(String path) {
|
| - // Reset the index.
|
| - git('read-tree', ['HEAD']);
|
| -
|
| - // Save modifications in index.
|
| - git('update-index', ['--add', path]);
|
| -
|
| - // If the file isn't modified, don't back it up.
|
| - if (checkGit('diff', ['--cached', '--quiet'])) return;
|
| -
|
| - String localModifications = git('write-tree');
|
| -
|
| - String tag = 'refs/tags/$GIT_TAG';
|
| - var arguments = ['-m', COMMIT_MESSAGE, localModifications];
|
| -
|
| - if (checkGit('rev-parse', ['-q', '--verify', tag])) {
|
| - // The tag already exists.
|
| -
|
| - if (checkGit('diff-tree', ['--quiet', localModifications, tag])) {
|
| - // localModifications are identical to the last backup.
|
| - return;
|
| - }
|
| -
|
| - // Use the tag as a parent.
|
| - arguments = ['-p', tag]..addAll(arguments);
|
| -
|
| - String headCommit = git('rev-parse', ['HEAD']);
|
| - String mergeBase = git('merge-base', [tag, 'HEAD']);
|
| - if (headCommit != mergeBase) {
|
| - arguments = ['-p', 'HEAD']..addAll(arguments);
|
| - }
|
| - } else {
|
| - arguments = ['-p', 'HEAD']..addAll(arguments);
|
| - }
|
| -
|
| - // Commit the local modifcations.
|
| - String commit = git('commit-tree', arguments);
|
| -
|
| - // Create or update the tag.
|
| - git('tag', ['-f', GIT_TAG, commit]);
|
| - }
|
| -
|
| - static String git(String command,
|
| - [List<String> arguments = const <String> []]) {
|
| - ProcessResult result =
|
| - run('git', <String>[command]..addAll(arguments), gitEnv);
|
| - if (result.exitCode != 0) {
|
| - throw 'git error: ${result.stdout}\n${result.stderr}';
|
| - }
|
| - return result.stdout.trim();
|
| - }
|
| -
|
| - static bool checkGit(String command,
|
| - [List<String> arguments = const <String> []]) {
|
| - return
|
| - run('git', <String>[command]..addAll(arguments), gitEnv).exitCode == 0;
|
| - }
|
| -
|
| - static Map<String, String> computeGitEnv() {
|
| - ProcessResult result = run('git', ['rev-parse', '--git-dir'], null);
|
| - if (result.exitCode != 0) {
|
| - throw 'git error: ${result.stdout}\n${result.stderr}';
|
| - }
|
| - String gitDir = result.stdout.trim();
|
| - return <String, String>{ 'GIT_INDEX_FILE': '$gitDir/try_dart_backup' };
|
| - }
|
| -
|
| - static ProcessResult run(String executable,
|
| - List<String> arguments,
|
| - Map<String, String> environment) {
|
| - // print('Running $executable ${arguments.join(" ")}');
|
| - return Process.runSync(executable, arguments, environment: environment);
|
| - }
|
| -
|
| static onRequest(HttpRequest request) {
|
| Conversation conversation = new Conversation(request, request.response);
|
| if (WebSocketTransformer.isUpgradeRequest(request)) {
|
| @@ -392,14 +132,6 @@ It is safe to delete tag '$GIT_TAG' if you don't need the backup.""";
|
| }
|
| }
|
|
|
| - static ensureProjectWatcher() {
|
| - if (projectChanges != null) return;
|
| - String nativeDir = projectRoot.toFilePath();
|
| - Directory dir = new Directory(nativeDir);
|
| - projectChanges = dir.watch();
|
| - projectChanges.listen(WatchHandler.onFileSystemEvent);
|
| - }
|
| -
|
| static onError(error) {
|
| if (error is HttpException) {
|
| print('Error: ${error.message}');
|
| @@ -440,9 +172,6 @@ main(List<String> arguments) {
|
| port = int.parse(arguments[2]);
|
| }
|
| if (arguments.length > 3) {
|
| - Conversation.projectRoot = Uri.base.resolve(arguments[3]);
|
| - }
|
| - if (arguments.length > 4) {
|
| Conversation.packageRoot = Uri.base.resolve(arguments[4]);
|
| }
|
| HttpServer.bind(host, port).then((HttpServer server) {
|
|
|