diff --git a/.idea/workspace.xml b/.idea/workspace.xml index aaad1c17..f012b5cd 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,6 +4,8 @@ + + @@ -18,7 +20,7 @@ + + + + + @@ -464,7 +487,8 @@ - + + 1481237183504 @@ -564,7 +588,14 @@ - @@ -600,7 +631,7 @@ - @@ -615,23 +646,23 @@ - + - + - + + - + - @@ -653,46 +684,18 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -850,20 +853,6 @@ - - - - - - - - - - - - - - @@ -871,20 +860,6 @@ - - - - - - - - - - - - - - @@ -892,13 +867,6 @@ - - - - - - - @@ -934,24 +902,9 @@ - - - - - - - - - - - - - - - - + @@ -978,7 +931,7 @@ - + @@ -986,22 +939,6 @@ - - - - - - - - - - - - - - - - @@ -1010,10 +947,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1021,7 +1073,7 @@ - + @@ -1029,10 +1081,10 @@ - - + + - + diff --git a/README.md b/README.md index 99210b04..5547bf0a 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # angel_framework -[![pub 1.0.3+1](https://img.shields.io/badge/pub-1.0.3+1-brightgreen.svg)](https://pub.dartlang.org/packages/angel_framework) +[![Pub](https://img.shields.io/pub/v/angel_framework.svg)](https://pub.dartlang.org/packages/angel_framework) [![build status](https://travis-ci.org/angel-dart/framework.svg)](https://travis-ci.org/angel-dart/framework) A high-powered HTTP server with support for dependency injection, sophisticated routing and more. +This is the core of the [Angel](https://github.com/angel-dart/angel) framework. +To build real-world applications, please see the [homepage](https://angel-dart.github.io). + ```dart import 'package:angel_framework/angel_framework.dart'; diff --git a/lib/src/http/metadata.dart b/lib/src/http/metadata.dart index 194b0306..48676510 100644 --- a/lib/src/http/metadata.dart +++ b/lib/src/http/metadata.dart @@ -14,6 +14,8 @@ class Hooked { const Hooked(); } +const Hooked hooked = const Hooked(); + /// Attaches hooks to a [HookedService]. class Hooks { final List before; diff --git a/lib/src/http/response_context.dart b/lib/src/http/response_context.dart index 9c49ca43..de4e318c 100644 --- a/lib/src/http/response_context.dart +++ b/lib/src/http/response_context.dart @@ -11,7 +11,7 @@ import 'server.dart' show Angel; import 'controller.dart'; final RegExp _contentType = -new RegExp(r'([^/\n]+)\/\s*([^;\n]+)\s*(;\s*charset=([^$;\n]+))?'); + new RegExp(r'([^/\n]+)\/\s*([^;\n]+)\s*(;\s*charset=([^$;\n]+))?'); final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)'); @@ -19,11 +19,10 @@ final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)'); typedef String ResponseSerializer(data); /// A convenience wrapper around an outgoing HTTP request. -class ResponseContext extends Extensible { +class ResponseContext extends Extensible implements StringSink { final _LockableBytesBuilder _buffer = new _LockableBytesBuilder(); final Map _headers = {HttpHeaders.SERVER: 'angel'}; - bool _isOpen = true, - _isClosed = false; + bool _isOpen = true, _isClosed = false; int _statusCode = 200; /// The [Angel] instance that is sending a response. @@ -124,7 +123,7 @@ class ResponseContext extends Extensible { if (!_isOpen) throw _closed(); headers["Content-Disposition"] = - 'attachment; filename="${filename ?? file.path}"'; + 'attachment; filename="${filename ?? file.path}"'; headers[HttpHeaders.CONTENT_TYPE] = lookupMimeType(file.path); headers[HttpHeaders.CONTENT_LENGTH] = file.lengthSync().toString(); buffer.add(await file.readAsBytes()); @@ -203,7 +202,7 @@ class ResponseContext extends Extensible { headers ..[HttpHeaders.CONTENT_TYPE] = ContentType.HTML.toString() ..[HttpHeaders.LOCATION] = - url is String ? url : app.navigate(url, absolute: absolute); + url is String ? url : app.navigate(url, absolute: absolute); statusCode = code ?? 302; write(''' @@ -261,7 +260,7 @@ class ResponseContext extends Extensible { "Controller redirects must take the form of 'Controller@action'. You gave: $action"); Controller controller = - app.controller(split[0].replaceAll(_straySlashes, '')); + app.controller(split[0].replaceAll(_straySlashes, '')); if (controller == null) throw new Exception("Could not find a controller named '${split[0]}'"); @@ -273,11 +272,7 @@ class ResponseContext extends Extensible { "Controller '${split[0]}' does not contain any action named '${split[1]}'"); final head = - controller - .findExpose() - .path - .toString() - .replaceAll(_straySlashes, ''); + controller.findExpose().path.toString().replaceAll(_straySlashes, ''); final tail = matched.makeUri(params).replaceAll(_straySlashes, ''); redirect('$head/$tail'.replaceAll(_straySlashes, ''), code: code); @@ -313,9 +308,9 @@ class ResponseContext extends Extensible { /// You can optionally transform the file stream with a [codec]. Future streamFile(File file, {int chunkSize, - int sleepMs: 0, - bool resumable: true, - Codec, List> codec}) async { + int sleepMs: 0, + bool resumable: true, + Codec, List> codec}) async { if (_isClosed) throw _closed(); headers[HttpHeaders.CONTENT_TYPE] = lookupMimeType(file.path); @@ -339,6 +334,25 @@ class ResponseContext extends Extensible { buffer.add(encoding.encode(value.toString())); } } + + @override + void writeCharCode(int charCode) { + if (_isClosed) + throw _closed(); + else + buffer.addByte(charCode); + } + + @override + void writeln([Object obj = ""]) { + write(obj.toString()); + write('\r\n'); + } + + @override + void writeAll(Iterable objects, [String separator = ""]) { + write(objects.join(separator)); + } } abstract class _LockableBytesBuilder extends BytesBuilder { diff --git a/lib/src/http/server.dart b/lib/src/http/server.dart index 5e2b7f14..836a40c8 100644 --- a/lib/src/http/server.dart +++ b/lib/src/http/server.dart @@ -309,6 +309,8 @@ class Angel extends AngelBase { contentType: res.headers[HttpHeaders.CONTENT_TYPE] ?? ContentType.JSON.mimeType); return false; + } else if (result is Stream) { + return await executeHandler(await result.toList(), req, res); } else return res.isOpen; } @@ -336,6 +338,22 @@ class Angel extends AngelBase { return parent != null ? parent.findProperty(key) : null; } + _handleAngelHttpException(AngelHttpException e, StackTrace st, + RequestContext req, ResponseContext res) async { + res.statusCode = e.statusCode; + List accept = request.headers[HttpHeaders.ACCEPT] ?? ['*/*']; + if (accept.isEmpty || + accept.contains('*/*') || + accept.contains(ContentType.JSON.mimeType) || + accept.contains("application/javascript")) { + res.serialize(e.toMap(), + contentType: res.headers[HttpHeaders.CONTENT_TYPE] ?? + ContentType.JSON.mimeType); + } else { + await errorHandler(e, req, res); + } + } + /// Handles a single request. Future handleRequest(HttpRequest request) async { try { @@ -377,42 +395,15 @@ class Angel extends AngelBase { // _printDebug( // 'Handler completed successfully, did not terminate response: $handler'); } - } catch (e, st) { - // _printDebug('Caught error in handler $handler: $e'); - // _printDebug(st); - - if (e is AngelHttpException) { - // Special handling for AngelHttpExceptions :) - try { - res.statusCode = e.statusCode; - List accept = - request.headers[HttpHeaders.ACCEPT] ?? ['*/*']; - if (accept.isEmpty || - accept.contains('*/*') || - accept.contains(ContentType.JSON.mimeType) || - accept.contains("application/javascript")) { - res.serialize(e.toMap(), - contentType: res.headers[HttpHeaders.CONTENT_TYPE] ?? - ContentType.JSON.mimeType); - } else { - await errorHandler(e, req, res); - } - // _finalizeResponse(request, res); - } catch (e, st) { - _fatalErrorStream.add( - new AngelFatalError(request: request, error: e, stack: st)); - } - } else { - _fatalErrorStream.add( - new AngelFatalError(request: request, error: e, stack: st)); - } - - break; + } on AngelHttpException catch (e, st) { + return await _handleAngelHttpException(e, st, req, res); } } try { await sendResponse(request, req, res); + } on AngelHttpException catch (e, st) { + return await _handleAngelHttpException(e, st, req, res); } catch (e, st) { _fatalErrorStream .add(new AngelFatalError(request: request, error: e, stack: st)); diff --git a/pubspec.yaml b/pubspec.yaml index 76c3009b..9e8946f2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_framework -version: 1.0.3+1 +version: 1.0.4 description: A high-powered HTTP server with DI, routing and more. author: Tobe O homepage: https://github.com/angel-dart/angel_framework