Embed Angel within shelf

This commit is contained in:
Tobe O 2019-10-14 12:38:30 -04:00
parent b84da76815
commit 071129902a
5 changed files with 121 additions and 7 deletions

View file

@ -0,0 +1,49 @@
import 'dart:io';
import 'package:angel_container/mirrors.dart';
import 'package:angel_framework/angel_framework.dart';
import 'package:angel_shelf/angel_shelf.dart';
import 'package:logging/logging.dart';
import 'package:pretty_logging/pretty_logging.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_static/shelf_static.dart';
main() async {
Logger.root
..level = Level.ALL
..onRecord.listen(prettyLog);
// Create a basic Angel server, with some routes.
var app = Angel(
logger: Logger('angel_shelf_demo'),
reflector: MirrorsReflector(),
);
app.get('/angel', (req, res) {
res.write('Angel embedded within shelf!');
return false;
});
app.get('/hello', ioc((@Query('name') String name) {
return {'hello': name};
}));
// Next, create an AngelShelf driver.
// If we have startup hooks we want to run, we need to call
// `startServer`. Otherwise, it can be omitted.
var angelShelf = AngelShelf(app);
await angelShelf.startServer();
// Create, and mount, a shelf pipeline...
// You can also embed Angel as a middleware...
var mwHandler = shelf.Pipeline()
.addMiddleware(angelShelf.middleware)
.addHandler(createStaticHandler('.',
defaultDocument: 'index.html', listDirectories: true));
// Run the servers.
await shelf_io.serve(mwHandler, InternetAddress.loopbackIPv4, 8080);
await shelf_io.serve(angelShelf.handler, InternetAddress.loopbackIPv4, 8081);
print('Angel as middleware: http://localhost:8080');
print('Angel as only handler: http://localhost:8081');
}

View file

@ -10,7 +10,7 @@ main() async {
Logger.root Logger.root
..level = Level.ALL ..level = Level.ALL
..onRecord.listen(prettyLog); ..onRecord.listen(prettyLog);
var app = Angel(logger: Logger('angel_shelf_demo')); var app = Angel(logger: Logger('angel_shelf_demo'));
var http = AngelHttp(app); var http = AngelHttp(app);

View file

@ -1,2 +1,5 @@
export 'src/convert.dart'; export 'src/convert.dart';
export 'src/embed_shelf.dart'; export 'src/embed_shelf.dart';
export 'src/shelf_driver.dart';
export 'src/shelf_request.dart';
export 'src/shelf_response.dart';

View file

@ -7,15 +7,68 @@ import 'shelf_response.dart';
class AngelShelf extends Driver<shelf.Request, ShelfResponseContext, class AngelShelf extends Driver<shelf.Request, ShelfResponseContext,
Stream<shelf.Request>, ShelfRequestContext, ShelfResponseContext> { Stream<shelf.Request>, ShelfRequestContext, ShelfResponseContext> {
AngelShelf.custom(Angel app, {bool useZone = true}) final StreamController<shelf.Request> incomingRequests = StreamController();
: super(app, (_, __) => throw _unsupported(), useZone: useZone);
final FutureOr<shelf.Response> Function() notFound;
AngelShelf(Angel app, {FutureOr<shelf.Response> Function() notFound})
: this.notFound =
notFound ?? (() => shelf.Response.notFound('Not Found')),
super(app, null, useZone: false) {
// Inject a final handler that will keep responses open, if we are using the
// driver as a middleware.
app.fallback((req, res) {
if (res is ShelfResponseContext) {
res.closeSilently();
}
return true;
});
}
Future<Stream<shelf.Request>> Function(dynamic, int) get serverGenerator =>
(_, __) async => incomingRequests.stream;
static UnsupportedError _unsupported() => UnsupportedError( static UnsupportedError _unsupported() => UnsupportedError(
'AngelShelf cannot mount a standalone server, or return a URI.'); 'AngelShelf cannot mount a standalone server, or return a URI.');
Future<shelf.Response> handler(shelf.Request request) async { Future<shelf.Response> handler(shelf.Request request) async {
var response = ShelfResponseContext(app); var response = ShelfResponseContext(app);
await handleRawRequest(request, response); var result = await handleRawRequest(request, response);
if (result is shelf.Response) {
return result;
} else if (!response.isOpen) {
return response.shelfResponse;
} else {
// return await handler(request);
return notFound();
}
}
shelf.Handler middleware(shelf.Handler handler) {
return (request) async {
var response = ShelfResponseContext(app);
var result = await handleRawRequest(request, response);
if (result is shelf.Response) {
return result;
} else if (!response.isOpen) {
return response.shelfResponse;
} else {
return await handler(request);
}
};
}
@override
Future<shelf.Response> handleAngelHttpException(
AngelHttpException e,
StackTrace st,
RequestContext req,
ResponseContext res,
shelf.Request request,
ShelfResponseContext response,
{bool ignoreFinalizers = false}) async {
await super.handleAngelHttpException(e, st, req, res, request, response,
ignoreFinalizers: ignoreFinalizers);
return response.shelfResponse; return response.shelfResponse;
} }
@ -38,7 +91,7 @@ class AngelShelf extends Driver<shelf.Request, ShelfResponseContext,
@override @override
Future<ShelfRequestContext> createRequestContext( Future<ShelfRequestContext> createRequestContext(
shelf.Request request, ShelfResponseContext response) { shelf.Request request, ShelfResponseContext response) {
var path = uri.path.replaceAll(_straySlashes, ''); var path = request.url.path.replaceAll(_straySlashes, '');
if (path.isEmpty) path = '/'; if (path.isEmpty) path = '/';
var rq = var rq =
ShelfRequestContext(app, app.container.createChild(), request, path); ShelfRequestContext(app, app.container.createChild(), request, path);

View file

@ -8,12 +8,19 @@ import 'shelf_request.dart';
class ShelfResponseContext extends ResponseContext<ShelfResponseContext> { class ShelfResponseContext extends ResponseContext<ShelfResponseContext> {
final Angel app; final Angel app;
final StreamController<List<int>> _ctrl = StreamController(); final StreamController<List<int>> _ctrl = StreamController();
bool _isOpen = true, _isDetached = false; bool _isOpen = true,
_isDetached = false,
_wasClosedByHandler = false,
_handlersAreDone = false;
ShelfResponseContext(this.app); ShelfResponseContext(this.app);
ShelfRequestContext _correspondingRequest; ShelfRequestContext _correspondingRequest;
bool get wasClosedByHandler => _wasClosedByHandler;
void closeSilently() => _handlersAreDone = true;
ShelfRequestContext get correspondingRequest => _correspondingRequest; ShelfRequestContext get correspondingRequest => _correspondingRequest;
set correspondingRequest(ShelfRequestContext value) { set correspondingRequest(ShelfRequestContext value) {
@ -30,7 +37,9 @@ class ShelfResponseContext extends ResponseContext<ShelfResponseContext> {
@override @override
Future<void> close() { Future<void> close() {
_isOpen = false; if (!_handlersAreDone) {
_isOpen = false;
}
_ctrl.close(); _ctrl.close();
return super.close(); return super.close();
} }