1.1.0-alpha
This commit is contained in:
parent
487e825f8d
commit
4d7cbb679e
13 changed files with 216 additions and 260 deletions
|
@ -5,10 +5,7 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/test/packages" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<configuration default="false" name="All Tests" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$" />
|
||||
<option name="scope" value="FOLDER" />
|
||||
<option name="testRunnerOptions" value="-j 4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
7
.idea/runConfigurations/multiple_dart.xml
Normal file
7
.idea/runConfigurations/multiple_dart.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="multiple.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$/example/multiple.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
30
README.md
30
README.md
|
@ -1,25 +1,33 @@
|
|||
# angel_proxy
|
||||
# proxy
|
||||
[![Pub](https://img.shields.io/pub/v/angel_proxy.svg)](https://pub.dartlang.org/packages/angel_proxy)
|
||||
[![build status](https://travis-ci.org/angel-dart/proxy.svg)](https://travis-ci.org/angel-dart/proxy)
|
||||
|
||||
[![Pub](https://img.shields.io/pub/v/angel_proxy.svg)](https://pub.dartlang.org/packages/angel_proxy)[![build status](https://travis-ci.org/angel-dart/proxy.svg)](https://travis-ci.org/angel-dart/proxy)
|
||||
|
||||
Angel middleware to forward requests to another server (i.e. pub serve).
|
||||
Angel middleware to forward requests to another server (i.e. `pub serve`).
|
||||
|
||||
```dart
|
||||
import 'package:angel_proxy/angel_proxy.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
main() async {
|
||||
// ...
|
||||
|
||||
// Forward requests instead of serving statically
|
||||
await app.configure(new ProxyLayer('localhost', 3000));
|
||||
var client = new http.Client();
|
||||
var proxy = new Proxy(app, client, 'http://localhost:3000');
|
||||
|
||||
// Or, use one for pub serve.
|
||||
//
|
||||
// This automatically deactivates itself if the app is
|
||||
// in production.
|
||||
await app.configure(new PubServeLayer());
|
||||
// Forward requests instead of serving statically
|
||||
app.use(proxy.handleRequest);
|
||||
}
|
||||
```
|
||||
|
||||
You can also restrict the proxy to serving only from a specific root:
|
||||
```dart
|
||||
new Proxy(app, client, '<host>', publicPath: '/remote');
|
||||
```
|
||||
|
||||
Also, you can map requests to a root path on the remote server
|
||||
```dart
|
||||
new Proxy(app, client, '<host>', mapTo: '/path');
|
||||
```
|
||||
|
||||
If your app's `storeOriginalBuffer` is `true`, then request bodies will be forwarded
|
||||
as well, if they are not empty. This allows things like POST requests to function.
|
||||
|
|
|
@ -1,38 +1,55 @@
|
|||
import 'dart:io';
|
||||
import 'package:angel_diagnostics/angel_diagnostics.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_proxy/angel_proxy.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final Duration TIMEOUT = new Duration(seconds: 5);
|
||||
final Duration timeout = new Duration(seconds: 5);
|
||||
|
||||
main() async {
|
||||
var app = new Angel();
|
||||
var client = new http.Client();
|
||||
|
||||
// Forward any /api requests to pub.
|
||||
// By default, if the host throws a 404, the request will fall through to the next handler.
|
||||
var pubProxy = new ProxyLayer('pub.dartlang.org', 80,
|
||||
publicPath: '/pub', timeout: TIMEOUT);
|
||||
await app.configure(pubProxy);
|
||||
var pubProxy = new Proxy(
|
||||
app,
|
||||
client,
|
||||
'https://pub.dartlang.org',
|
||||
publicPath: '/pub',
|
||||
timeout: timeout,
|
||||
);
|
||||
app.use(pubProxy.handleRequest);
|
||||
|
||||
// Pub's HTML assumes that the site's styles, etc. are on the absolute path `/static`.
|
||||
// This is not the case here. Let's patch that up:
|
||||
app.get('/static/*', (RequestContext req, res) {
|
||||
return pubProxy.serveFile(req.path, req, res);
|
||||
return pubProxy.servePath(req.path, req, res);
|
||||
});
|
||||
|
||||
// Anything else should fall through to dartlang.org.
|
||||
await app.configure(new ProxyLayer('dartlang.org', 80, timeout: TIMEOUT));
|
||||
var dartlangProxy = new Proxy(
|
||||
app,
|
||||
client,
|
||||
'https://dartlang.org',
|
||||
timeout: timeout,
|
||||
recoverFrom404: false,
|
||||
);
|
||||
app.use(dartlangProxy.handleRequest);
|
||||
|
||||
// In case we can't connect to dartlang.org, show an error.
|
||||
app.after.add('Couldn\'t connect to Pub or dartlang.');
|
||||
app.use('Couldn\'t connect to Pub or dartlang.');
|
||||
|
||||
await app.configure(logRequests());
|
||||
|
||||
app.fatalErrorStream.listen((AngelFatalError e) {
|
||||
print(e.error);
|
||||
print(e.stack);
|
||||
});
|
||||
app.logger = new Logger('angel')
|
||||
..onRecord.listen(
|
||||
(rec) {
|
||||
print(rec);
|
||||
if (rec.error != null) print(rec.error);
|
||||
if (rec.stackTrace != null) print(rec.stackTrace);
|
||||
},
|
||||
);
|
||||
|
||||
var server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 8080);
|
||||
print('Listening at http://${server.address.address}:${server.port}');
|
||||
print('Check this out! http://${server.address.address}:${server.port}/pub/packages/angel_framework');
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
library angel_proxy;
|
||||
|
||||
export 'src/proxy_layer.dart';
|
||||
export 'src/pub_serve_layer.dart';
|
||||
|
|
|
@ -1,142 +1,107 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:http/src/base_client.dart' as http;
|
||||
import 'package:http/src/request.dart' as http;
|
||||
import 'package:http/src/response.dart' as http;
|
||||
import 'package:http/src/streamed_response.dart' as http;
|
||||
|
||||
final RegExp _param = new RegExp(r':([A-Za-z0-9_]+)(\((.+)\))?');
|
||||
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
||||
|
||||
/// Used to mount a route for a [ProxyLayer].
|
||||
typedef Route ProxyLayerRouteAssigner(Router router, String path, handler);
|
||||
|
||||
String _pathify(String path) {
|
||||
var p = path.replaceAll(_straySlashes, '');
|
||||
|
||||
Map<String, String> replace = {};
|
||||
|
||||
for (Match match in _param.allMatches(p)) {
|
||||
if (match[3] != null) replace[match[0]] = ':${match[1]}';
|
||||
}
|
||||
|
||||
replace.forEach((k, v) {
|
||||
p = p.replaceAll(k, v);
|
||||
});
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/// Copies HTTP headers ;)
|
||||
void copyHeaders(HttpHeaders from, HttpHeaders to) {
|
||||
from.forEach((k, v) {
|
||||
if (k != HttpHeaders.SERVER &&
|
||||
(k != HttpHeaders.CONTENT_ENCODING || !v.contains('gzip')))
|
||||
to.set(k, v);
|
||||
});
|
||||
|
||||
/*to
|
||||
..chunkedTransferEncoding = from.chunkedTransferEncoding
|
||||
..contentLength = from.contentLength
|
||||
..contentType = from.contentType
|
||||
..date = from.date
|
||||
..expires = from.expires
|
||||
..host = from.host
|
||||
..ifModifiedSince = from.ifModifiedSince
|
||||
..persistentConnection = from.persistentConnection
|
||||
..port = from.port;*/
|
||||
}
|
||||
|
||||
class ProxyLayer {
|
||||
Angel app;
|
||||
HttpClient _client;
|
||||
class Proxy {
|
||||
String _prefix;
|
||||
|
||||
final Angel app;
|
||||
final http.BaseClient httpClient;
|
||||
|
||||
/// If `true` (default), then the plug-in will ignore failures to connect to the proxy, and allow other handlers to run.
|
||||
final bool recoverFromDead;
|
||||
final bool debug, recoverFrom404, streamToIO;
|
||||
final bool recoverFrom404;
|
||||
final String host, mapTo, publicPath;
|
||||
final int port;
|
||||
final String protocol;
|
||||
final Duration timeout;
|
||||
ProxyLayerRouteAssigner routeAssigner;
|
||||
|
||||
ProxyLayer(
|
||||
this.host,
|
||||
this.port, {
|
||||
this.debug: false,
|
||||
Proxy(
|
||||
this.app,
|
||||
this.httpClient,
|
||||
this.host, {
|
||||
this.port,
|
||||
this.mapTo: '/',
|
||||
this.publicPath: '/',
|
||||
this.protocol: 'http',
|
||||
this.recoverFromDead: true,
|
||||
this.recoverFrom404: true,
|
||||
this.streamToIO: false,
|
||||
this.timeout,
|
||||
this.routeAssigner,
|
||||
SecurityContext securityContext,
|
||||
}) {
|
||||
_client = new HttpClient(context: securityContext);
|
||||
_prefix = publicPath.replaceAll(_straySlashes, '');
|
||||
routeAssigner ??=
|
||||
(Router router, String path, handler) => router.get(path, handler);
|
||||
}
|
||||
|
||||
call(Angel app) async => serve(this.app = app);
|
||||
void close() => httpClient.close();
|
||||
|
||||
void close() => _client.close(force: true);
|
||||
/// Handles an incoming HTTP request.
|
||||
Future<bool> handleRequest(RequestContext req, ResponseContext res) {
|
||||
var path = req.path.replaceAll(_straySlashes, '');
|
||||
|
||||
void serve(Router router) {
|
||||
// _printDebug('Public path prefix: "$_prefix"');
|
||||
|
||||
handler(RequestContext req, ResponseContext res) async {
|
||||
var path = req.path.replaceAll(_straySlashes, '');
|
||||
return serveFile(path, req, res);
|
||||
if (_prefix?.isNotEmpty == true) {
|
||||
if (!path.startsWith(_prefix))
|
||||
return new Future<bool>.value(true);
|
||||
else {
|
||||
path = path.replaceFirst(_prefix, '').replaceAll(_straySlashes, '');
|
||||
}
|
||||
}
|
||||
|
||||
routeAssigner(router, '$publicPath/*', handler);
|
||||
return servePath(path, req, res);
|
||||
}
|
||||
|
||||
serveFile(String path, RequestContext req, ResponseContext res) async {
|
||||
var _path = path;
|
||||
HttpClientResponse rs;
|
||||
/// Proxies a request to the given path on the remote server.
|
||||
Future<bool> servePath(
|
||||
String path, RequestContext req, ResponseContext res) async {
|
||||
http.StreamedResponse rs;
|
||||
|
||||
if (_prefix.isNotEmpty) {
|
||||
_path = path.replaceAll(new RegExp('^' + _pathify(_prefix)), '');
|
||||
}
|
||||
|
||||
// Create mapping
|
||||
// _printDebug('Serving path $_path via proxy');
|
||||
final mapping = '$mapTo/$_path'.replaceAll(_straySlashes, '');
|
||||
// _printDebug('Mapped path $_path to path $mapping on proxy $host:$port');
|
||||
final mapping = '$mapTo/$path'.replaceAll(_straySlashes, '');
|
||||
|
||||
try {
|
||||
Future<HttpClientResponse> accessRemote() async {
|
||||
var ips = await InternetAddress.lookup(host);
|
||||
if (ips.isEmpty)
|
||||
throw new StateError('Could not resolve remote host "$host".');
|
||||
var address = ips.first.address;
|
||||
final rq = await _client.open(req.method, address, port, mapping);
|
||||
// _printDebug('Opened client request at "$address:$port/$mapping"');
|
||||
Future<http.StreamedResponse> accessRemote() async {
|
||||
var url = port == null ? host : '$host:$port';
|
||||
url = url.replaceAll(_straySlashes, '');
|
||||
url = '$url/$mapping';
|
||||
|
||||
copyHeaders(req.headers, rq.headers);
|
||||
rq.headers.set(HttpHeaders.HOST, host);
|
||||
// _printDebug('Copied headers');
|
||||
rq.cookies.addAll(req.cookies ?? []);
|
||||
// _printDebug('Added cookies');
|
||||
rq.headers.set(
|
||||
'X-Forwarded-For', req.io.connectionInfo.remoteAddress.address);
|
||||
rq.headers
|
||||
..set('X-Forwarded-Port', req.io.connectionInfo.remotePort.toString())
|
||||
..set('X-Forwarded-Host',
|
||||
req.headers.host ?? req.headers.value(HttpHeaders.HOST) ?? 'none')
|
||||
..set('X-Forwarded-Proto', protocol);
|
||||
// _printDebug('Added X-Forwarded headers');
|
||||
if (!url.startsWith('http')) url = 'http://$url';
|
||||
url = url.replaceAll(_straySlashes, '');
|
||||
|
||||
if (app.storeOriginalBuffer == true) {
|
||||
var headers = <String, String>{
|
||||
'host': port == null ? host : '$host:$port',
|
||||
'x-forwarded-for': req.io.connectionInfo.remoteAddress.address,
|
||||
'x-forwarded-port': req.io.connectionInfo.remotePort.toString(),
|
||||
'x-forwarded-host':
|
||||
req.headers.host ?? req.headers.value('host') ?? 'none',
|
||||
'x-forwarded-proto': protocol,
|
||||
};
|
||||
|
||||
req.headers.forEach((name, values) {
|
||||
headers[name] = values.join(',');
|
||||
});
|
||||
|
||||
headers['cookie'] =
|
||||
req.cookies.map<String>((c) => '${c.name}=${c.value}').join('; ');
|
||||
|
||||
var body;
|
||||
|
||||
if (req.method != 'GET' && app.storeOriginalBuffer == true) {
|
||||
await req.parse();
|
||||
if (req.originalBuffer?.isNotEmpty == true)
|
||||
rq.add(req.originalBuffer);
|
||||
if (req.originalBuffer?.isNotEmpty == true) body = req.originalBuffer;
|
||||
}
|
||||
|
||||
await rq.flush();
|
||||
return await rq.close();
|
||||
var rq = new http.Request(req.method, Uri.parse(url));
|
||||
rq.headers.addAll(headers);
|
||||
rq.headers['host'] = rq.url.host;
|
||||
|
||||
if (body != null) rq.bodyBytes = body;
|
||||
|
||||
return await httpClient.send(rq);
|
||||
}
|
||||
|
||||
var future = accessRemote();
|
||||
|
@ -146,11 +111,13 @@ class ProxyLayer {
|
|||
if (recoverFromDead != false)
|
||||
return true;
|
||||
else
|
||||
throw new AngelHttpException(e,
|
||||
stackTrace: st,
|
||||
statusCode: HttpStatus.GATEWAY_TIMEOUT,
|
||||
message:
|
||||
'Connection to remote host "$host" timed out after ${timeout.inMilliseconds}ms.');
|
||||
throw new AngelHttpException(
|
||||
e,
|
||||
stackTrace: st,
|
||||
statusCode: 504,
|
||||
message:
|
||||
'Connection to remote host "$host" timed out after ${timeout.inMilliseconds}ms.',
|
||||
);
|
||||
} catch (e) {
|
||||
if (recoverFromDead != false)
|
||||
return true;
|
||||
|
@ -158,44 +125,21 @@ class ProxyLayer {
|
|||
rethrow;
|
||||
}
|
||||
|
||||
// _printDebug(
|
||||
// 'Proxy responded to $mapping with status code ${rs.statusCode}');
|
||||
|
||||
if (rs.statusCode == 404 && recoverFrom404 != false) return true;
|
||||
|
||||
res
|
||||
..statusCode = rs.statusCode
|
||||
..contentType = rs.headers.contentType;
|
||||
..headers.addAll(rs.headers);
|
||||
|
||||
// _printDebug('Proxy response headers:\n${rs.headers}');
|
||||
if (rs.contentLength == 0 && recoverFromDead != false) return true;
|
||||
|
||||
if (streamToIO == true) {
|
||||
res
|
||||
..willCloseItself = true
|
||||
..end();
|
||||
var stream = rs.stream;
|
||||
|
||||
copyHeaders(rs.headers, res.io.headers);
|
||||
res.io.statusCode = rs.statusCode;
|
||||
if (rs.headers['content-encoding'] == 'gzip')
|
||||
stream = stream.transform(GZIP.encoder);
|
||||
|
||||
if (rs.headers.contentType != null)
|
||||
res.io.headers.contentType = rs.headers.contentType;
|
||||
await stream.pipe(res);
|
||||
|
||||
// _printDebug('Outgoing content length: ${res.io.contentLength}');
|
||||
|
||||
if (rs.headers[HttpHeaders.CONTENT_ENCODING]?.contains('gzip') == true) {
|
||||
res.io.headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip');
|
||||
await rs.transform(GZIP.encoder).pipe(res.io);
|
||||
} else
|
||||
await rs.pipe(res.io);
|
||||
} else {
|
||||
rs.headers.forEach((k, v) {
|
||||
if (k != HttpHeaders.CONTENT_ENCODING || !v.contains('gzip'))
|
||||
res.headers[k] = v.join(',');
|
||||
});
|
||||
|
||||
await rs.forEach(res.buffer.add);
|
||||
}
|
||||
|
||||
return res.buffer.isEmpty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import 'package:angel_route/angel_route.dart';
|
||||
import 'proxy_layer.dart';
|
||||
|
||||
class PubServeLayer extends ProxyLayer {
|
||||
PubServeLayer(
|
||||
{bool debug: false,
|
||||
bool recoverFromDead: true,
|
||||
bool recoverFrom404: true,
|
||||
bool streamToIO: true,
|
||||
String host: 'localhost',
|
||||
String mapTo: '/',
|
||||
int port: 8080,
|
||||
String protocol: 'http',
|
||||
String publicPath: '/',
|
||||
ProxyLayerRouteAssigner routeAssigner,
|
||||
Duration timeout})
|
||||
: super(host, port,
|
||||
debug: debug,
|
||||
mapTo: mapTo,
|
||||
protocol: protocol,
|
||||
publicPath: publicPath,
|
||||
recoverFromDead: recoverFromDead != false,
|
||||
recoverFrom404: recoverFrom404 != false,
|
||||
streamToIO: streamToIO != false,
|
||||
routeAssigner: routeAssigner,
|
||||
timeout: timeout);
|
||||
|
||||
@override
|
||||
void serve(Router router) {
|
||||
if (app?.isProduction == true) {
|
||||
// Auto-deactivate in production ;)
|
||||
return;
|
||||
}
|
||||
|
||||
super.serve(router);
|
||||
}
|
||||
}
|
12
pubspec.yaml
12
pubspec.yaml
|
@ -1,16 +1,14 @@
|
|||
name: angel_proxy
|
||||
description: Angel middleware to forward requests to another server (i.e. pub serve).
|
||||
version: 1.0.9
|
||||
version: 1.1.0-alpha
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/proxy
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
dependencies:
|
||||
angel_framework: ^1.0.0-dev
|
||||
angel_framework: ^1.1.0-alpha
|
||||
http: ^0.11.3
|
||||
dev_dependencies:
|
||||
angel_compress: ^1.0.0
|
||||
angel_diagnostics: ^1.0.0
|
||||
angel_test: ^1.0.0
|
||||
http: ^0.11.3
|
||||
test: ^0.12.15
|
||||
angel_test: ^1.1.0-alpha
|
||||
test: ^0.12.15
|
||||
|
|
@ -2,8 +2,8 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_proxy/angel_proxy.dart';
|
||||
import 'package:angel_test/angel_test.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'common.dart';
|
||||
|
||||
|
@ -15,18 +15,44 @@ main() {
|
|||
|
||||
setUp(() async {
|
||||
app = new Angel()..storeOriginalBuffer = true;
|
||||
var httpClient = new http.Client();
|
||||
|
||||
testServer = await testApp().startServer();
|
||||
|
||||
await app.configure(new ProxyLayer(
|
||||
testServer.address.address, testServer.port,
|
||||
publicPath: '/proxy',
|
||||
routeAssigner: (router, path, handler) => router.all(path, handler)));
|
||||
await app.configure(new ProxyLayer(
|
||||
testServer.address.address, testServer.port,
|
||||
mapTo: '/foo'));
|
||||
var proxy1 = new Proxy(
|
||||
app,
|
||||
httpClient,
|
||||
testServer.address.address,
|
||||
port: testServer.port,
|
||||
publicPath: '/proxy',
|
||||
);
|
||||
var proxy2 = new Proxy(
|
||||
app,
|
||||
httpClient,
|
||||
testServer.address.address,
|
||||
port: testServer.port,
|
||||
mapTo: '/foo',
|
||||
);
|
||||
|
||||
app.after.add((req, res) async => res.write('intercept empty'));
|
||||
app.use(proxy1.handleRequest);
|
||||
app.use(proxy2.handleRequest);
|
||||
|
||||
app.use((req, res) {
|
||||
print('Intercepting empty from ${req.uri}');
|
||||
res.write('intercept empty');
|
||||
});
|
||||
|
||||
app.shutdownHooks.add((_) async {
|
||||
httpClient.close();
|
||||
});
|
||||
|
||||
app.logger = new Logger('angel');
|
||||
|
||||
Logger.root.onRecord.listen((rec) {
|
||||
print(rec);
|
||||
if (rec.error != null) print(rec.error);
|
||||
if (rec.stackTrace != null) print(rec.stackTrace);
|
||||
});
|
||||
|
||||
server = await app.startServer();
|
||||
url = 'http://${server.address.address}:${server.port}';
|
||||
|
@ -48,11 +74,6 @@ main() {
|
|||
test('empty', () async {
|
||||
var response = await client.get('$url/proxy/empty');
|
||||
print('Response: ${response.body}');
|
||||
|
||||
// Shouldn't say it is gzipped...
|
||||
expect(response, isNot(hasHeader('content-encoding')));
|
||||
|
||||
// Should have gzipped body
|
||||
expect(response.body, 'intercept empty');
|
||||
});
|
||||
|
||||
|
@ -64,10 +85,10 @@ main() {
|
|||
|
||||
test('original buffer', () async {
|
||||
var response = await client.post('$url/proxy/body',
|
||||
body: {'foo': 'bar'},
|
||||
headers: {'content-type': 'application/x-www-form-urlencoded'});
|
||||
body: JSON.encode({'foo': 'bar'}),
|
||||
headers: {'content-type': 'application/json'});
|
||||
print('Response: ${response.body}');
|
||||
expect(response.body, isNotEmpty);
|
||||
expect(JSON.decode(response.body), {'foo': 'bar'});
|
||||
expect(response.body, isNot('intercept empty'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
Angel testApp() {
|
||||
final app = new Angel();
|
||||
final app = new Angel()..lazyParseBodies = true;
|
||||
|
||||
app.get('/hello', 'world');
|
||||
app.get('/foo/bar', 'baz');
|
||||
app.post('/body', (req, res) => req.lazyBody());
|
||||
|
||||
app.fatalErrorStream.listen((e) {
|
||||
print('FATAL IN TEST APP: ${e.error}');
|
||||
print(e.stack);
|
||||
app.post('/body', (RequestContext req, res) async {
|
||||
var body = await req.lazyBody();
|
||||
print('Body: $body');
|
||||
return body;
|
||||
});
|
||||
|
||||
app.logger = new Logger('testApp');
|
||||
|
||||
return app..dumpTree();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:angel_compress/angel_compress.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_proxy/angel_proxy.dart';
|
||||
import 'package:angel_test/angel_test.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mock_request/mock_request.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
|
@ -12,27 +13,40 @@ main() {
|
|||
|
||||
setUp(() async {
|
||||
testApp = new Angel();
|
||||
testApp.get('/foo', (req, res) => res.write('pub serve'));
|
||||
testApp.get('/foo', (req, res) async {
|
||||
res.write('pub serve');
|
||||
});
|
||||
testApp.get('/empty', (req, res) => res.end());
|
||||
testApp.responseFinalizers.add(gzip());
|
||||
|
||||
testApp.responseFinalizers.add((req, ResponseContext res) async {
|
||||
print('OUTGOING: ' + new String.fromCharCodes(res.buffer.toBytes()));
|
||||
});
|
||||
|
||||
testApp.injectEncoders({'gzip': GZIP.encoder});
|
||||
|
||||
var server = await testApp.startServer();
|
||||
|
||||
app = new Angel();
|
||||
app.get('/bar', (req, res) => res.write('normal'));
|
||||
var layer = new PubServeLayer(
|
||||
debug: true,
|
||||
publicPath: '/proxy',
|
||||
host: server.address.address,
|
||||
port: server.port);
|
||||
print('streamToIO: ${layer.streamToIO}');
|
||||
await app.configure(layer);
|
||||
|
||||
var httpClient = new http.Client();
|
||||
|
||||
var layer = new Proxy(
|
||||
app,
|
||||
httpClient,
|
||||
server.address.address,
|
||||
port: server.port,
|
||||
publicPath: '/proxy',
|
||||
);
|
||||
app.use(layer.handleRequest);
|
||||
|
||||
app.responseFinalizers.add((req, ResponseContext res) async {
|
||||
print('Normal. Buf: ' +
|
||||
new String.fromCharCodes(res.buffer.toBytes()) +
|
||||
', headers: ${res.headers}');
|
||||
});
|
||||
app.responseFinalizers.add(gzip());
|
||||
|
||||
app.injectEncoders({'gzip': GZIP.encoder});
|
||||
|
||||
client = await connectTo(app);
|
||||
});
|
||||
|
@ -46,34 +60,19 @@ main() {
|
|||
});
|
||||
|
||||
test('proxied', () async {
|
||||
var response = await client.get('/proxy/foo');
|
||||
|
||||
// Should say it is gzipped...
|
||||
expect(response, hasHeader('content-encoding', 'gzip'));
|
||||
|
||||
// Should have gzipped body
|
||||
//
|
||||
// We have to decode it, because `mock_request` does not auto-decode.
|
||||
expect(response, hasBody('pub serve'));
|
||||
var rq = new MockHttpRequest('GET', Uri.parse('/proxy/foo'))..close();
|
||||
await app.handleRequest(rq);
|
||||
var response = await rq.response.transform(UTF8.decoder).join();
|
||||
expect(response, 'pub serve');
|
||||
});
|
||||
|
||||
test('empty', () async {
|
||||
var response = await client.get('/proxy/empty');
|
||||
|
||||
// Should say it is gzipped...
|
||||
expect(response, hasHeader('content-encoding', 'gzip'));
|
||||
|
||||
// Should have gzipped body
|
||||
expect(response.body, isEmpty);
|
||||
});
|
||||
|
||||
test('normal', () async {
|
||||
var response = await client.get('/bar');
|
||||
|
||||
// Should say it is gzipped...
|
||||
expect(response, hasHeader('content-encoding', 'gzip'));
|
||||
|
||||
// Should have normal body
|
||||
expect(response, hasBody('normal'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue