This commit is contained in:
thosakwe 2017-06-20 15:31:35 -04:00
parent 5c8f3e28b1
commit e05d35ee67
7 changed files with 121 additions and 46 deletions

View file

@ -5,6 +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" />
@ -13,7 +14,6 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart SDK" level="application" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

11
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Multiple Proxies",
"type": "dart-cli",
"request": "launch",
"program": "${workspaceRoot}/example/multiple.dart"
}
]
}

View file

@ -1,7 +1,6 @@
# angel_proxy
[![version 1.0.7](https://img.shields.io/badge/version-1.0.7-brightgreen.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).

38
example/multiple.dart Normal file
View file

@ -0,0 +1,38 @@
import 'dart:io';
import 'package:angel_diagnostics/angel_diagnostics.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_proxy/angel_proxy.dart';
final Duration TIMEOUT = new Duration(seconds: 5);
main() async {
var app = new Angel();
// 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);
// 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);
});
// Anything else should fall through to dartlang.org.
await app.configure(new ProxyLayer('dartlang.org', 80, timeout: TIMEOUT));
// In case we can't connect to dartlang.org, show an error.
app.after.add('Couldn\'t connect to Pub or dartlang.');
await app.configure(logRequests());
app.fatalErrorStream.listen((AngelFatalError e) {
print(e.error);
print(e.stack);
});
var server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 8080);
print('Listening at http://${server.address.address}:${server.port}');
}

View file

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:angel_framework/angel_framework.dart';
@ -51,30 +52,31 @@ class ProxyLayer {
final String host, mapTo, publicPath;
final int port;
final String protocol;
final Duration timeout;
ProxyLayer(this.host, this.port,
{this.debug: false,
this.mapTo: '/',
this.publicPath: '/',
this.protocol: 'http',
this.recoverFromDead: true,
this.recoverFrom404: true,
this.streamToIO: false,
SecurityContext securityContext}) {
ProxyLayer(
this.host,
this.port, {
this.debug: false,
this.mapTo: '/',
this.publicPath: '/',
this.protocol: 'http',
this.recoverFromDead: true,
this.recoverFrom404: true,
this.streamToIO: false,
this.timeout,
SecurityContext securityContext,
}) {
_client = new HttpClient(context: securityContext);
_prefix = publicPath.replaceAll(_straySlashes, '');
}
call(Angel app) async => serve(this.app = app);
_printDebug(msg) {
if (debug == true) print(msg);
}
void close() => _client.close(force: true);
void serve(Router router) {
_printDebug('Public path prefix: "$_prefix"');
// _printDebug('Public path prefix: "$_prefix"');
handler(RequestContext req, ResponseContext res) async {
var path = req.path.replaceAll(_straySlashes, '');
@ -93,34 +95,55 @@ class ProxyLayer {
}
// Create mapping
_printDebug('Serving path $_path via proxy');
// _printDebug('Serving path $_path via proxy');
final mapping = '$mapTo/$_path'.replaceAll(_straySlashes, '');
_printDebug('Mapped path $_path to path $mapping on proxy $host:$port');
// _printDebug('Mapped path $_path to path $mapping on proxy $host:$port');
try {
final rq = await _client.open(req.method, host, port, mapping);
_printDebug('Opened client request');
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"');
copyHeaders(req.headers, rq.headers);
_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');
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 (app.storeOriginalBuffer == true) {
await req.parse();
if (req.originalBuffer?.isNotEmpty == true) rq.add(req.originalBuffer);
if (app.storeOriginalBuffer == true) {
await req.parse();
if (req.originalBuffer?.isNotEmpty == true)
rq.add(req.originalBuffer);
}
await rq.flush();
return await rq.close();
}
await rq.flush();
rs = await rq.close();
var future = accessRemote();
if (timeout != null) future = future.timeout(timeout);
rs = await future;
} on TimeoutException catch (e, st) {
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.');
} catch (e) {
if (recoverFromDead != false)
return true;
@ -128,8 +151,8 @@ class ProxyLayer {
rethrow;
}
_printDebug(
'Proxy responded to $mapping with status code ${rs.statusCode}');
// _printDebug(
// 'Proxy responded to $mapping with status code ${rs.statusCode}');
if (rs.statusCode == 404 && recoverFrom404 != false) return true;
@ -137,7 +160,7 @@ class ProxyLayer {
..statusCode = rs.statusCode
..contentType = rs.headers.contentType;
_printDebug('Proxy response headers:\n${rs.headers}');
// _printDebug('Proxy response headers:\n${rs.headers}');
if (streamToIO == true) {
res
@ -150,7 +173,7 @@ class ProxyLayer {
if (rs.headers.contentType != null)
res.io.headers.contentType = rs.headers.contentType;
_printDebug('Outgoing content length: ${res.io.contentLength}');
// _printDebug('Outgoing content length: ${res.io.contentLength}');
if (rs.headers[HttpHeaders.CONTENT_ENCODING]?.contains('gzip') == true) {
res.io.headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip');

View file

@ -11,7 +11,8 @@ class PubServeLayer extends ProxyLayer {
String mapTo: '/',
int port: 8080,
String protocol: 'http',
String publicPath: '/'})
String publicPath: '/',
Duration timeout})
: super(host, port,
debug: debug,
mapTo: mapTo,
@ -19,7 +20,8 @@ class PubServeLayer extends ProxyLayer {
publicPath: publicPath,
recoverFromDead: recoverFromDead != false,
recoverFrom404: recoverFrom404 != false,
streamToIO: streamToIO != false);
streamToIO: streamToIO != false,
timeout: timeout);
@override
void serve(Router router) {

View file

@ -1,6 +1,6 @@
name: angel_proxy
description: Angel middleware to forward requests to another server (i.e. pub serve).
version: 1.0.7
version: 1.0.8
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/proxy
environment:
@ -9,6 +9,8 @@ dependencies:
angel_framework: ^1.0.0-dev
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
test: ^0.12.15