2016-09-15 19:53:01 +00:00
|
|
|
library angel_framework.http.server;
|
|
|
|
|
|
|
|
import 'dart:async';
|
|
|
|
import 'dart:io';
|
|
|
|
import 'dart:math' show Random;
|
2016-09-17 16:12:25 +00:00
|
|
|
import 'dart:mirrors';
|
2016-11-28 00:49:27 +00:00
|
|
|
import 'package:angel_route/angel_route.dart';
|
2016-09-15 19:53:01 +00:00
|
|
|
import 'angel_base.dart';
|
|
|
|
import 'angel_http_exception.dart';
|
|
|
|
import 'controller.dart';
|
2016-12-10 14:12:07 +00:00
|
|
|
import 'fatal_error.dart';
|
2016-09-15 19:53:01 +00:00
|
|
|
import 'request_context.dart';
|
|
|
|
import 'response_context.dart';
|
|
|
|
import 'routable.dart';
|
|
|
|
import 'service.dart';
|
2016-09-17 16:12:25 +00:00
|
|
|
export 'package:container/container.dart';
|
2016-02-28 13:11:17 +00:00
|
|
|
|
2016-10-22 20:41:36 +00:00
|
|
|
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
|
|
|
|
2016-04-18 03:27:23 +00:00
|
|
|
/// A function that binds an [Angel] server to an Internet address and port.
|
2016-02-28 13:11:17 +00:00
|
|
|
typedef Future<HttpServer> ServerGenerator(InternetAddress address, int port);
|
|
|
|
|
2016-05-02 22:28:14 +00:00
|
|
|
/// Handles an [AngelHttpException].
|
2016-09-17 16:12:25 +00:00
|
|
|
typedef Future AngelErrorHandler(
|
|
|
|
AngelHttpException err, RequestContext req, ResponseContext res);
|
2016-05-02 22:28:14 +00:00
|
|
|
|
2016-09-15 19:53:01 +00:00
|
|
|
/// A function that configures an [AngelBase] server in some way.
|
|
|
|
typedef Future AngelConfigurer(AngelBase app);
|
2016-04-18 03:27:23 +00:00
|
|
|
|
2016-02-28 13:11:17 +00:00
|
|
|
/// A powerful real-time/REST/MVC server class.
|
2016-09-15 19:53:01 +00:00
|
|
|
class Angel extends AngelBase {
|
2016-12-10 14:12:07 +00:00
|
|
|
StreamController<HttpRequest> _afterProcessed =
|
|
|
|
new StreamController<HttpRequest>.broadcast();
|
|
|
|
StreamController<HttpRequest> _beforeProcessed =
|
|
|
|
new StreamController<HttpRequest>.broadcast();
|
|
|
|
StreamController<AngelFatalError> _fatalErrorStream =
|
|
|
|
new StreamController<AngelFatalError>.broadcast();
|
|
|
|
StreamController<Controller> _onController =
|
|
|
|
new StreamController<Controller>.broadcast();
|
2016-12-21 18:18:26 +00:00
|
|
|
final List<Angel> _children = [];
|
|
|
|
Angel _parent;
|
2016-10-22 20:41:36 +00:00
|
|
|
final Random _rand = new Random.secure();
|
2016-11-24 07:12:53 +00:00
|
|
|
ServerGenerator _serverGenerator = HttpServer.bind;
|
2016-09-17 16:12:25 +00:00
|
|
|
|
2016-12-31 01:46:41 +00:00
|
|
|
final Map<dynamic, InjectionRequest> _preContained = {};
|
|
|
|
|
2016-09-17 16:12:25 +00:00
|
|
|
/// Fired after a request is processed. Always runs.
|
|
|
|
Stream<HttpRequest> get afterProcessed => _afterProcessed.stream;
|
2016-07-04 18:06:31 +00:00
|
|
|
|
2016-07-05 22:11:54 +00:00
|
|
|
/// Fired before a request is processed. Always runs.
|
2016-07-04 18:06:31 +00:00
|
|
|
Stream<HttpRequest> get beforeProcessed => _beforeProcessed.stream;
|
2016-07-05 22:11:54 +00:00
|
|
|
|
2016-12-21 18:18:26 +00:00
|
|
|
/// All child application mounted on this instance.
|
|
|
|
List<Angel> get children => new List<Angel>.unmodifiable(_children);
|
|
|
|
|
2016-09-19 06:52:21 +00:00
|
|
|
/// Fired on fatal errors.
|
2016-12-10 14:12:07 +00:00
|
|
|
Stream<AngelFatalError> get fatalErrorStream => _fatalErrorStream.stream;
|
2016-09-19 06:52:21 +00:00
|
|
|
|
2016-12-21 18:18:26 +00:00
|
|
|
/// Indicates whether the application is running in a production environment.
|
|
|
|
///
|
|
|
|
/// The criteria for this is the `ANGEL_ENV` environment variable being set to
|
|
|
|
/// `'production'`.
|
|
|
|
bool get isProduction => Platform.environment['ANGEL_ENV'] == 'production';
|
|
|
|
|
2016-07-05 22:11:54 +00:00
|
|
|
/// Fired whenever a controller is added to this instance.
|
|
|
|
///
|
|
|
|
/// **NOTE**: This is a broadcast stream.
|
|
|
|
Stream<Controller> get onController => _onController.stream;
|
|
|
|
|
2016-12-21 18:18:26 +00:00
|
|
|
/// Returns the parent instance of this application, if any.
|
|
|
|
Angel get parent => _parent;
|
|
|
|
|
2016-12-19 01:38:23 +00:00
|
|
|
/// Always run before responses are sent.
|
|
|
|
///
|
|
|
|
/// These will only not run if an [AngelFatalError] occurs.
|
|
|
|
final List<RequestHandler> responseFinalizers = [];
|
|
|
|
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
/// Default error handler, show HTML error page
|
2016-09-17 16:12:25 +00:00
|
|
|
AngelErrorHandler _errorHandler =
|
|
|
|
(AngelHttpException e, req, ResponseContext res) {
|
2016-12-21 20:27:07 +00:00
|
|
|
res.headers[HttpHeaders.CONTENT_TYPE] = ContentType.HTML.toString();
|
2016-12-19 01:38:23 +00:00
|
|
|
res.statusCode = e.statusCode;
|
2016-07-03 22:23:55 +00:00
|
|
|
res.write("<!DOCTYPE html><html><head><title>${e.message}</title>");
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
res.write("</head><body><h1>${e.message}</h1><ul>");
|
2016-12-31 01:46:41 +00:00
|
|
|
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
for (String error in e.errors) {
|
|
|
|
res.write("<li>$error</li>");
|
|
|
|
}
|
2016-12-31 01:46:41 +00:00
|
|
|
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
res.write("</ul></body></html>");
|
|
|
|
res.end();
|
|
|
|
};
|
|
|
|
|
2016-09-21 05:10:21 +00:00
|
|
|
/// The handler currently configured to run on [AngelHttpException]s.
|
|
|
|
AngelErrorHandler get errorHandler => _errorHandler;
|
|
|
|
|
2016-05-02 22:28:14 +00:00
|
|
|
/// [RequestMiddleware] to be run before all requests.
|
2016-12-21 18:18:26 +00:00
|
|
|
final List before = [];
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
|
2016-05-02 22:28:14 +00:00
|
|
|
/// [RequestMiddleware] to be run after all requests.
|
2016-12-21 18:18:26 +00:00
|
|
|
final List after = [];
|
2016-04-18 03:27:23 +00:00
|
|
|
|
2016-06-21 22:56:04 +00:00
|
|
|
/// The native HttpServer running this instancce.
|
2016-04-18 03:27:23 +00:00
|
|
|
HttpServer httpServer;
|
|
|
|
|
2016-10-22 20:41:36 +00:00
|
|
|
/// Handles a server error.
|
2016-12-10 14:12:07 +00:00
|
|
|
_onError(e, [StackTrace st]) {
|
|
|
|
_fatalErrorStream.add(new AngelFatalError(error: e, stack: st));
|
2016-10-22 20:41:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void _printDebug(x) {
|
|
|
|
if (debug) print(x);
|
|
|
|
}
|
|
|
|
|
|
|
|
String _randomString(int length) {
|
|
|
|
var codeUnits = new List.generate(length, (index) {
|
|
|
|
return _rand.nextInt(33) + 89;
|
|
|
|
});
|
|
|
|
|
|
|
|
return new String.fromCharCodes(codeUnits);
|
|
|
|
}
|
|
|
|
|
2016-06-21 22:56:04 +00:00
|
|
|
/// Starts the server.
|
|
|
|
///
|
|
|
|
/// Returns false on failure; otherwise, returns the HttpServer.
|
2016-09-17 16:12:25 +00:00
|
|
|
Future<HttpServer> startServer([InternetAddress address, int port]) async {
|
2016-11-22 04:58:53 +00:00
|
|
|
final host = address ?? InternetAddress.LOOPBACK_IP_V4;
|
2016-11-23 09:10:47 +00:00
|
|
|
this.httpServer = await _serverGenerator(host, port ?? 0);
|
|
|
|
return httpServer..listen(handleRequest);
|
2016-07-04 18:06:31 +00:00
|
|
|
}
|
|
|
|
|
2016-09-17 16:12:25 +00:00
|
|
|
/// Loads some base dependencies into the service container.
|
|
|
|
void bootstrapContainer() {
|
|
|
|
container.singleton(this, as: AngelBase);
|
2016-11-23 19:50:17 +00:00
|
|
|
container.singleton(this, as: Routable);
|
|
|
|
container.singleton(this, as: Router);
|
2016-09-17 16:12:25 +00:00
|
|
|
container.singleton(this);
|
|
|
|
|
2016-09-18 02:59:06 +00:00
|
|
|
if (runtimeType != Angel) container.singleton(this, as: Angel);
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
2016-10-22 20:41:36 +00:00
|
|
|
Future<bool> executeHandler(
|
2016-09-17 16:12:25 +00:00
|
|
|
handler, RequestContext req, ResponseContext res) async {
|
2016-05-02 22:28:14 +00:00
|
|
|
if (handler is RequestMiddleware) {
|
2016-04-21 20:37:02 +00:00
|
|
|
var result = await handler(req, res);
|
2016-09-17 16:12:25 +00:00
|
|
|
|
2016-04-21 20:37:02 +00:00
|
|
|
if (result is bool)
|
|
|
|
return result == true;
|
|
|
|
else if (result != null) {
|
2016-12-31 01:46:41 +00:00
|
|
|
res.serialize(result);
|
2016-04-21 20:37:02 +00:00
|
|
|
return false;
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
} else
|
|
|
|
return res.isOpen;
|
2016-04-18 03:27:23 +00:00
|
|
|
}
|
|
|
|
|
2016-04-21 20:37:02 +00:00
|
|
|
if (handler is RequestHandler) {
|
2016-04-18 03:27:23 +00:00
|
|
|
await handler(req, res);
|
|
|
|
return res.isOpen;
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (handler is Future) {
|
|
|
|
var result = await handler;
|
|
|
|
if (result is bool)
|
|
|
|
return result == true;
|
|
|
|
else if (result != null) {
|
2016-12-31 01:46:41 +00:00
|
|
|
res.serialize(result);
|
2016-09-17 16:12:25 +00:00
|
|
|
return false;
|
|
|
|
} else
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handler is Function) {
|
|
|
|
var result = await runContained(handler, req, res);
|
2016-04-21 20:37:02 +00:00
|
|
|
if (result is bool)
|
|
|
|
return result == true;
|
|
|
|
else if (result != null) {
|
2016-12-31 01:46:41 +00:00
|
|
|
res.serialize(result);
|
2016-04-21 20:37:02 +00:00
|
|
|
return false;
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
} else
|
|
|
|
return true;
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (requestMiddleware.containsKey(handler)) {
|
2016-10-22 20:41:36 +00:00
|
|
|
return await executeHandler(requestMiddleware[handler], req, res);
|
2016-04-18 03:27:23 +00:00
|
|
|
}
|
2016-09-17 16:12:25 +00:00
|
|
|
|
2016-12-31 01:46:41 +00:00
|
|
|
res.serialize(handler);
|
2016-09-17 16:12:25 +00:00
|
|
|
return false;
|
2016-04-18 03:27:23 +00:00
|
|
|
}
|
|
|
|
|
2016-10-22 20:41:36 +00:00
|
|
|
Future handleRequest(HttpRequest request) async {
|
|
|
|
_beforeProcessed.add(request);
|
|
|
|
|
|
|
|
final req = await RequestContext.from(request, this);
|
|
|
|
final res = new ResponseContext(request.response, this);
|
2016-11-23 19:50:17 +00:00
|
|
|
String requestedUrl = request.uri.path.replaceAll(_straySlashes, '');
|
2016-10-22 20:41:36 +00:00
|
|
|
|
|
|
|
if (requestedUrl.isEmpty) requestedUrl = '/';
|
|
|
|
|
2016-11-28 00:49:27 +00:00
|
|
|
final resolved =
|
|
|
|
resolveAll(requestedUrl, requestedUrl, method: request.method);
|
2016-10-22 20:41:36 +00:00
|
|
|
|
2016-11-28 00:49:27 +00:00
|
|
|
for (final result in resolved) req.params.addAll(result.allParams);
|
2016-11-23 19:50:17 +00:00
|
|
|
|
|
|
|
if (resolved.isNotEmpty) {
|
2016-11-28 00:49:27 +00:00
|
|
|
final route = resolved.first.route;
|
|
|
|
req.inject(Match, route.match(requestedUrl));
|
2016-11-23 19:50:17 +00:00
|
|
|
}
|
2016-10-22 20:41:36 +00:00
|
|
|
|
2016-11-28 00:49:27 +00:00
|
|
|
final m = new MiddlewarePipeline(resolved);
|
2016-12-19 01:38:23 +00:00
|
|
|
req.inject(MiddlewarePipeline, m);
|
|
|
|
|
2016-11-28 00:49:27 +00:00
|
|
|
final pipeline = []..addAll(before)..addAll(m.handlers)..addAll(after);
|
2016-10-22 20:41:36 +00:00
|
|
|
|
2016-11-23 19:50:17 +00:00
|
|
|
_printDebug('Handler sequence on $requestedUrl: $pipeline');
|
|
|
|
|
|
|
|
for (final handler in pipeline) {
|
2016-10-22 20:41:36 +00:00
|
|
|
try {
|
|
|
|
_printDebug('Executing handler: $handler');
|
|
|
|
final result = await executeHandler(handler, req, res);
|
|
|
|
_printDebug('Result: $result');
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
_printDebug('Last executed handler: $handler');
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
_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 {
|
2016-12-19 01:38:23 +00:00
|
|
|
res.statusCode = e.statusCode;
|
2016-12-31 01:46:41 +00:00
|
|
|
List<String> accept =
|
|
|
|
request.headers[HttpHeaders.ACCEPT] ?? ['*/*'];
|
|
|
|
if (accept.isEmpty ||
|
|
|
|
accept.contains('*/*') ||
|
2016-10-22 20:41:36 +00:00
|
|
|
accept.contains(ContentType.JSON.mimeType) ||
|
|
|
|
accept.contains("application/javascript")) {
|
2016-12-31 01:46:41 +00:00
|
|
|
res.serialize(e.toMap());
|
2016-10-22 20:41:36 +00:00
|
|
|
} else {
|
|
|
|
await _errorHandler(e, req, res);
|
|
|
|
}
|
2016-12-19 01:38:23 +00:00
|
|
|
// _finalizeResponse(request, res);
|
2016-12-10 14:12:07 +00:00
|
|
|
} catch (e, st) {
|
2016-12-19 01:38:23 +00:00
|
|
|
_fatalErrorStream.add(
|
|
|
|
new AngelFatalError(request: request, error: e, stack: st));
|
2016-10-22 20:41:36 +00:00
|
|
|
}
|
2016-12-10 17:10:21 +00:00
|
|
|
} else {
|
2016-12-19 01:38:23 +00:00
|
|
|
_fatalErrorStream
|
|
|
|
.add(new AngelFatalError(request: request, error: e, stack: st));
|
2016-10-22 20:41:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-01 01:42:52 +00:00
|
|
|
try {
|
2016-10-07 21:39:41 +00:00
|
|
|
_afterProcessed.add(request);
|
2016-10-22 20:41:36 +00:00
|
|
|
|
2016-05-01 01:42:52 +00:00
|
|
|
if (!res.willCloseItself) {
|
2016-12-19 01:38:23 +00:00
|
|
|
for (var finalizer in responseFinalizers) {
|
|
|
|
await finalizer(req, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var key in res.headers.keys) {
|
|
|
|
request.response.headers.set(key, res.headers[key]);
|
|
|
|
}
|
2016-12-21 18:18:26 +00:00
|
|
|
|
2016-12-19 04:32:36 +00:00
|
|
|
request.response.headers.chunkedTransferEncoding = res.chunked ?? true;
|
2016-12-19 01:38:23 +00:00
|
|
|
|
|
|
|
request.response
|
|
|
|
..statusCode = res.statusCode
|
|
|
|
..cookies.addAll(res.cookies)
|
|
|
|
..add(res.buffer.takeBytes());
|
2016-05-01 01:42:52 +00:00
|
|
|
await request.response.close();
|
|
|
|
}
|
|
|
|
} catch (e) {
|
2016-09-17 16:12:25 +00:00
|
|
|
failSilently(request, res);
|
2016-04-22 01:42:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-31 01:46:41 +00:00
|
|
|
/// Run a function after injecting from service container.
|
|
|
|
/// If this function has been reflected before, then
|
|
|
|
/// the execution will be faster, as the injection requirements were stored beforehand.
|
|
|
|
Future runContained(
|
|
|
|
Function handler, RequestContext req, ResponseContext res) {
|
|
|
|
if (_preContained.containsKey(handler)) {
|
|
|
|
return handleContained(handler, _preContained[handler])(req, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
return runReflected(handler, req, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Runs with DI, and *always* reflects. Prefer [runContained].
|
|
|
|
Future runReflected(
|
|
|
|
Function handler, RequestContext req, ResponseContext res) async {
|
2016-09-17 16:12:25 +00:00
|
|
|
ClosureMirror closureMirror = reflect(handler);
|
2016-12-31 01:46:41 +00:00
|
|
|
var injection = new InjectionRequest();
|
|
|
|
|
|
|
|
// Load parameters
|
|
|
|
for (var parameter in closureMirror.function.parameters) {
|
|
|
|
var name = MirrorSystem.getName(parameter.simpleName);
|
|
|
|
var type = parameter.type.reflectedType;
|
|
|
|
|
|
|
|
if (type == RequestContext || type == ResponseContext) {
|
|
|
|
injection.required.add(type);
|
|
|
|
} else if (name == 'req') {
|
|
|
|
injection.required.add(RequestContext);
|
|
|
|
} else if (name == 'res') {
|
|
|
|
injection.required.add(ResponseContext);
|
|
|
|
} else if (type == dynamic) {
|
|
|
|
injection.required.add(name);
|
|
|
|
} else {
|
|
|
|
injection.required.add([name, type]);
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-31 01:46:41 +00:00
|
|
|
_preContained[handler] = injection;
|
|
|
|
return handleContained(handler, injection);
|
|
|
|
// return await closureMirror.apply(args).reflectee;
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
2016-04-18 03:27:23 +00:00
|
|
|
/// Applies an [AngelConfigurer] to this instance.
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
Future configure(AngelConfigurer configurer) async {
|
|
|
|
await configurer(this);
|
2016-07-05 22:11:54 +00:00
|
|
|
|
2016-11-28 00:49:27 +00:00
|
|
|
if (configurer is Controller)
|
|
|
|
_onController.add(controllers[configurer.exposeDecl.path] = configurer);
|
2016-02-28 13:11:17 +00:00
|
|
|
}
|
|
|
|
|
2016-09-17 16:12:25 +00:00
|
|
|
/// Fallback when an error is thrown while handling a request.
|
|
|
|
void failSilently(HttpRequest request, ResponseContext res) {}
|
|
|
|
|
2016-02-28 13:11:17 +00:00
|
|
|
/// Starts the server.
|
|
|
|
void listen({InternetAddress address, int port: 3000}) {
|
|
|
|
runZoned(() async {
|
2016-04-18 03:27:23 +00:00
|
|
|
await startServer(address, port);
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
}, onError: _onError);
|
|
|
|
}
|
|
|
|
|
2016-12-21 18:18:26 +00:00
|
|
|
/// Mounts the child on this router.
|
|
|
|
///
|
|
|
|
/// If the router is an [Angel] instance, all controllers
|
|
|
|
/// will be copied, as well as services and response finalizers.
|
|
|
|
///
|
|
|
|
/// [before] and [after] will be preserved.
|
2016-12-31 01:46:41 +00:00
|
|
|
///
|
2016-12-21 18:18:26 +00:00
|
|
|
/// NOTE: The above will not be properly copied if [path] is
|
|
|
|
/// a [RegExp].
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
@override
|
2016-05-02 22:28:14 +00:00
|
|
|
use(Pattern path, Routable routable,
|
2016-10-22 20:41:36 +00:00
|
|
|
{bool hooked: true, String namespace: null}) {
|
2016-12-21 18:18:26 +00:00
|
|
|
final head = path.toString().replaceAll(_straySlashes, '');
|
|
|
|
|
2016-11-28 00:49:27 +00:00
|
|
|
if (routable is Angel) {
|
2016-12-21 18:18:26 +00:00
|
|
|
_children.add(routable.._parent = this);
|
|
|
|
|
|
|
|
if (routable.before.isNotEmpty) {
|
|
|
|
all(path, (req, res) {
|
|
|
|
return true;
|
|
|
|
}, middleware: routable.before);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (routable.after.isNotEmpty) {
|
|
|
|
all(path, (req, res) {
|
|
|
|
return true;
|
|
|
|
}, middleware: routable.after);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (routable.responseFinalizers.isNotEmpty) {
|
|
|
|
responseFinalizers.add((req, res) async {
|
|
|
|
if (req.path.replaceAll(_straySlashes, '').startsWith(head)) {
|
|
|
|
for (var finalizer in routable.responseFinalizers)
|
|
|
|
await finalizer(req, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
2016-11-28 00:49:27 +00:00
|
|
|
|
|
|
|
routable.controllers.forEach((k, v) {
|
|
|
|
final tail = k.toString().replaceAll(_straySlashes, '');
|
|
|
|
controllers['$head/$tail'.replaceAll(_straySlashes, '')] = v;
|
|
|
|
});
|
2016-12-21 18:18:26 +00:00
|
|
|
|
|
|
|
routable.services.forEach((k, v) {
|
|
|
|
final tail = k.toString().replaceAll(_straySlashes, '');
|
|
|
|
services['$head/$tail'.replaceAll(_straySlashes, '')] = v;
|
|
|
|
});
|
2016-11-28 00:49:27 +00:00
|
|
|
}
|
|
|
|
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
if (routable is Service) {
|
|
|
|
routable.app = this;
|
|
|
|
}
|
2016-10-22 20:41:36 +00:00
|
|
|
|
|
|
|
return super.use(path, routable, hooked: hooked, namespace: namespace);
|
2016-02-28 13:11:17 +00:00
|
|
|
}
|
|
|
|
|
2016-06-21 22:56:04 +00:00
|
|
|
/// Registers a callback to run upon errors.
|
|
|
|
onError(AngelErrorHandler handler) {
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
_errorHandler = handler;
|
|
|
|
}
|
2016-04-22 01:42:39 +00:00
|
|
|
|
2016-10-22 20:41:36 +00:00
|
|
|
Angel({bool debug: false}) : super(debug: debug) {
|
2016-09-17 16:12:25 +00:00
|
|
|
bootstrapContainer();
|
|
|
|
}
|
2016-02-28 13:11:17 +00:00
|
|
|
|
2016-04-18 03:27:23 +00:00
|
|
|
/// Creates an HTTPS server.
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
/// Provide paths to a certificate chain and server key (both .pem).
|
|
|
|
/// If no password is provided, a random one will be generated upon running
|
|
|
|
/// the server.
|
2016-11-23 19:50:17 +00:00
|
|
|
factory Angel.secure(String certificateChainPath, String serverKeyPath,
|
|
|
|
{bool debug: false, String password}) {
|
|
|
|
final app = new Angel(debug: debug);
|
|
|
|
|
|
|
|
app._serverGenerator = (InternetAddress address, int port) async {
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
var certificateChain =
|
2016-11-23 09:10:47 +00:00
|
|
|
Platform.script.resolve(certificateChainPath).toFilePath();
|
|
|
|
var serverKey = Platform.script.resolve(serverKeyPath).toFilePath();
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
var serverContext = new SecurityContext();
|
|
|
|
serverContext.useCertificateChain(certificateChain);
|
|
|
|
serverContext.usePrivateKey(serverKey,
|
2016-11-23 19:50:17 +00:00
|
|
|
password: password ?? app._randomString(8));
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
|
|
|
|
return await HttpServer.bindSecure(address, port, serverContext);
|
|
|
|
};
|
2016-11-23 19:50:17 +00:00
|
|
|
|
|
|
|
return app;
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
}
|
|
|
|
}
|