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);
}