Add the package implementation.

R=jakemac@google.com

Review URL: https://codereview.chromium.org//2171743003 .
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6f5e0ea..fd217bd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -23,7 +23,7 @@
 ### File headers
 All files in the project must start with the following header.
 
-    // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+    // Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
     // 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.
 
diff --git a/LICENSE b/LICENSE
index de31e1a..82e9b52 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2015, the Dart project authors. All rights reserved.
+Copyright 2016, the Dart project authors. All rights reserved.
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are
 met:
diff --git a/codereview.settings b/codereview.settings
new file mode 100644
index 0000000..812af63
--- /dev/null
+++ b/codereview.settings
@@ -0,0 +1,3 @@
+CODE_REVIEW_SERVER: http://codereview.chromium.org/
+VIEW_VC: https://github.com/dart-lang/shelf_packages_handler/commit/
+CC_LIST: reviews@dartlang.org
\ No newline at end of file
diff --git a/lib/shelf_packages_handler.dart b/lib/shelf_packages_handler.dart
new file mode 100644
index 0000000..eda6203
--- /dev/null
+++ b/lib/shelf_packages_handler.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// 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 shelf_packages_handler;
+
+import 'package:shelf/shelf.dart';
+import 'package:shelf_static/shelf_static.dart';
+import 'package:package_resolver/package_resolver.dart';
+import 'package:path/path.dart' as p;
+
+import 'src/async_handler.dart';
+import 'src/dir_handler.dart';
+import 'src/package_config_handler.dart';
+
+/// A handler that serves the contents of a virtual packages directory.
+///
+/// This effectively serves `package:${request.url}`. It locates packages using
+/// the package resolution logic defined by [resolver]. If [resolver] isn't
+/// passed, it defaults to the current isolate's package resolution logic.
+///
+/// This can only serve assets from `file:` URIs.
+Handler packagesHandler({PackageResolver resolver}) {
+  resolver ??= PackageResolver.current;
+  return new AsyncHandler(resolver.packageRoot.then((packageRoot) {
+    if (packageRoot != null) {
+      return createStaticHandler(p.fromUri(packageRoot),
+          serveFilesOutsidePath: true);
+    } else {
+      return new PackageConfigHandler(resolver);
+    }
+  }));
+}
+
+/// A handler that serves virtual `packages/` directories wherever they're
+/// requested.
+///
+/// This serves the same assets as [packagesHandler] for every URL that contains
+/// `/packages/`. Otherwise, it returns 404s for all requests.
+///
+/// This is useful for ensuring that `package:` imports work for all entrypoints
+/// in Dartium.
+Handler packagesDirHandler({PackageResolver resolver}) =>
+    new DirHandler("packages", packagesHandler(resolver: resolver));
diff --git a/lib/src/async_handler.dart b/lib/src/async_handler.dart
new file mode 100644
index 0000000..1b68f12
--- /dev/null
+++ b/lib/src/async_handler.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:shelf/shelf.dart';
+
+class AsyncHandler {
+  final ResultFuture<Handler> _future;
+
+  AsyncHandler(Future<Handler> future) : _future = new ResultFuture(future);
+
+  call(Request request) {
+    if (_future.result == null) {
+      return _future.then((handler) => handler(request));
+    }
+
+    if (_future.result.isError) return _future;
+
+    return _future.result.asValue.value(request);
+  }
+}
diff --git a/lib/src/dir_handler.dart b/lib/src/dir_handler.dart
new file mode 100644
index 0000000..3605aa9
--- /dev/null
+++ b/lib/src/dir_handler.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// 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 shelf_packages_handler.dir_handler;
+
+import 'package:path/path.dart' as p;
+import 'package:shelf/shelf.dart';
+
+/// A utility handler that mounts a sub-handler beneath a directory name,
+/// wherever that directory name appears in a URL.
+///
+/// In practice, this is used to mount a [PackagesHandler] underneath
+/// `packages/` directories.
+class DirHandler {
+  /// The directory name to look for.
+  final String _name;
+
+  /// The inner handler to mount.
+  final Handler _inner;
+
+  DirHandler(this._name, this._inner);
+
+  /// The callback for handling a single request.
+  call(Request request) {
+    var segments = request.url.pathSegments;
+    for (var i = 0; i < segments.length; i++) {
+      if (segments[i] != _name) continue;
+      return _inner(request.change(path: p.url.joinAll(segments.take(i + 1))));
+    }
+
+    return new Response.notFound("Not found.");
+  }
+}
diff --git a/lib/src/package_config_handler.dart b/lib/src/package_config_handler.dart
new file mode 100644
index 0000000..fd79290
--- /dev/null
+++ b/lib/src/package_config_handler.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+import 'package:package_resolver/package_resolver.dart';
+import 'package:path/path.dart' as p;
+import 'package:shelf/shelf.dart';
+import 'package:shelf_static/shelf_static.dart';
+
+import 'async_handler.dart';
+
+/// A shelf handler that serves a virtual packages directory based on a package
+/// config.
+class PackageConfigHandler {
+  /// The static handlers for serving entries in the package config, indexed by
+  /// name.
+  final _packageHandlers = new Map<String, Handler>();
+
+  /// The information specifying how to do package resolution.
+  PackageResolver _resolver;
+
+  PackageConfigHandler(this._resolver);
+
+  /// The callback for handling a single request.
+  call(Request request) {
+    var segments = request.url.pathSegments;
+    return _handlerFor(segments.first)(request.change(path: segments.first));
+  }
+
+  /// Creates a handler for [package] based on the package map in [resolver].
+  Handler _handlerFor(String package) {
+    return _packageHandlers.putIfAbsent(package, () {
+      return new AsyncHandler(_resolver.urlFor(package).then((url) {
+        var handler = url == null
+            ? (_) => new Response.notFound("Package $package not found.")
+            : createStaticHandler(p.fromUri(url), serveFilesOutsidePath: true);
+
+        return handler;
+      }));
+    });
+  }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 33b3459..323adb2 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: shelf_packages_handler
-version: 1.0.0-dev
+version: 1.0.0
 description: A shelf handler for serving a `packages/` directory.
 author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/shelf_packages_handler
@@ -7,5 +7,12 @@
 environment:
   sdk: '>=1.9.0 <2.0.0'
 
+dependencies:
+  async: '^1.1.0'
+  path: '^1.0.0'
+  shelf: '^0.6.0'
+  shelf_static: '^0.2.0'
+  package_resolver: '^1.0.0'
+
 dev_dependencies:
   test: '^0.12.0'
diff --git a/test/packages_handler_test.dart b/test/packages_handler_test.dart
new file mode 100644
index 0000000..dce58a6
--- /dev/null
+++ b/test/packages_handler_test.dart
@@ -0,0 +1,147 @@
+// Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
+// 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.
+
+import 'dart:io';
+
+import 'package:package_resolver/package_resolver.dart';
+import 'package:path/path.dart' as p;
+import 'package:shelf/shelf.dart';
+import 'package:shelf_packages_handler/shelf_packages_handler.dart';
+import 'package:test/test.dart';
+
+void main() {
+  var dir;
+  setUp(() {
+    dir = Directory.systemTemp
+        .createTempSync("shelf_packages_handler_test")
+        .path;
+    new Directory(dir).createSync();
+    new Directory("$dir/foo").createSync();
+    new File("$dir/foo/foo.dart")
+        .writeAsStringSync("void main() => print('in foo');");
+  });
+
+  tearDown(() {
+    new Directory(dir).deleteSync(recursive: true);
+  });
+
+  group("packagesHandler", () {
+    test("defaults to the current method of package resolution", () async {
+      var handler = packagesHandler();
+      var request = new Request(
+          "GET",
+          Uri.parse(
+              "http://example.com/shelf_packages_handler/"
+              "shelf_packages_handler.dart"));
+      var response = await handler(request);
+      expect(response.statusCode, equals(200));
+      expect(await response.readAsString(),
+          contains("Handler packagesHandler"));
+    });
+
+    group("with a package root", () {
+      var resolver;
+      setUp(() => resolver = new PackageResolver.root(p.toUri(dir)));
+
+      test("looks up a real file", () async {
+        var handler = packagesHandler(resolver: resolver);
+        var request = new Request(
+            "GET", Uri.parse("http://example.com/foo/foo.dart"));
+        var response = await handler(request);
+        expect(response.statusCode, equals(200));
+        expect(await response.readAsString(), contains("in foo"));
+      });
+
+      test("404s for a nonexistent file", () async {
+        var handler = packagesHandler(resolver: resolver);
+        var request = new Request(
+            "GET", Uri.parse("http://example.com/foo/bar.dart"));
+        var response = await handler(request);
+        expect(response.statusCode, equals(404));
+      });
+    });
+
+    group("with a package config", () {
+      var resolver;
+      setUp(() {
+        resolver = new PackageResolver.config({"foo": p.toUri("$dir/foo")});
+      });
+
+      test("looks up a real file", () async {
+        var handler = packagesHandler(resolver: resolver);
+        var request = new Request(
+            "GET", Uri.parse("http://example.com/foo/foo.dart"));
+        var response = await handler(request);
+        expect(response.statusCode, equals(200));
+        expect(await response.readAsString(), contains("in foo"));
+      });
+
+      test("404s for a nonexistent package", () async {
+        var handler = packagesHandler(resolver: resolver);
+        var request = new Request(
+            "GET", Uri.parse("http://example.com/bar/foo.dart"));
+        var response = await handler(request);
+        expect(response.statusCode, equals(404));
+      });
+
+      test("404s for a nonexistent file", () async {
+        var handler = packagesHandler(resolver: resolver);
+        var request = new Request(
+            "GET", Uri.parse("http://example.com/foo/bar.dart"));
+        var response = await handler(request);
+        expect(response.statusCode, equals(404));
+      });
+    });
+  });
+
+  group("packagesDirHandler", () {
+    test("supports a directory at the root of the URL", () async {
+      var handler = packagesDirHandler();
+      var request = new Request(
+          "GET",
+          Uri.parse(
+              "http://example.com/packages/shelf_packages_handler/"
+              "shelf_packages_handler.dart"));
+      var response = await handler(request);
+      expect(response.statusCode, equals(200));
+      expect(await response.readAsString(),
+          contains("Handler packagesHandler"));
+    });
+
+    test("supports a directory deep in the URL", () async {
+      var handler = packagesDirHandler();
+      var request = new Request(
+          "GET",
+          Uri.parse(
+              "http://example.com/foo/bar/very/deep/packages/"
+              "shelf_packages_handler/shelf_packages_handler.dart"));
+      var response = await handler(request);
+      expect(response.statusCode, equals(200));
+      expect(await response.readAsString(),
+          contains("Handler packagesHandler"));
+    });
+
+    test("404s for a URL without a packages directory", () async {
+      var handler = packagesDirHandler();
+      var request = new Request(
+          "GET",
+          Uri.parse(
+              "http://example.com/shelf_packages_handler/"
+              "shelf_packages_handler.dart"));
+      var response = await handler(request);
+      expect(response.statusCode, equals(404));
+    });
+
+    test("404s for a non-existent file within a packages directory", () async {
+      var handler = packagesDirHandler();
+      var request = new Request(
+          "GET",
+          Uri.parse(
+              "http://example.com/packages/shelf_packages_handler/"
+              "non_existent.dart"));
+      var response = await handler(request);
+      expect(response.statusCode, equals(404));
+    });
+  });
+}