| // Copyright (c) 2014, 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:http/http.dart' as http; |
| import 'package:http/testing.dart'; |
| import 'package:test/test.dart'; |
| import 'package:shelf/shelf.dart' as shelf; |
| import 'package:shelf/shelf_io.dart' as shelf_io; |
| import 'package:shelf_proxy/shelf_proxy.dart'; |
| |
| /// The URI of the server the current proxy server is proxying to. |
| Uri targetUri; |
| |
| /// The URI of the current proxy server. |
| Uri proxyUri; |
| |
| void main() { |
| group("forwarding", () { |
| test("forwards request method", () async { |
| await createProxy((request) { |
| expect(request.method, equals('DELETE')); |
| return new shelf.Response.ok(':)'); |
| }); |
| |
| await http.delete(proxyUri); |
| }); |
| |
| test("forwards request headers", () async { |
| await createProxy((request) { |
| expect(request.headers, containsPair('foo', 'bar')); |
| expect(request.headers, containsPair('accept', '*/*')); |
| return new shelf.Response.ok(':)'); |
| }); |
| |
| await get(headers: {'foo': 'bar', 'accept': '*/*'}); |
| }); |
| |
| test("forwards request body", () async { |
| await createProxy((request) { |
| expect(request.readAsString(), completion(equals('hello, server'))); |
| return new shelf.Response.ok(':)'); |
| }); |
| |
| await http.post(proxyUri, body: 'hello, server'); |
| }); |
| |
| test("forwards response status", () async { |
| await createProxy((request) { |
| return new shelf.Response(567); |
| }); |
| |
| var response = await get(); |
| expect(response.statusCode, equals(567)); |
| }); |
| |
| test("forwards response headers", () async { |
| await createProxy((request) { |
| return new shelf.Response.ok(':)', |
| headers: {'foo': 'bar', 'accept': '*/*'}); |
| }); |
| |
| var response = await get(); |
| |
| expect(response.headers, containsPair('foo', 'bar')); |
| expect(response.headers, containsPair('accept', '*/*')); |
| }); |
| |
| test("forwards response body", () async { |
| await createProxy((request) { |
| return new shelf.Response.ok('hello, client'); |
| }); |
| |
| expect(await http.read(proxyUri), equals('hello, client')); |
| }); |
| |
| test("adjusts the Host header for the target server", () async { |
| await createProxy((request) { |
| expect(request.headers, containsPair('host', targetUri.authority)); |
| return new shelf.Response.ok(':)'); |
| }); |
| |
| await get(); |
| }); |
| }); |
| |
| group("via", () { |
| test("adds a Via header to the request", () async { |
| await createProxy((request) { |
| expect(request.headers, containsPair('via', '1.1 shelf_proxy')); |
| return new shelf.Response.ok(':)'); |
| }); |
| |
| await get(); |
| }); |
| |
| test("adds to a request's existing Via header", () async { |
| await createProxy((request) { |
| expect(request.headers, |
| containsPair('via', '1.0 something, 1.1 shelf_proxy')); |
| return new shelf.Response.ok(':)'); |
| }); |
| |
| await get(headers: {'via': '1.0 something'}); |
| }); |
| |
| test("adds a Via header to the response", () async { |
| await createProxy((request) => new shelf.Response.ok(':)')); |
| |
| var response = await get(); |
| expect(response.headers, containsPair('via', '1.1 shelf_proxy')); |
| }); |
| |
| test("adds to a response's existing Via header", () async { |
| await createProxy((request) { |
| return new shelf.Response.ok(':)', headers: {'via': '1.0 something'}); |
| }); |
| |
| var response = await get(); |
| expect(response.headers, |
| containsPair('via', '1.0 something, 1.1 shelf_proxy')); |
| }); |
| }); |
| |
| group("redirects", () { |
| test("doesn't modify a Location for a foreign server", () async { |
| await createProxy((request) { |
| return new shelf.Response.found('http://dartlang.org'); |
| }); |
| |
| var response = await get(); |
| expect(response.headers, containsPair('location', 'http://dartlang.org')); |
| }); |
| |
| test("relativizes a reachable root-relative Location", () async { |
| await createProxy((request) { |
| return new shelf.Response.found('/foo/bar'); |
| }, targetPath: '/foo'); |
| |
| var response = await get(); |
| expect(response.headers, containsPair('location', '/bar')); |
| }); |
| |
| test("absolutizes an unreachable root-relative Location", () async { |
| await createProxy((request) { |
| return new shelf.Response.found('/baz'); |
| }, targetPath: '/foo'); |
| |
| var response = await get(); |
| expect(response.headers, |
| containsPair('location', targetUri.resolve('/baz').toString())); |
| }); |
| }); |
| |
| test("removes a transfer-encoding header", () async { |
| var handler = mockHandler((request) { |
| return new http.Response('', 200, |
| headers: {'transfer-encoding': 'chunked'}); |
| }); |
| |
| 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'}); |
| }); |
| |
| 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"')); |
| }); |
| } |
| |
| /// Creates a proxy server proxying to a server running [handler]. |
| /// |
| /// [targetPath] is the root-relative path on the target server to proxy to. It |
| /// defaults to `/`. |
| Future createProxy(shelf.Handler handler, {String targetPath}) async { |
| handler = expectAsync1(handler, reason: 'target server handler'); |
| 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'); |
| |
| var proxyServer = await shelf_io.serve(proxyServerHandler, 'localhost', 0); |
| proxyUri = Uri.parse('http://localhost:${proxyServer.port}'); |
| |
| addTearDown(() { |
| proxyServer.close(force: true); |
| targetServer.close(force: true); |
| }); |
| } |
| |
| /// Creates a [shelf.Handler] that's backed by a [MockClient] running |
| /// [callback]. |
| 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); |
| } |
| |
| /// Schedules a GET request with [headers] to the proxy server. |
| Future<http.Response> get({Map<String, String> headers}) { |
| var uri = proxyUri; |
| var request = new http.Request('GET', uri); |
| if (headers != null) request.headers.addAll(headers); |
| request.followRedirects = false; |
| return request.send().then(http.Response.fromStream); |
| } |