71
This commit is contained in:
parent
218465a26f
commit
2f5935d5ad
8 changed files with 127 additions and 38 deletions
|
@ -1 +1,2 @@
|
||||||
language: dart
|
language: dart
|
||||||
|
script: ./tool/travis.sh
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
|
{
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
# angel_framework
|
# angel_framework
|
||||||
|
|
||||||
[](https://pub.dartlang.org/packages/angel_framework)
|
[](https://pub.dartlang.org/packages/angel_framework)
|
||||||
[](https://travis-ci.org/angel-dart/framework)
|
[](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
|
```dart
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
/// Various libraries useful for creating highly-extensible servers.
|
/// Various libraries useful for creating highly-extensible servers.
|
||||||
library angel_framework.http;
|
library angel_framework.http;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'server.dart' show ServerGenerator;
|
||||||
export 'package:angel_route/angel_route.dart';
|
export 'package:angel_route/angel_route.dart';
|
||||||
export 'package:body_parser/body_parser.dart' show FileUploadInfo;
|
export 'package:body_parser/body_parser.dart' show FileUploadInfo;
|
||||||
export 'angel_base.dart';
|
export 'angel_base.dart';
|
||||||
|
@ -21,3 +24,13 @@ export 'server.dart';
|
||||||
export 'service.dart';
|
export 'service.dart';
|
||||||
export 'typed_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);
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:mirrors';
|
import 'dart:mirrors';
|
||||||
import 'package:angel_route/angel_route.dart';
|
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_base.dart';
|
||||||
import 'angel_http_exception.dart';
|
import 'angel_http_exception.dart';
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
|
@ -12,7 +15,6 @@ import 'request_context.dart';
|
||||||
import 'response_context.dart';
|
import 'response_context.dart';
|
||||||
import 'routable.dart';
|
import 'routable.dart';
|
||||||
import 'service.dart';
|
import 'service.dart';
|
||||||
export 'package:container/container.dart';
|
|
||||||
|
|
||||||
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
||||||
|
|
||||||
|
@ -37,10 +39,14 @@ class Angel extends AngelBase {
|
||||||
StreamController<Controller> _onController =
|
StreamController<Controller> _onController =
|
||||||
new StreamController<Controller>.broadcast();
|
new StreamController<Controller>.broadcast();
|
||||||
final List<Angel> _children = [];
|
final List<Angel> _children = [];
|
||||||
|
Router _flattened;
|
||||||
|
bool _isProduction;
|
||||||
Angel _parent;
|
Angel _parent;
|
||||||
ServerGenerator _serverGenerator = HttpServer.bind;
|
ServerGenerator _serverGenerator = HttpServer.bind;
|
||||||
|
|
||||||
|
final Map _injections = {};
|
||||||
final Map<dynamic, InjectionRequest> _preContained = {};
|
final Map<dynamic, InjectionRequest> _preContained = {};
|
||||||
|
ResponseSerializer _serializer;
|
||||||
|
|
||||||
/// Determines whether to allow HTTP request method overrides.
|
/// Determines whether to allow HTTP request method overrides.
|
||||||
bool allowMethodOverrides = true;
|
bool allowMethodOverrides = true;
|
||||||
|
@ -61,7 +67,15 @@ class Angel extends AngelBase {
|
||||||
///
|
///
|
||||||
/// The criteria for this is the `ANGEL_ENV` environment variable being set to
|
/// The criteria for this is the `ANGEL_ENV` environment variable being set to
|
||||||
/// `'production'`.
|
/// `'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.
|
/// The function used to bind this instance to an HTTP server.
|
||||||
ServerGenerator get serverGenerator => _serverGenerator;
|
ServerGenerator get serverGenerator => _serverGenerator;
|
||||||
|
@ -130,10 +144,22 @@ class Angel extends AngelBase {
|
||||||
await configure(configurer);
|
await configure(configurer);
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocessRoutes();
|
optimizeForProduction();
|
||||||
return httpServer..listen(handleRequest);
|
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.
|
/// Loads some base dependencies into the service container.
|
||||||
void bootstrapContainer() {
|
void bootstrapContainer() {
|
||||||
if (runtimeType != Angel) container.singleton(this, as: Angel);
|
if (runtimeType != Angel) container.singleton(this, as: Angel);
|
||||||
|
@ -143,20 +169,45 @@ class Angel extends AngelBase {
|
||||||
container.singleton(this);
|
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.
|
/// Shortcut for adding a middleware to inject a key/value pair on every request.
|
||||||
void inject(key, value) {
|
void inject(key, value) {
|
||||||
before.add((RequestContext req, ResponseContext res) async {
|
_injections[key] = value;
|
||||||
req.inject(key, value);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shortcut for adding a middleware to inject a serialize on every request.
|
/// Shortcut for adding a middleware to inject a serialize on every request.
|
||||||
void injectSerializer(ResponseSerializer serializer) {
|
void injectSerializer(ResponseSerializer serializer) {
|
||||||
before.add((RequestContext req, ResponseContext res) async {
|
_serializer = serializer;
|
||||||
res.serializer = serializer;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs some [handler]. Returns `true` if request execution should continue.
|
/// Runs some [handler]. Returns `true` if request execution should continue.
|
||||||
|
@ -227,11 +278,14 @@ class Angel extends AngelBase {
|
||||||
|
|
||||||
Future<RequestContext> createRequestContext(HttpRequest request) {
|
Future<RequestContext> createRequestContext(HttpRequest request) {
|
||||||
_beforeProcessed.add(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 =>
|
Future<ResponseContext> createResponseContext(HttpResponse response) async =>
|
||||||
new ResponseContext(response, this);
|
new ResponseContext(response, this)
|
||||||
|
..serializer = (_serializer ?? god.serialize);
|
||||||
|
|
||||||
/// Handles a single request.
|
/// Handles a single request.
|
||||||
Future handleRequest(HttpRequest request) async {
|
Future handleRequest(HttpRequest request) async {
|
||||||
|
@ -242,7 +296,10 @@ class Angel extends AngelBase {
|
||||||
|
|
||||||
if (requestedUrl.isEmpty) requestedUrl = '/';
|
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);
|
for (var result in resolved) req.params.addAll(result.allParams);
|
||||||
|
|
||||||
|
@ -317,9 +374,13 @@ 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.
|
/// at run-time.
|
||||||
void preprocessRoutes() {
|
/// * [flatten]s the route tree into a linear one.
|
||||||
|
void optimizeForProduction() {
|
||||||
|
if (isProduction == true) {
|
||||||
_add(v) {
|
_add(v) {
|
||||||
if (v is Function && !_preContained.containsKey(v)) {
|
if (v is Function && !_preContained.containsKey(v)) {
|
||||||
_preContained[v] = preInject(v);
|
_preContained[v] = preInject(v);
|
||||||
|
@ -339,7 +400,9 @@ class Angel extends AngelBase {
|
||||||
.forEach(_walk);
|
.forEach(_walk);
|
||||||
}
|
}
|
||||||
|
|
||||||
_walk(this);
|
_walk(_flattened = flatten(this));
|
||||||
|
print('Angel is running in production mode.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a function after injecting from service container.
|
/// Run a function after injecting from service container.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: angel_framework
|
name: angel_framework
|
||||||
version: 1.0.0-dev.70
|
version: 1.0.0-dev.71
|
||||||
description: Core libraries for the Angel framework.
|
description: A high-powered HTTP server with DI, routing and more.
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_framework
|
homepage: https://github.com/angel-dart/angel_framework
|
||||||
environment:
|
environment:
|
||||||
|
@ -9,6 +9,7 @@ dependencies:
|
||||||
angel_route: ^1.0.0-dev
|
angel_route: ^1.0.0-dev
|
||||||
body_parser: ^1.0.0-dev
|
body_parser: ^1.0.0-dev
|
||||||
container: ^0.1.2
|
container: ^0.1.2
|
||||||
|
flatten: ^1.0.0
|
||||||
json_god: ^2.0.0-beta
|
json_god: ^2.0.0-beta
|
||||||
merge_map: ^1.0.0
|
merge_map: ^1.0.0
|
||||||
random_string: ^0.0.1
|
random_string: ^0.0.1
|
||||||
|
|
|
@ -31,6 +31,11 @@ main() {
|
||||||
nested = new Angel(debug: debug);
|
nested = new Angel(debug: debug);
|
||||||
todos = new Angel(debug: debug);
|
todos = new Angel(debug: debug);
|
||||||
|
|
||||||
|
// Lazy-parse in production
|
||||||
|
[app, nested, todos].forEach((Angel app) {
|
||||||
|
app.lazyParseBodies = app.isProduction;
|
||||||
|
});
|
||||||
|
|
||||||
app
|
app
|
||||||
..registerMiddleware('interceptor', (req, res) async {
|
..registerMiddleware('interceptor', (req, res) async {
|
||||||
res.write('Middleware');
|
res.write('Middleware');
|
||||||
|
|
3
tool/travis.sh
Normal file
3
tool/travis.sh
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
pub run test
|
||||||
|
ANGEL_ENV=production pub run test
|
Loading…
Reference in a new issue