diff --git a/CHANGELOG.md b/CHANGELOG.md index 7801bfa7..cbd058d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # 2.0.1 * Tracked down a bug in `Driver.runPipeline` that allowed fallback handlers to run, even after the response was closed. +* Add `RequestContext.shutdownHooks`. +* Call `RequestContext.close` in `Driver.sendResponse`. +* AngelConfigurer is now `FutureOr`, instead of just `FutureOr`. +* Use a `Container.has` check in `Driver.sendResponse`. # 2.0.0 * Angel 2! :angel: :rocket: diff --git a/lib/src/core/driver.dart b/lib/src/core/driver.dart index 2be90faa..ecc6fce1 100644 --- a/lib/src/core/driver.dart +++ b/lib/src/core/driver.dart @@ -280,12 +280,15 @@ abstract class Driver< Future sendResponse(Request request, Response response, RequestContext req, ResponseContext res, {bool ignoreFinalizers = false}) { - void _cleanup(_) { - if (!app.environment.isProduction && app.logger != null) { + Future _cleanup(_) { + if (!app.environment.isProduction && + app.logger != null && + req.container.has()) { var sw = req.container.make(); app.logger.info( "${res.statusCode} ${req.method} ${req.uri} (${sw?.elapsedMilliseconds ?? 'unknown'} ms)"); } + return req.close(); } if (!res.isBuffered) return res.close().then(_cleanup); diff --git a/lib/src/core/request_context.dart b/lib/src/core/request_context.dart index 0e1853e0..db823fe8 100644 --- a/lib/src/core/request_context.dart +++ b/lib/src/core/request_context.dart @@ -14,6 +14,7 @@ import 'dart:io' import 'package:angel_container/angel_container.dart'; import 'package:http_parser/http_parser.dart'; import 'package:http_server/http_server.dart'; +import 'package:meta/meta.dart'; import 'package:mime/mime.dart'; import 'package:path/path.dart' as p; @@ -26,8 +27,12 @@ part 'injection.dart'; /// A convenience wrapper around an incoming [RawRequest]. abstract class RequestContext { + /// Similar to [Angel.shutdownHooks], allows for logic to be executed + /// when a [RequestContext] is done being processed. + final List Function()> shutdownHooks = []; + String _acceptHeaderCache, _extensionCache; - bool _acceptsAllCache, _hasParsedBody = false; + bool _acceptsAllCache, _hasParsedBody = false, _closed = false; Map _bodyFields, _queryParameters; List _bodyList; Object _bodyObject; @@ -271,12 +276,16 @@ abstract class RequestContext { } /// Disposes of all resources. - Future close() { - _acceptsAllCache = null; - _acceptHeaderCache = null; - serviceParams.clear(); - params.clear(); - return new Future.value(); + @mustCallSuper + Future close() async { + if (!_closed) { + _closed = true; + _acceptsAllCache = null; + _acceptHeaderCache = null; + serviceParams.clear(); + params.clear(); + await Future.forEach(shutdownHooks, (hook) => hook()); + } } } diff --git a/lib/src/core/server.dart b/lib/src/core/server.dart index 8d5d00d8..82758ead 100644 --- a/lib/src/core/server.dart +++ b/lib/src/core/server.dart @@ -22,7 +22,7 @@ import 'service.dart'; //final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)'); /// A function that configures an [Angel] server in some way. -typedef FutureOr AngelConfigurer(Angel app); +typedef FutureOr AngelConfigurer(Angel app); /// A function that asynchronously generates a view from the given path and data. typedef FutureOr ViewGenerator(String path, diff --git a/test/all.dart b/test/all.dart index 8a944ff6..54196b46 100644 --- a/test/all.dart +++ b/test/all.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:io/ansi.dart'; +import '404_hole_test.dart' as hole404; import 'accepts_test.dart' as accepts; import 'anonymous_service_test.dart' as anonymous_service; import 'body_test.dart' as body; @@ -30,6 +31,7 @@ import 'package:test/test.dart'; /// For running with coverage main() { print(cyan.wrap('Running tests on ${Platform.version}')); + group('404_hole', hole404.main); group('accepts', accepts.main); group('anonymous service', anonymous_service.main); group('body', body.main);