This commit is contained in:
thosakwe 2017-03-31 21:00:24 -04:00
parent 218465a26f
commit 2f5935d5ad
8 changed files with 127 additions and 38 deletions

View file

@ -1 +1,2 @@
language: dart
language: dart
script: ./tool/travis.sh

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
// Place your settings in this file to overwrite default and user settings.
{
}

View file

@ -1,9 +1,9 @@
# angel_framework
[![pub 1.0.0-dev.70](https://img.shields.io/badge/pub-1.0.0--dev.70-red.svg)](https://pub.dartlang.org/packages/angel_framework)
[![pub 1.0.0-dev.71](https://img.shields.io/badge/pub-1.0.0--dev.71-red.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)
Core libraries for the Angel Framework.
A high-powered HTTP server with support for dependency injection, sophisticated routing and more.
```dart
import 'package:angel_framework/angel_framework.dart';

View file

@ -1,6 +1,9 @@
/// Various libraries useful for creating highly-extensible servers.
library angel_framework.http;
import 'dart:async';
import 'dart:io';
import 'server.dart' show ServerGenerator;
export 'package:angel_route/angel_route.dart';
export 'package:body_parser/body_parser.dart' show FileUploadInfo;
export 'angel_base.dart';
@ -21,3 +24,13 @@ export 'server.dart';
export 'service.dart';
export 'typed_service.dart';
/// Boots a shared server instance. Use this if launching multiple isolates
Future<HttpServer> startShared(InternetAddress address, int port) => HttpServer
.bind(address ?? InternetAddress.LOOPBACK_IP_V4, port ?? 0, shared: true);
/// Boots a secure shared server instance. Use this if launching multiple isolates
ServerGenerator startSharedSecure(SecurityContext securityContext) {
return (InternetAddress address, int port) => HttpServer.bindSecure(
address ?? InternetAddress.LOOPBACK_IP_V4, port ?? 0, securityContext,
shared: true);
}

View file

@ -4,6 +4,9 @@ import 'dart:async';
import 'dart:io';
import 'dart:mirrors';
import 'package:angel_route/angel_route.dart';
export 'package:container/container.dart';
import 'package:flatten/flatten.dart';
import 'package:json_god/json_god.dart' as god;
import 'angel_base.dart';
import 'angel_http_exception.dart';
import 'controller.dart';
@ -12,7 +15,6 @@ import 'request_context.dart';
import 'response_context.dart';
import 'routable.dart';
import 'service.dart';
export 'package:container/container.dart';
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
@ -37,10 +39,14 @@ class Angel extends AngelBase {
StreamController<Controller> _onController =
new StreamController<Controller>.broadcast();
final List<Angel> _children = [];
Router _flattened;
bool _isProduction;
Angel _parent;
ServerGenerator _serverGenerator = HttpServer.bind;
final Map _injections = {};
final Map<dynamic, InjectionRequest> _preContained = {};
ResponseSerializer _serializer;
/// Determines whether to allow HTTP request method overrides.
bool allowMethodOverrides = true;
@ -61,7 +67,15 @@ class Angel extends AngelBase {
///
/// The criteria for this is the `ANGEL_ENV` environment variable being set to
/// `'production'`.
bool get isProduction => Platform.environment['ANGEL_ENV'] == 'production';
///
/// This value is memoized the first time you call it, so do not change environment
/// configuration at runtime!
bool get isProduction {
if (_isProduction != null)
return _isProduction;
else
return _isProduction = Platform.environment['ANGEL_ENV'] == 'production';
}
/// The function used to bind this instance to an HTTP server.
ServerGenerator get serverGenerator => _serverGenerator;
@ -130,10 +144,22 @@ class Angel extends AngelBase {
await configure(configurer);
}
preprocessRoutes();
optimizeForProduction();
return httpServer..listen(handleRequest);
}
@override
Route addRoute(String method, String path, Object handler,
{List middleware: const []}) {
if (_flattened != null) {
print(
'WARNING: You added a route ($method $path) to the router, after it has been optimized.');
print('This route will be ignored, and no requests will ever reach it.');
}
return super.addRoute(method, path, handler, middleware: middleware ?? []);
}
/// Loads some base dependencies into the service container.
void bootstrapContainer() {
if (runtimeType != Angel) container.singleton(this, as: Angel);
@ -143,20 +169,45 @@ class Angel extends AngelBase {
container.singleton(this);
}
@override
void dumpTree(
{callback(String tree),
String header: 'Dumping route tree:',
String tab: ' ',
bool showMatchers: false}) {
if (isProduction) {
if (_flattened == null) _flattened = flatten(this);
_flattened.dumpTree(
callback: callback,
header: header?.isNotEmpty == true
? header
: (isProduction
? 'Dumping flattened route tree:'
: 'Dumping route tree:'),
tab: tab ?? ' ',
showMatchers: showMatchers == true);
} else {
super.dumpTree(
callback: callback,
header: header?.isNotEmpty == true
? header
: (isProduction
? 'Dumping flattened route tree:'
: 'Dumping route tree:'),
tab: tab ?? ' ',
showMatchers: showMatchers == true);
}
}
/// Shortcut for adding a middleware to inject a key/value pair on every request.
void inject(key, value) {
before.add((RequestContext req, ResponseContext res) async {
req.inject(key, value);
return true;
});
_injections[key] = value;
}
/// Shortcut for adding a middleware to inject a serialize on every request.
void injectSerializer(ResponseSerializer serializer) {
before.add((RequestContext req, ResponseContext res) async {
res.serializer = serializer;
return true;
});
_serializer = serializer;
}
/// Runs some [handler]. Returns `true` if request execution should continue.
@ -227,11 +278,14 @@ class Angel extends AngelBase {
Future<RequestContext> createRequestContext(HttpRequest request) {
_beforeProcessed.add(request);
return RequestContext.from(request, this);
return RequestContext.from(request, this).then((req) {
return req..injections.addAll(_injections ?? {});
});
}
Future<ResponseContext> createResponseContext(HttpResponse response) async =>
new ResponseContext(response, this);
new ResponseContext(response, this)
..serializer = (_serializer ?? god.serialize);
/// Handles a single request.
Future handleRequest(HttpRequest request) async {
@ -242,7 +296,10 @@ class Angel extends AngelBase {
if (requestedUrl.isEmpty) requestedUrl = '/';
var resolved = resolveAll(requestedUrl, requestedUrl, method: req.method);
Router r =
isProduction ? (_flattened ?? (_flattened = flatten(this))) : this;
var resolved =
r.resolveAll(requestedUrl, requestedUrl, method: req.method);
for (var result in resolved) req.params.addAll(result.allParams);
@ -317,29 +374,35 @@ class Angel extends AngelBase {
}
}
/// Preprocesses all routes, and eliminates the burden of reflecting handlers
/// Runs several optimizations, *if* [isProduction] is `true`.
///
/// * Preprocesses all dependency injection, and eliminates the burden of reflecting handlers
/// at run-time.
void preprocessRoutes() {
_add(v) {
if (v is Function && !_preContained.containsKey(v)) {
_preContained[v] = preInject(v);
}
}
void _walk(Router router) {
if (router is Angel) {
router..before.forEach(_add)..after.forEach(_add);
/// * [flatten]s the route tree into a linear one.
void optimizeForProduction() {
if (isProduction == true) {
_add(v) {
if (v is Function && !_preContained.containsKey(v)) {
_preContained[v] = preInject(v);
}
}
router.requestMiddleware.forEach((k, v) => _add(v));
router.middleware.forEach(_add);
router.routes
.where((r) => r is SymlinkRoute)
.map((SymlinkRoute r) => r.router)
.forEach(_walk);
}
void _walk(Router router) {
if (router is Angel) {
router..before.forEach(_add)..after.forEach(_add);
}
_walk(this);
router.requestMiddleware.forEach((k, v) => _add(v));
router.middleware.forEach(_add);
router.routes
.where((r) => r is SymlinkRoute)
.map((SymlinkRoute r) => r.router)
.forEach(_walk);
}
_walk(_flattened = flatten(this));
print('Angel is running in production mode.');
}
}
/// Run a function after injecting from service container.

View file

@ -1,6 +1,6 @@
name: angel_framework
version: 1.0.0-dev.70
description: Core libraries for the Angel framework.
version: 1.0.0-dev.71
description: A high-powered HTTP server with DI, routing and more.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_framework
environment:
@ -9,6 +9,7 @@ dependencies:
angel_route: ^1.0.0-dev
body_parser: ^1.0.0-dev
container: ^0.1.2
flatten: ^1.0.0
json_god: ^2.0.0-beta
merge_map: ^1.0.0
random_string: ^0.0.1

View file

@ -31,6 +31,11 @@ main() {
nested = new Angel(debug: debug);
todos = new Angel(debug: debug);
// Lazy-parse in production
[app, nested, todos].forEach((Angel app) {
app.lazyParseBodies = app.isProduction;
});
app
..registerMiddleware('interceptor', (req, res) async {
res.write('Middleware');

3
tool/travis.sh Normal file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
pub run test
ANGEL_ENV=production pub run test