Code cleanup: use async, using expectAsync1 (#6)

diff --git a/lib/shelf_proxy.dart b/lib/shelf_proxy.dart
index a045b7d..1aa3a15 100644
--- a/lib/shelf_proxy.dart
+++ b/lib/shelf_proxy.dart
@@ -15,27 +15,36 @@
 /// `http://example.com/docs`, a request to `/documentation/tutorials`
 /// will be proxied to `http://example.com/docs/tutorials`.
 ///
+/// [url] must be a [String] or [Uri].
+///
 /// [client] is used internally to make HTTP requests. It defaults to a
 /// `dart:io`-based client.
 ///
 /// [proxyName] is used in headers to identify this proxy. It should be a valid
 /// HTTP token or a hostname. It defaults to `shelf_proxy`.
 Handler proxyHandler(url, {http.Client client, String proxyName}) {
-  if (url is String) url = Uri.parse(url);
-  if (client == null) client = new http.Client();
-  if (proxyName == null) proxyName = 'shelf_proxy';
+  Uri uri;
+  if (url is String) {
+    uri = Uri.parse(url);
+  } else if (url is Uri) {
+    uri = url;
+  } else {
+    throw new ArgumentError.value(url, 'url', 'url must be a String or Uri.');
+  }
+  client ??= new http.Client();
+  proxyName ??= 'shelf_proxy';
 
-  return (serverRequest) {
+  return (serverRequest) async {
     // TODO(nweiz): Support WebSocket requests.
 
     // TODO(nweiz): Handle TRACE requests correctly. See
     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.8
-    var requestUrl = url.resolve(serverRequest.url.toString());
+    var requestUrl = uri.resolve(serverRequest.url.toString());
     var clientRequest =
         new http.StreamedRequest(serverRequest.method, requestUrl);
     clientRequest.followRedirects = false;
     clientRequest.headers.addAll(serverRequest.headers);
-    clientRequest.headers['Host'] = url.authority;
+    clientRequest.headers['Host'] = uri.authority;
 
     // Add a Via header. See
     // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.45
@@ -43,44 +52,43 @@
         '${serverRequest.protocolVersion} $proxyName');
 
     store(serverRequest.read(), clientRequest.sink);
-    return client.send(clientRequest).then((clientResponse) {
-      // Add a Via header. See
-      // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.45
-      _addHeader(clientResponse.headers, 'via', '1.1 $proxyName');
+    var clientResponse = await client.send(clientRequest);
+    // Add a Via header. See
+    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.45
+    _addHeader(clientResponse.headers, 'via', '1.1 $proxyName');
 
-      // Remove the transfer-encoding since the body has already been decoded by
-      // [client].
-      clientResponse.headers.remove('transfer-encoding');
+    // Remove the transfer-encoding since the body has already been decoded by
+    // [client].
+    clientResponse.headers.remove('transfer-encoding');
 
-      // If the original response was gzipped, it will be decoded by [client]
-      // and we'll have no way of knowing its actual content-length.
-      if (clientResponse.headers['content-encoding'] == 'gzip') {
-        clientResponse.headers.remove('content-encoding');
-        clientResponse.headers.remove('content-length');
+    // If the original response was gzipped, it will be decoded by [client]
+    // and we'll have no way of knowing its actual content-length.
+    if (clientResponse.headers['content-encoding'] == 'gzip') {
+      clientResponse.headers.remove('content-encoding');
+      clientResponse.headers.remove('content-length');
 
-        // Add a Warning header. See
-        // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.2
-        _addHeader(
-            clientResponse.headers, 'warning', '214 $proxyName "GZIP decoded"');
+      // Add a Warning header. See
+      // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.2
+      _addHeader(
+          clientResponse.headers, 'warning', '214 $proxyName "GZIP decoded"');
+    }
+
+    // Make sure the Location header is pointing to the proxy server rather
+    // than the destination server, if possible.
+    if (clientResponse.isRedirect &&
+        clientResponse.headers.containsKey('location')) {
+      var location =
+          requestUrl.resolve(clientResponse.headers['location']).toString();
+      if (p.url.isWithin(uri.toString(), location)) {
+        clientResponse.headers['location'] =
+            '/' + p.url.relative(location, from: uri.toString());
+      } else {
+        clientResponse.headers['location'] = location;
       }
+    }
 
-      // Make sure the Location header is pointing to the proxy server rather
-      // than the destination server, if possible.
-      if (clientResponse.isRedirect &&
-          clientResponse.headers.containsKey('location')) {
-        var location =
-            requestUrl.resolve(clientResponse.headers['location']).toString();
-        if (p.url.isWithin(url.toString(), location)) {
-          clientResponse.headers['location'] =
-              '/' + p.url.relative(location, from: url.toString());
-        } else {
-          clientResponse.headers['location'] = location;
-        }
-      }
-
-      return new Response(clientResponse.statusCode,
-          body: clientResponse.stream, headers: clientResponse.headers);
-    });
+    return new Response(clientResponse.statusCode,
+        body: clientResponse.stream, headers: clientResponse.headers);
   };
 }
 
diff --git a/test/shelf_proxy_test.dart b/test/shelf_proxy_test.dart
index 7524205..de6f032 100644
--- a/test/shelf_proxy_test.dart
+++ b/test/shelf_proxy_test.dart
@@ -189,36 +189,32 @@
     });
   });
 
-  test("removes a transfer-encoding header", () {
+  test("removes a transfer-encoding header", () async {
     var handler = mockHandler((request) {
       return new http.Response('', 200,
           headers: {'transfer-encoding': 'chunked'});
     });
 
-    expect(
-        handler(new shelf.Request('GET', Uri.parse('http://localhost/')))
-            .then((response) {
-          expect(response.headers, isNot(contains("transfer-encoding")));
-        }),
-        completes);
+    var response =
+        await handler(new shelf.Request('GET', Uri.parse('http://localhost/')));
+
+    expect(response.headers, isNot(contains("transfer-encoding")));
   });
 
   test("removes content-length and content-encoding for a gzipped response",
-      () {
+      () async {
     var handler = mockHandler((request) {
       return new http.Response('', 200,
           headers: {'content-encoding': 'gzip', 'content-length': '1234'});
     });
 
-    expect(
-        handler(new shelf.Request('GET', Uri.parse('http://localhost/')))
-            .then((response) {
-          expect(response.headers, isNot(contains("content-encoding")));
-          expect(response.headers, isNot(contains("content-length")));
-          expect(response.headers,
-              containsPair('warning', '214 shelf_proxy "GZIP decoded"'));
-        }),
-        completes);
+    var response =
+        await handler(new shelf.Request('GET', Uri.parse('http://localhost/')));
+
+    expect(response.headers, isNot(contains("content-encoding")));
+    expect(response.headers, isNot(contains("content-length")));
+    expect(response.headers,
+        containsPair('warning', '214 shelf_proxy "GZIP decoded"'));
   });
 }
 
@@ -227,34 +223,29 @@
 /// [targetPath] is the root-relative path on the target server to proxy to. It
 /// defaults to `/`.
 void createProxy(shelf.Handler handler, {String targetPath}) {
-  handler = expectAsync(handler, reason: 'target server handler');
-  schedule(() {
-    return shelf_io.serve(handler, 'localhost', 0).then((targetServer) {
-      targetUri = Uri.parse('http://localhost:${targetServer.port}');
-      if (targetPath != null) targetUri = targetUri.resolve(targetPath);
-      var proxyServerHandler =
-          expectAsync(proxyHandler(targetUri), reason: 'proxy server handler');
+  handler = expectAsync1(handler, reason: 'target server handler');
+  schedule(() async {
+    var targetServer = await shelf_io.serve(handler, 'localhost', 0);
+    targetUri = Uri.parse('http://localhost:${targetServer.port}');
+    if (targetPath != null) targetUri = targetUri.resolve(targetPath);
+    var proxyServerHandler =
+        expectAsync1(proxyHandler(targetUri), reason: 'proxy server handler');
 
-      return shelf_io
-          .serve(proxyServerHandler, 'localhost', 0)
-          .then((proxyServer) {
-        proxyUri = Uri.parse('http://localhost:${proxyServer.port}');
+    var proxyServer = await shelf_io.serve(proxyServerHandler, 'localhost', 0);
+    proxyUri = Uri.parse('http://localhost:${proxyServer.port}');
 
-        currentSchedule.onComplete.schedule(() {
-          proxyServer.close(force: true);
-          targetServer.close(force: true);
-        }, 'tear down servers');
-      });
-    });
+    currentSchedule.onComplete.schedule(() {
+      proxyServer.close(force: true);
+      targetServer.close(force: true);
+    }, 'tear down servers');
   }, 'spin up servers');
 }
 
 /// Creates a [shelf.Handler] that's backed by a [MockClient] running
 /// [callback].
-shelf.Handler mockHandler(callback(http.Request request)) {
-  var client = new MockClient((request) {
-    return new Future.sync(() => callback(request));
-  });
+shelf.Handler mockHandler(
+    FutureOr<http.Response> callback(http.Request request)) {
+  var client = new MockClient((request) async => await callback(request));
   return proxyHandler('http://dartlang.org', client: client);
 }