platform/lib/src/http/routable.dart

133 lines
4.4 KiB
Dart
Raw Normal View History

library angel_framework.http.routable;
import 'dart:async';
import 'dart:io';
2016-10-22 20:41:36 +00:00
import 'package:angel_route/angel_route.dart';
import '../util.dart';
import 'angel_base.dart';
import 'controller.dart';
import 'hooked_service.dart';
import 'metadata.dart';
import 'request_context.dart';
import 'response_context.dart';
import 'service.dart';
2016-11-28 00:49:27 +00:00
final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
2016-02-28 13:11:17 +00:00
/// A function that intercepts a request and determines whether handling of it should continue.
typedef Future<bool> 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);
2016-02-28 13:11:17 +00:00
/// A routable server that can handle dynamic requests.
2016-10-22 20:41:36 +00:00
class Routable extends Router {
2016-11-28 00:49:27 +00:00
final Map<Pattern, Controller> _controllers = {};
2016-10-22 20:41:36 +00:00
final Map<Pattern, Service> _services = {};
Routable({bool debug: false}) : super(debug: debug);
2016-02-28 13:11:17 +00:00
2016-10-22 20:41:36 +00:00
/// Additional filters to be run on designated requests.
@override
final Map<String, RequestMiddleware> requestMiddleware = {};
2016-04-18 03:27:23 +00:00
/// A set of [Service] objects that have been mapped into routes.
2016-11-23 09:10:47 +00:00
Map<Pattern, Service> get services => _services;
2016-04-18 03:27:23 +00:00
2016-06-27 00:20:42 +00:00
/// A set of [Controller] objects that have been loaded into the application.
2016-11-28 00:49:27 +00:00
Map<Pattern, Controller> get controllers => _controllers;
2016-06-27 00:20:42 +00:00
2016-10-22 20:41:36 +00:00
StreamController<Service> _onService =
new StreamController<Service>.broadcast();
2016-07-05 22:11:54 +00:00
/// Fired whenever a service is added to this instance.
///
/// **NOTE**: This is a broadcast stream.
Stream<Service> get onService => _onService.stream;
2016-04-18 03:27:23 +00:00
/// Assigns a middleware to a name for convenience.
2016-10-22 20:41:36 +00:00
@override
registerMiddleware(String name, RequestMiddleware middleware) =>
super.registerMiddleware(name, middleware);
2016-04-18 03:27:23 +00:00
/// Retrieves the service assigned to the given path.
2016-10-22 20:41:36 +00:00
Service service(Pattern path) => _services[path];
2016-04-18 03:27:23 +00:00
2016-06-27 00:20:42 +00:00
/// Retrieves the controller with the given name.
Controller controller(String name) => controllers[name];
2016-10-22 20:41:36 +00:00
@override
Route addRoute(String method, Pattern path, Object handler,
2016-11-23 09:10:47 +00:00
{List middleware: const []}) {
2016-10-22 20:41:36 +00:00
final List handlers = [];
// Merge @Middleware declaration, if any
Middleware middlewareDeclaration = getAnnotation(handler, Middleware);
if (middlewareDeclaration != null) {
handlers.addAll(middlewareDeclaration.handlers);
}
2016-11-23 09:10:47 +00:00
final List handlerSequence = [];
handlerSequence.addAll(middleware ?? []);
handlerSequence.addAll(handlers);
return super.addRoute(method, path, handler, middleware: handlerSequence);
2016-10-22 20:41:36 +00:00
}
void use(Pattern path, Router router,
{bool hooked: true, String namespace: null}) {
Router _router = router;
Service service;
2016-06-21 04:19:43 +00:00
// If we need to hook this service, do it here. It has to be first, or
// else all routes will point to the old service.
2016-10-22 20:41:36 +00:00
if (router is Service) {
Hooked hookedDeclaration = getAnnotation(router, Hooked);
_router = service = (hookedDeclaration != null || hooked)
? new HookedService(router)
: router;
_services[path
.toString()
.trim()
.replaceAll(new RegExp(r'(^/+)|(/+$)'), '')] = service;
2016-11-23 09:10:47 +00:00
service.addRoutes();
2016-06-21 04:19:43 +00:00
}
2016-10-22 20:41:36 +00:00
final handlers = [];
2016-11-23 09:10:47 +00:00
2016-10-22 20:41:36 +00:00
if (_router is AngelBase) {
handlers.add((RequestContext req, ResponseContext res) async {
req.app = _router;
res.app = _router;
return true;
});
}
// Let's copy middleware, heeding the optional middleware namespace.
2016-10-22 20:41:36 +00:00
String middlewarePrefix = namespace != null ? "$namespace." : "";
2016-10-22 20:41:36 +00:00
Map copiedMiddleware = new Map.from(router.requestMiddleware);
for (String middlewareName in copiedMiddleware.keys) {
requestMiddleware["$middlewarePrefix$middlewareName"] =
2016-10-22 20:41:36 +00:00
copiedMiddleware[middlewareName];
}
2016-11-23 09:10:47 +00:00
// _router.dumpTree(header: 'Mounting on "$path":');
// root.child(path, debug: debug, handlers: handlers).addChild(router.root);
mount(path, _router);
2016-04-18 03:27:23 +00:00
2016-10-22 20:41:36 +00:00
if (router is Routable) {
// Copy services, too. :)
for (Pattern servicePath in _router._services.keys) {
String newServicePath =
path.toString().trim().replaceAll(new RegExp(r'(^/+)|(/+$)'), '') +
'/$servicePath';
_services[newServicePath] = _router._services[servicePath];
}
}
2016-10-22 20:41:36 +00:00
if (service != null) _onService.add(service);
}
2016-10-22 20:41:36 +00:00
}