import 'dart:async'; import 'dart:io'; import 'package:angel_framework/angel_framework.dart'; import 'package:shelf/shelf.dart' as shelf; import 'shelf_request.dart'; import 'shelf_response.dart'; class AngelShelf extends Driver, ShelfRequestContext, ShelfResponseContext> { final StreamController incomingRequests = StreamController(); final FutureOr Function() notFound; AngelShelf(Angel app, {FutureOr 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> close() { incomingRequests.close(); return super.close(); } Future> Function(dynamic, int) get serverGenerator => (_, __) async => incomingRequests.stream; static UnsupportedError _unsupported() => UnsupportedError( 'AngelShelf cannot mount a standalone server, or return a URI.'); Future handler(shelf.Request 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); 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 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; } @override void addCookies(ShelfResponseContext response, Iterable cookies) { // Don't do anything here, otherwise you get duplicate cookies. // response.cookies.addAll(cookies); } @override Future closeResponse(ShelfResponseContext response) { return response.close(); } @override Uri get uri => throw _unsupported(); static final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)'); @override Future createRequestContext( shelf.Request request, ShelfResponseContext response) { var path = request.url.path.replaceAll(_straySlashes, ''); if (path.isEmpty) path = '/'; var rq = ShelfRequestContext(app, app.container.createChild(), request, path); return Future.value(rq); } @override Future createResponseContext( shelf.Request request, ShelfResponseContext response, [ShelfRequestContext correspondingRequest]) { // Return the original response. return Future.value(response..correspondingRequest = correspondingRequest); } @override Stream createResponseStreamFromRawRequest( shelf.Request request) { return Stream.fromIterable([null]); } @override void setChunkedEncoding(ShelfResponseContext response, bool value) { response.chunked = value; } @override void setContentLength(ShelfResponseContext response, int length) { response.contentLength = length; } @override void setHeader(ShelfResponseContext response, String key, String value) { response.headers[key] = value; } @override void setStatusCode(ShelfResponseContext response, int value) { response.statusCode = value; } @override void writeStringToResponse(ShelfResponseContext response, String value) { response.write(value); } @override void writeToResponse(ShelfResponseContext response, List data) { response.add(data); } }