2.2.0
This commit is contained in:
parent
49ea99ed6c
commit
daca263062
7 changed files with 54 additions and 35 deletions
|
@ -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
|
# 2.1.2
|
||||||
* Apply lints.
|
* Apply lints.
|
||||||
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -10,12 +10,9 @@ import 'package:angel_proxy/angel_proxy.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
// ...
|
// Forward requests instead of serving statically.
|
||||||
|
// You can also pass a URI, instead of a string.
|
||||||
var client = http.Client();
|
var proxy1 = Proxy('http://localhost:3000');
|
||||||
|
|
||||||
// Forward requests instead of serving statically
|
|
||||||
var proxy1 = Proxy(client, Uri.parse('http://localhost:3000'));
|
|
||||||
|
|
||||||
// handle all methods (GET, POST, ...)
|
// handle all methods (GET, POST, ...)
|
||||||
app.fallback(proxy.handleRequest);
|
app.fallback(proxy.handleRequest);
|
||||||
|
@ -24,12 +21,12 @@ main() async {
|
||||||
|
|
||||||
You can also restrict the proxy to serving only from a specific root:
|
You can also restrict the proxy to serving only from a specific root:
|
||||||
```dart
|
```dart
|
||||||
Proxy(client, baseUrl, publicPath: '/remote');
|
Proxy(baseUrl, publicPath: '/remote');
|
||||||
```
|
```
|
||||||
|
|
||||||
Also, you can map requests to a root path on the remote server:
|
Also, you can map requests to a root path on the remote server:
|
||||||
```dart
|
```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.
|
Request bodies will be forwarded as well, if they are not empty. This allows things like POST requests to function.
|
||||||
|
|
|
@ -2,20 +2,17 @@ import 'dart:io';
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:angel_framework/http.dart';
|
import 'package:angel_framework/http.dart';
|
||||||
import 'package:angel_proxy/angel_proxy.dart';
|
import 'package:angel_proxy/angel_proxy.dart';
|
||||||
import 'package:http/io_client.dart' as http;
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
final Duration timeout = Duration(seconds: 5);
|
final Duration timeout = Duration(seconds: 5);
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
var app = Angel();
|
var app = Angel();
|
||||||
var client = http.IOClient();
|
|
||||||
|
|
||||||
// Forward any /api requests to pub.
|
// Forward any /api requests to pub.
|
||||||
// By default, if the host throws a 404, the request will fall through to the next handler.
|
// By default, if the host throws a 404, the request will fall through to the next handler.
|
||||||
var pubProxy = Proxy(
|
var pubProxy = Proxy(
|
||||||
client,
|
'https://pub.dartlang.org',
|
||||||
Uri.parse('https://pub.dartlang.org'),
|
|
||||||
publicPath: '/pub',
|
publicPath: '/pub',
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
|
@ -25,8 +22,7 @@ main() async {
|
||||||
//
|
//
|
||||||
// Play around with this at http://www.websocket.org/echo.html.
|
// Play around with this at http://www.websocket.org/echo.html.
|
||||||
var echoProxy = Proxy(
|
var echoProxy = Proxy(
|
||||||
client,
|
'http://echo.websocket.org',
|
||||||
Uri.parse('http://echo.websocket.org'),
|
|
||||||
publicPath: '/echo',
|
publicPath: '/echo',
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
);
|
);
|
||||||
|
@ -40,8 +36,7 @@ main() async {
|
||||||
|
|
||||||
// Anything else should fall through to dartlang.org.
|
// Anything else should fall through to dartlang.org.
|
||||||
var dartlangProxy = Proxy(
|
var dartlangProxy = Proxy(
|
||||||
client,
|
'https://dartlang.org',
|
||||||
Uri.parse('https://dartlang.org'),
|
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
recoverFrom404: false,
|
recoverFrom404: false,
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,10 +10,14 @@ import 'package:path/path.dart' as p;
|
||||||
final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
|
final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
|
||||||
final MediaType _fallbackMediaType = MediaType('application', 'octet-stream');
|
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 {
|
class Proxy {
|
||||||
String _prefix;
|
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.
|
/// 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 recoverFromDead;
|
||||||
|
@ -25,26 +29,53 @@ class Proxy {
|
||||||
final Duration timeout;
|
final Duration timeout;
|
||||||
|
|
||||||
Proxy(
|
Proxy(
|
||||||
this.httpClient,
|
baseUrl, {
|
||||||
this.baseUrl, {
|
http.Client httpClient,
|
||||||
this.publicPath = '/',
|
this.publicPath = '/',
|
||||||
this.recoverFromDead = true,
|
this.recoverFromDead = true,
|
||||||
this.recoverFrom404 = true,
|
this.recoverFrom404 = true,
|
||||||
this.timeout,
|
this.timeout,
|
||||||
}) {
|
}) : this.baseUrl = baseUrl is Uri ? baseUrl : Uri.parse(baseUrl.toString()),
|
||||||
if (!baseUrl.hasScheme || !baseUrl.hasAuthority)
|
this.httpClient = httpClient ?? http.Client() {
|
||||||
|
if (!this.baseUrl.hasScheme || !this.baseUrl.hasAuthority) {
|
||||||
throw ArgumentError(
|
throw ArgumentError(
|
||||||
'Invalid `baseUrl`. URI must have both a scheme and authority.');
|
'Invalid `baseUrl`. URI must have both a scheme and authority.');
|
||||||
if (this.recoverFromDead == null)
|
}
|
||||||
|
if (this.recoverFromDead == null) {
|
||||||
throw ArgumentError.notNull("recoverFromDead");
|
throw ArgumentError.notNull("recoverFromDead");
|
||||||
if (this.recoverFrom404 == null)
|
}
|
||||||
|
if (this.recoverFrom404 == null) {
|
||||||
throw ArgumentError.notNull("recoverFrom404");
|
throw ArgumentError.notNull("recoverFrom404");
|
||||||
|
}
|
||||||
|
|
||||||
_prefix = publicPath?.replaceAll(_straySlashes, '') ?? '';
|
_prefix = publicPath?.replaceAll(_straySlashes, '') ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() => httpClient.close();
|
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.
|
/// Handles an incoming HTTP request.
|
||||||
Future<bool> handleRequest(RequestContext req, ResponseContext res) {
|
Future<bool> handleRequest(RequestContext req, ResponseContext res) {
|
||||||
var path = req.path.replaceAll(_straySlashes, '');
|
var path = req.path.replaceAll(_straySlashes, '');
|
||||||
|
|
|
@ -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: 2.1.2
|
version: 2.2.0
|
||||||
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:
|
||||||
|
|
|
@ -17,12 +17,10 @@ main() {
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
app = Angel();
|
app = Angel();
|
||||||
var appHttp = AngelHttp(app);
|
var appHttp = AngelHttp(app);
|
||||||
var httpClient = http.IOClient();
|
|
||||||
|
|
||||||
testServer = await startTestServer();
|
testServer = await startTestServer();
|
||||||
|
|
||||||
var proxy1 = Proxy(
|
var proxy1 = Proxy(
|
||||||
httpClient,
|
|
||||||
Uri(
|
Uri(
|
||||||
scheme: 'http',
|
scheme: 'http',
|
||||||
host: testServer.address.address,
|
host: testServer.address.address,
|
||||||
|
@ -30,7 +28,7 @@ main() {
|
||||||
publicPath: '/proxy',
|
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 1 on: ${proxy1.baseUrl}');
|
||||||
print('Proxy 2 on: ${proxy2.baseUrl}');
|
print('Proxy 2 on: ${proxy2.baseUrl}');
|
||||||
|
|
||||||
|
@ -42,10 +40,6 @@ main() {
|
||||||
res.write('intercept empty');
|
res.write('intercept empty');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.shutdownHooks.add((_) async {
|
|
||||||
httpClient.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
app.logger = Logger('angel');
|
app.logger = Logger('angel');
|
||||||
|
|
||||||
Logger.root.onRecord.listen((rec) {
|
Logger.root.onRecord.listen((rec) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:angel_framework/http.dart';
|
import 'package:angel_framework/http.dart';
|
||||||
import 'package:angel_proxy/angel_proxy.dart';
|
import 'package:angel_proxy/angel_proxy.dart';
|
||||||
import 'package:angel_test/angel_test.dart';
|
import 'package:angel_test/angel_test.dart';
|
||||||
import 'package:http/io_client.dart' as http;
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:mock_request/mock_request.dart';
|
import 'package:mock_request/mock_request.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
@ -37,10 +36,7 @@ main() {
|
||||||
});
|
});
|
||||||
app.get('/bar', (req, res) => res.write('normal'));
|
app.get('/bar', (req, res) => res.write('normal'));
|
||||||
|
|
||||||
var httpClient = http.IOClient();
|
|
||||||
|
|
||||||
layer = Proxy(
|
layer = Proxy(
|
||||||
httpClient,
|
|
||||||
Uri(scheme: 'http', host: server.address.address, port: server.port),
|
Uri(scheme: 'http', host: server.address.address, port: server.port),
|
||||||
publicPath: '/proxy',
|
publicPath: '/proxy',
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue