This commit is contained in:
Tobe O 2019-10-12 11:19:57 -04:00
parent 49ea99ed6c
commit daca263062
7 changed files with 54 additions and 35 deletions

View file

@ -1,3 +1,9 @@
# 2.2.0
* Use `http.Client` instead of `http.BaseClient`, and make it an
optional parameter.
* Allow `baseUrl` to accept `Uri` or `String`.
* Add `Proxy.pushState`.
# 2.1.2
* Apply lints.

View file

@ -10,12 +10,9 @@ import 'package:angel_proxy/angel_proxy.dart';
import 'package:http/http.dart' as http;
main() async {
// ...
var client = http.Client();
// Forward requests instead of serving statically
var proxy1 = Proxy(client, Uri.parse('http://localhost:3000'));
// Forward requests instead of serving statically.
// You can also pass a URI, instead of a string.
var proxy1 = Proxy('http://localhost:3000');
// handle all methods (GET, POST, ...)
app.fallback(proxy.handleRequest);
@ -24,12 +21,12 @@ main() async {
You can also restrict the proxy to serving only from a specific root:
```dart
Proxy(client, baseUrl, publicPath: '/remote');
Proxy(baseUrl, publicPath: '/remote');
```
Also, you can map requests to a root path on the remote server:
```dart
Proxy(client, baseUrl.replace(path: '/path'));
Proxy(baseUrl.replace(path: '/path'));
```
Request bodies will be forwarded as well, if they are not empty. This allows things like POST requests to function.

View file

@ -2,20 +2,17 @@ import 'dart:io';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:angel_proxy/angel_proxy.dart';
import 'package:http/io_client.dart' as http;
import 'package:logging/logging.dart';
final Duration timeout = Duration(seconds: 5);
main() async {
var app = Angel();
var client = http.IOClient();
// 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 = Proxy(
client,
Uri.parse('https://pub.dartlang.org'),
'https://pub.dartlang.org',
publicPath: '/pub',
timeout: timeout,
);
@ -25,8 +22,7 @@ main() async {
//
// Play around with this at http://www.websocket.org/echo.html.
var echoProxy = Proxy(
client,
Uri.parse('http://echo.websocket.org'),
'http://echo.websocket.org',
publicPath: '/echo',
timeout: timeout,
);
@ -40,8 +36,7 @@ main() async {
// Anything else should fall through to dartlang.org.
var dartlangProxy = Proxy(
client,
Uri.parse('https://dartlang.org'),
'https://dartlang.org',
timeout: timeout,
recoverFrom404: false,
);

View file

@ -10,10 +10,14 @@ import 'package:path/path.dart' as p;
final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
final MediaType _fallbackMediaType = MediaType('application', 'octet-stream');
/// A middleware class that forwards requests (reverse proxies) to an upstream server.
///
/// Supports WebSockets, in addition to regular HTTP requests.
class Proxy {
String _prefix;
final http.BaseClient httpClient;
/// The underlying [Client] to use.
final http.Client 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;
@ -25,26 +29,53 @@ class Proxy {
final Duration timeout;
Proxy(
this.httpClient,
this.baseUrl, {
baseUrl, {
http.Client httpClient,
this.publicPath = '/',
this.recoverFromDead = true,
this.recoverFrom404 = true,
this.timeout,
}) {
if (!baseUrl.hasScheme || !baseUrl.hasAuthority)
}) : this.baseUrl = baseUrl is Uri ? baseUrl : Uri.parse(baseUrl.toString()),
this.httpClient = httpClient ?? http.Client() {
if (!this.baseUrl.hasScheme || !this.baseUrl.hasAuthority) {
throw ArgumentError(
'Invalid `baseUrl`. URI must have both a scheme and authority.');
if (this.recoverFromDead == null)
}
if (this.recoverFromDead == null) {
throw ArgumentError.notNull("recoverFromDead");
if (this.recoverFrom404 == null)
}
if (this.recoverFrom404 == null) {
throw ArgumentError.notNull("recoverFrom404");
}
_prefix = publicPath?.replaceAll(_straySlashes, '') ?? '';
}
void close() => httpClient.close();
/// A handler that serves the file at the given path, unless the user has requested that path.
///
/// You can also limit this functionality to specific values of the `Accept` header, ex. `text/html`.
/// If [accepts] is `null`, OR at least one of the content types in [accepts] is present,
/// the view will be served.
RequestHandler pushState(String path, {Iterable accepts}) {
var vPath = path.replaceAll(_straySlashes, '');
if (_prefix?.isNotEmpty == true) vPath = '$_prefix/$vPath';
return (RequestContext req, ResponseContext res) {
var path = req.path.replaceAll(_straySlashes, '');
if (path == vPath) return Future<bool>.value(true);
if (accepts?.isNotEmpty == true) {
if (!accepts.any((x) => req.accepts(x, strict: true))) {
return Future<bool>.value(true);
}
}
return servePath(vPath, req, res);
};
}
/// Handles an incoming HTTP request.
Future<bool> handleRequest(RequestContext req, ResponseContext res) {
var path = req.path.replaceAll(_straySlashes, '');

View file

@ -1,6 +1,6 @@
name: angel_proxy
description: Angel middleware to forward requests to another server (i.e. pub serve).
version: 2.1.2
version: 2.2.0
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/proxy
environment:

View file

@ -17,12 +17,10 @@ main() {
setUp(() async {
app = Angel();
var appHttp = AngelHttp(app);
var httpClient = http.IOClient();
testServer = await startTestServer();
var proxy1 = Proxy(
httpClient,
Uri(
scheme: 'http',
host: testServer.address.address,
@ -30,7 +28,7 @@ main() {
publicPath: '/proxy',
);
var proxy2 = Proxy(httpClient, proxy1.baseUrl.replace(path: '/foo'));
var proxy2 = Proxy(proxy1.baseUrl.replace(path: '/foo'));
print('Proxy 1 on: ${proxy1.baseUrl}');
print('Proxy 2 on: ${proxy2.baseUrl}');
@ -42,10 +40,6 @@ main() {
res.write('intercept empty');
});
app.shutdownHooks.add((_) async {
httpClient.close();
});
app.logger = Logger('angel');
Logger.root.onRecord.listen((rec) {

View file

@ -4,7 +4,6 @@ import 'package:angel_framework/angel_framework.dart';
import 'package:angel_framework/http.dart';
import 'package:angel_proxy/angel_proxy.dart';
import 'package:angel_test/angel_test.dart';
import 'package:http/io_client.dart' as http;
import 'package:logging/logging.dart';
import 'package:mock_request/mock_request.dart';
import 'package:test/test.dart';
@ -37,10 +36,7 @@ main() {
});
app.get('/bar', (req, res) => res.write('normal'));
var httpClient = http.IOClient();
layer = Proxy(
httpClient,
Uri(scheme: 'http', host: server.address.address, port: server.port),
publicPath: '/proxy',
);