diff --git a/.idea/workspace.xml b/.idea/workspace.xml index ef4dd47b..2bba0364 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -3,10 +3,6 @@ - - - - @@ -52,7 +48,7 @@ - + @@ -73,8 +69,8 @@ - - + + @@ -96,7 +92,7 @@ - + @@ -106,7 +102,7 @@ - + @@ -124,16 +120,6 @@ - - - - - - - - - - @@ -193,8 +179,8 @@ - + @@ -372,7 +358,8 @@ - + + 1481237183504 @@ -395,7 +382,14 @@ - @@ -431,7 +425,7 @@ - @@ -478,13 +472,101 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -557,7 +639,6 @@ - @@ -573,7 +654,6 @@ - @@ -589,7 +669,6 @@ - @@ -597,7 +676,6 @@ - @@ -621,7 +699,6 @@ - @@ -637,14 +714,13 @@ - - + @@ -677,7 +753,7 @@ - + @@ -696,14 +772,13 @@ - - - + + @@ -714,13 +789,23 @@ - + + + + + + + + + + + diff --git a/README.md b/README.md index 47bf246e..b81d2026 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # angel_framework -[![pub 1.0.0-dev.32](https://img.shields.io/badge/pub-1.0.0--dev.32-red.svg)](https://pub.dartlang.org/packages/angel_framework) +[![pub 1.0.0-dev.33](https://img.shields.io/badge/pub-1.0.0--dev.33-red.svg)](https://pub.dartlang.org/packages/angel_framework) ![build status](https://travis-ci.org/angel-dart/framework.svg) Core libraries for the Angel Framework. \ No newline at end of file diff --git a/lib/src/http/http.dart b/lib/src/http/http.dart index c2859a48..5ca7da13 100644 --- a/lib/src/http/http.dart +++ b/lib/src/http/http.dart @@ -7,6 +7,7 @@ export 'angel_http_exception.dart'; export 'base_middleware.dart'; export 'base_plugin.dart'; export 'controller.dart'; +export 'fatal_error.dart'; export 'hooked_service.dart'; export 'metadata.dart'; export 'memory_service.dart'; diff --git a/lib/src/http/response_context.dart b/lib/src/http/response_context.dart index ec7b7d5a..37d94292 100644 --- a/lib/src/http/response_context.dart +++ b/lib/src/http/response_context.dart @@ -19,6 +19,15 @@ class ResponseContext extends Extensible { /// The [Angel] instance that is sending a response. AngelBase app; + /// Any and all cookies to be sent to the user. + final List cookies = []; + + /// Headers that will be sent to the user. + final Map headers = {}; + + /// This response's status code. + int statusCode = 200; + /// Can we still write to this response? bool get isOpen => _isOpen; @@ -26,8 +35,9 @@ class ResponseContext extends Extensible { final BytesBuilder buffer = new BytesBuilder(); /// Sets the status code to be sent with this response. + @Deprecated('Please use `statusCode=` instead.') void status(int code) { - io.statusCode = code; + statusCode = code; } /// The underlying [HttpResponse] under this instance. @@ -41,18 +51,15 @@ class ResponseContext extends Extensible { ResponseContext(this.io, this.app); - /// Any and all cookies to be sent to the user. - List get cookies => io.cookies; - /// Set this to true if you will manually close the response. bool willCloseItself = false; /// Sends a download as a response. download(File file, {String filename}) async { - header("Content-Disposition", - 'attachment; filename="${filename ?? file.path}"'); - header(HttpHeaders.CONTENT_TYPE, lookupMimeType(file.path)); - header(HttpHeaders.CONTENT_LENGTH, file.lengthSync().toString()); + headers["Content-Disposition"] = + 'attachment; filename="${filename ?? file.path}"'; + headers[HttpHeaders.CONTENT_TYPE] = lookupMimeType(file.path); + headers[HttpHeaders.CONTENT_LENGTH] = file.lengthSync().toString(); buffer.add(await file.readAsBytes()); end(); } @@ -63,31 +70,32 @@ class ResponseContext extends Extensible { } /// Sets a response header to the given value, or retrieves its value. + @Deprecated('Please use `headers` instead.') header(String key, [String value]) { if (value == null) - return io.headers[key]; + return headers[key]; else - io.headers.set(key, value); + headers[key] = value; } /// Serializes JSON to the response. void json(value) { write(god.serialize(value)); - header(HttpHeaders.CONTENT_TYPE, ContentType.JSON.toString()); + headers[HttpHeaders.CONTENT_TYPE] = ContentType.JSON.toString(); end(); } /// Returns a JSONP response. void jsonp(value, {String callbackName: "callback"}) { write("$callbackName(${god.serialize(value)})"); - header(HttpHeaders.CONTENT_TYPE, "application/javascript"); + headers[HttpHeaders.CONTENT_TYPE] = "application/javascript"; end(); } /// Renders a view to the response stream, and closes the response. Future render(String view, [Map data]) async { write(await app.viewGenerator(view, data)); - header(HttpHeaders.CONTENT_TYPE, ContentType.HTML.toString()); + headers[HttpHeaders.CONTENT_TYPE] = ContentType.HTML.toString(); end(); } @@ -99,9 +107,9 @@ class ResponseContext extends Extensible { /// /// See [Router]#navigate for more. :) void redirect(url, {bool absolute: true, int code: 301}) { - header(HttpHeaders.LOCATION, - url is String ? url : app.navigate(url, absolute: absolute)); - status(code ?? 301); + headers[HttpHeaders.LOCATION] = + url is String ? url : app.navigate(url, absolute: absolute); + statusCode = code ?? 301; write(''' @@ -175,15 +183,13 @@ class ResponseContext extends Extensible { } /// Streams a file to this response as chunked data. - /// - /// Useful for video sites. Future streamFile(File file, {int chunkSize, int sleepMs: 0, bool resumable: true}) async { if (!isOpen) return; - header(HttpHeaders.CONTENT_TYPE, lookupMimeType(file.path)); - willCloseItself = true; - await file.openRead().pipe(io); + headers[HttpHeaders.CONTENT_TYPE] = lookupMimeType(file.path); + end(); + buffer.add(await file.readAsBytes()); } /// Writes data to the response. diff --git a/lib/src/http/routable.dart b/lib/src/http/routable.dart index 16c3ef87..7206d878 100644 --- a/lib/src/http/routable.dart +++ b/lib/src/http/routable.dart @@ -1,7 +1,6 @@ library angel_framework.http.routable; import 'dart:async'; -import 'dart:io'; import 'package:angel_route/angel_route.dart'; import '../util.dart'; import 'angel_base.dart'; @@ -20,9 +19,6 @@ typedef Future RequestMiddleware(RequestContext req, ResponseContext res); /// A function that receives an incoming [RequestContext] and responds to it. typedef Future RequestHandler(RequestContext req, ResponseContext res); -/// A function that handles an [HttpRequest]. -typedef Future RawRequestHandler(HttpRequest request); - /// A routable server that can handle dynamic requests. class Routable extends Router { final Map _controllers = {}; diff --git a/lib/src/http/server.dart b/lib/src/http/server.dart index e1b5fe68..69ab054e 100644 --- a/lib/src/http/server.dart +++ b/lib/src/http/server.dart @@ -56,11 +56,16 @@ class Angel extends AngelBase { /// **NOTE**: This is a broadcast stream. Stream get onController => _onController.stream; + /// Always run before responses are sent. + /// + /// These will only not run if an [AngelFatalError] occurs. + final List responseFinalizers = []; + /// Default error handler, show HTML error page AngelErrorHandler _errorHandler = (AngelHttpException e, req, ResponseContext res) { - res.header(HttpHeaders.CONTENT_TYPE, ContentType.HTML.toString()); - res.status(e.statusCode); + res.heades[HttpHeaders.CONTENT_TYPE] = ContentType.HTML.toString(); + res.statusCode = e.statusCode; res.write("${e.message}"); res.write("

${e.message}

    "); for (String error in e.errors) { @@ -137,17 +142,6 @@ class Angel extends AngelBase { return res.isOpen; } - if (handler is RawRequestHandler) { - var result = await handler(req.io); - if (result is bool) - return result == true; - else if (result != null) { - res.json(result); - return false; - } else - return true; - } - if (handler is Future) { var result = await handler; if (result is bool) @@ -200,6 +194,8 @@ class Angel extends AngelBase { } final m = new MiddlewarePipeline(resolved); + req.inject(MiddlewarePipeline, m); + final pipeline = []..addAll(before)..addAll(m.handlers)..addAll(after); _printDebug('Handler sequence on $requestedUrl: $pipeline'); @@ -224,7 +220,7 @@ class Angel extends AngelBase { if (e is AngelHttpException) { // Special handling for AngelHttpExceptions :) try { - res.status(e.statusCode); + res.statusCode = e.statusCode; String accept = request.headers.value(HttpHeaders.ACCEPT); if (accept == "*/*" || accept.contains(ContentType.JSON.mimeType) || @@ -233,12 +229,14 @@ class Angel extends AngelBase { } else { await _errorHandler(e, req, res); } - _finalizeResponse(request, res); + // _finalizeResponse(request, res); } catch (e, st) { - _fatalErrorStream.add(new AngelFatalError(request: request, error: e, stack: st)); + _fatalErrorStream.add( + new AngelFatalError(request: request, error: e, stack: st)); } } else { - _fatalErrorStream.add(new AngelFatalError(request: request, error: e, stack: st)); + _fatalErrorStream + .add(new AngelFatalError(request: request, error: e, stack: st)); } break; @@ -249,7 +247,18 @@ class Angel extends AngelBase { _afterProcessed.add(request); if (!res.willCloseItself) { - request.response.add(res.buffer.takeBytes()); + for (var finalizer in responseFinalizers) { + await finalizer(req, res); + } + + for (var key in res.headers.keys) { + request.response.headers.set(key, res.headers[key]); + } + + request.response + ..statusCode = res.statusCode + ..cookies.addAll(res.cookies) + ..add(res.buffer.takeBytes()); await request.response.close(); } } catch (e) { diff --git a/pubspec.yaml b/pubspec.yaml index 15d722ad..6c90ba8b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_framework -version: 1.0.0-dev.32 +version: 1.0.0-dev.33 description: Core libraries for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_framework