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 @@
1481379127310
-
+
+ 1481389821679
+
+
+
+ 1481389821679
+
+
@@ -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