1.0.8
This commit is contained in:
parent
5c8f3e28b1
commit
e05d35ee67
7 changed files with 121 additions and 46 deletions
|
@ -5,6 +5,7 @@
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/example/packages" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/packages" />
|
<excludeFolder url="file://$MODULE_DIR$/packages" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/test/packages" />
|
<excludeFolder url="file://$MODULE_DIR$/test/packages" />
|
||||||
|
@ -13,7 +14,6 @@
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||||
<orderEntry type="library" name="Dart SDK" level="application" />
|
|
||||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Multiple Proxies",
|
||||||
|
"type": "dart-cli",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/example/multiple.dart"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
# angel_proxy
|
# angel_proxy
|
||||||
|
|
||||||
[](https://pub.dartlang.org/packages/angel_proxy)
|
[](https://pub.dartlang.org/packages/angel_proxy)[](https://travis-ci.org/angel-dart/proxy)
|
||||||
[](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).
|
||||||
|
|
||||||
|
|
38
example/multiple.dart
Normal file
38
example/multiple.dart
Normal 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}');
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
|
||||||
|
@ -51,30 +52,31 @@ class ProxyLayer {
|
||||||
final String host, mapTo, publicPath;
|
final String host, mapTo, publicPath;
|
||||||
final int port;
|
final int port;
|
||||||
final String protocol;
|
final String protocol;
|
||||||
|
final Duration timeout;
|
||||||
|
|
||||||
ProxyLayer(this.host, this.port,
|
ProxyLayer(
|
||||||
{this.debug: false,
|
this.host,
|
||||||
this.mapTo: '/',
|
this.port, {
|
||||||
this.publicPath: '/',
|
this.debug: false,
|
||||||
this.protocol: 'http',
|
this.mapTo: '/',
|
||||||
this.recoverFromDead: true,
|
this.publicPath: '/',
|
||||||
this.recoverFrom404: true,
|
this.protocol: 'http',
|
||||||
this.streamToIO: false,
|
this.recoverFromDead: true,
|
||||||
SecurityContext securityContext}) {
|
this.recoverFrom404: true,
|
||||||
|
this.streamToIO: false,
|
||||||
|
this.timeout,
|
||||||
|
SecurityContext securityContext,
|
||||||
|
}) {
|
||||||
_client = new HttpClient(context: securityContext);
|
_client = new HttpClient(context: securityContext);
|
||||||
_prefix = publicPath.replaceAll(_straySlashes, '');
|
_prefix = publicPath.replaceAll(_straySlashes, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
call(Angel app) async => serve(this.app = app);
|
call(Angel app) async => serve(this.app = app);
|
||||||
|
|
||||||
_printDebug(msg) {
|
|
||||||
if (debug == true) print(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() => _client.close(force: true);
|
void close() => _client.close(force: true);
|
||||||
|
|
||||||
void serve(Router router) {
|
void serve(Router router) {
|
||||||
_printDebug('Public path prefix: "$_prefix"');
|
// _printDebug('Public path prefix: "$_prefix"');
|
||||||
|
|
||||||
handler(RequestContext req, ResponseContext res) async {
|
handler(RequestContext req, ResponseContext res) async {
|
||||||
var path = req.path.replaceAll(_straySlashes, '');
|
var path = req.path.replaceAll(_straySlashes, '');
|
||||||
|
@ -93,34 +95,55 @@ class ProxyLayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create mapping
|
// Create mapping
|
||||||
_printDebug('Serving path $_path via proxy');
|
// _printDebug('Serving path $_path via proxy');
|
||||||
final mapping = '$mapTo/$_path'.replaceAll(_straySlashes, '');
|
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 {
|
try {
|
||||||
final rq = await _client.open(req.method, host, port, mapping);
|
Future<HttpClientResponse> accessRemote() async {
|
||||||
_printDebug('Opened client request');
|
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);
|
copyHeaders(req.headers, rq.headers);
|
||||||
_printDebug('Copied headers');
|
rq.headers.set(HttpHeaders.HOST, host);
|
||||||
rq.cookies.addAll(req.cookies ?? []);
|
// _printDebug('Copied headers');
|
||||||
_printDebug('Added cookies');
|
rq.cookies.addAll(req.cookies ?? []);
|
||||||
rq.headers
|
// _printDebug('Added cookies');
|
||||||
.set('X-Forwarded-For', req.io.connectionInfo.remoteAddress.address);
|
rq.headers.set(
|
||||||
rq.headers
|
'X-Forwarded-For', req.io.connectionInfo.remoteAddress.address);
|
||||||
..set('X-Forwarded-Port', req.io.connectionInfo.remotePort.toString())
|
rq.headers
|
||||||
..set('X-Forwarded-Host',
|
..set('X-Forwarded-Port', req.io.connectionInfo.remotePort.toString())
|
||||||
req.headers.host ?? req.headers.value(HttpHeaders.HOST) ?? 'none')
|
..set('X-Forwarded-Host',
|
||||||
..set('X-Forwarded-Proto', protocol);
|
req.headers.host ?? req.headers.value(HttpHeaders.HOST) ?? 'none')
|
||||||
_printDebug('Added X-Forwarded headers');
|
..set('X-Forwarded-Proto', protocol);
|
||||||
|
// _printDebug('Added X-Forwarded headers');
|
||||||
|
|
||||||
if (app.storeOriginalBuffer == true) {
|
if (app.storeOriginalBuffer == true) {
|
||||||
await req.parse();
|
await req.parse();
|
||||||
if (req.originalBuffer?.isNotEmpty == true) rq.add(req.originalBuffer);
|
if (req.originalBuffer?.isNotEmpty == true)
|
||||||
|
rq.add(req.originalBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
await rq.flush();
|
||||||
|
return await rq.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
await rq.flush();
|
var future = accessRemote();
|
||||||
rs = await rq.close();
|
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) {
|
} catch (e) {
|
||||||
if (recoverFromDead != false)
|
if (recoverFromDead != false)
|
||||||
return true;
|
return true;
|
||||||
|
@ -128,8 +151,8 @@ class ProxyLayer {
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
|
||||||
_printDebug(
|
// _printDebug(
|
||||||
'Proxy responded to $mapping with status code ${rs.statusCode}');
|
// 'Proxy responded to $mapping with status code ${rs.statusCode}');
|
||||||
|
|
||||||
if (rs.statusCode == 404 && recoverFrom404 != false) return true;
|
if (rs.statusCode == 404 && recoverFrom404 != false) return true;
|
||||||
|
|
||||||
|
@ -137,7 +160,7 @@ class ProxyLayer {
|
||||||
..statusCode = rs.statusCode
|
..statusCode = rs.statusCode
|
||||||
..contentType = rs.headers.contentType;
|
..contentType = rs.headers.contentType;
|
||||||
|
|
||||||
_printDebug('Proxy response headers:\n${rs.headers}');
|
// _printDebug('Proxy response headers:\n${rs.headers}');
|
||||||
|
|
||||||
if (streamToIO == true) {
|
if (streamToIO == true) {
|
||||||
res
|
res
|
||||||
|
@ -150,7 +173,7 @@ class ProxyLayer {
|
||||||
if (rs.headers.contentType != null)
|
if (rs.headers.contentType != null)
|
||||||
res.io.headers.contentType = rs.headers.contentType;
|
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) {
|
if (rs.headers[HttpHeaders.CONTENT_ENCODING]?.contains('gzip') == true) {
|
||||||
res.io.headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip');
|
res.io.headers.set(HttpHeaders.CONTENT_ENCODING, 'gzip');
|
||||||
|
|
|
@ -11,7 +11,8 @@ class PubServeLayer extends ProxyLayer {
|
||||||
String mapTo: '/',
|
String mapTo: '/',
|
||||||
int port: 8080,
|
int port: 8080,
|
||||||
String protocol: 'http',
|
String protocol: 'http',
|
||||||
String publicPath: '/'})
|
String publicPath: '/',
|
||||||
|
Duration timeout})
|
||||||
: super(host, port,
|
: super(host, port,
|
||||||
debug: debug,
|
debug: debug,
|
||||||
mapTo: mapTo,
|
mapTo: mapTo,
|
||||||
|
@ -19,7 +20,8 @@ class PubServeLayer extends ProxyLayer {
|
||||||
publicPath: publicPath,
|
publicPath: publicPath,
|
||||||
recoverFromDead: recoverFromDead != false,
|
recoverFromDead: recoverFromDead != false,
|
||||||
recoverFrom404: recoverFrom404 != false,
|
recoverFrom404: recoverFrom404 != false,
|
||||||
streamToIO: streamToIO != false);
|
streamToIO: streamToIO != false,
|
||||||
|
timeout: timeout);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void serve(Router router) {
|
void serve(Router router) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: angel_proxy
|
name: angel_proxy
|
||||||
description: Angel middleware to forward requests to another server (i.e. pub serve).
|
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>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/proxy
|
homepage: https://github.com/angel-dart/proxy
|
||||||
environment:
|
environment:
|
||||||
|
@ -9,6 +9,8 @@ dependencies:
|
||||||
angel_framework: ^1.0.0-dev
|
angel_framework: ^1.0.0-dev
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
angel_compress: ^1.0.0
|
angel_compress: ^1.0.0
|
||||||
|
angel_diagnostics: ^1.0.0
|
||||||
angel_test: ^1.0.0
|
angel_test: ^1.0.0
|
||||||
http: ^0.11.3
|
http: ^0.11.3
|
||||||
test: ^0.12.15
|
test: ^0.12.15
|
||||||
|
|
Loading…
Reference in a new issue