2016-02-28 13:11:17 +00:00
|
|
|
part of angel_framework.http;
|
|
|
|
|
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-04-18 03:27:23 +00:00
|
|
|
/// A function that configures an [Angel] server in some way.
|
|
|
|
typedef AngelConfigurer(Angel app);
|
|
|
|
|
2016-02-28 13:11:17 +00:00
|
|
|
/// A powerful real-time/REST/MVC server class.
|
|
|
|
class Angel extends Routable {
|
2016-04-18 03:27:23 +00:00
|
|
|
ServerGenerator _serverGenerator = (address, port) async => await HttpServer
|
|
|
|
.bind(address, port);
|
2016-04-22 01:27:28 +00:00
|
|
|
var viewGenerator = (String view, {Map data}) => "No view engine has been configured yet.";
|
2016-04-18 03:27:23 +00:00
|
|
|
|
|
|
|
HttpServer httpServer;
|
|
|
|
God god = new God();
|
|
|
|
|
|
|
|
startServer(InternetAddress address, int port) async {
|
2016-02-28 13:11:17 +00:00
|
|
|
var server = await _serverGenerator(
|
|
|
|
address ?? InternetAddress.LOOPBACK_IP_V4, port);
|
2016-04-18 03:27:23 +00:00
|
|
|
this.httpServer = server;
|
2016-02-28 13:11:17 +00:00
|
|
|
var router = new Router(server);
|
|
|
|
|
2016-04-18 03:27:23 +00:00
|
|
|
this.routes.forEach((Route route) {
|
2016-02-28 13:11:17 +00:00
|
|
|
router.serve(route.matcher, method: route.method).listen((
|
2016-04-18 03:27:23 +00:00
|
|
|
HttpRequest request) async {
|
|
|
|
RequestContext req = await RequestContext.from(
|
|
|
|
request, route.parseParameters(request.uri.toString()), this,
|
|
|
|
route);
|
|
|
|
ResponseContext res = await ResponseContext.from(
|
|
|
|
request.response, this);
|
|
|
|
bool canContinue = true;
|
2016-02-28 13:11:17 +00:00
|
|
|
|
2016-04-18 03:27:23 +00:00
|
|
|
for (var handler in route.handlers) {
|
|
|
|
if (canContinue) {
|
|
|
|
canContinue = await new Future<bool>.sync(() async {
|
|
|
|
return _applyHandler(handler, req, res);
|
|
|
|
}).catchError((e) {
|
|
|
|
stderr.write(e.error);
|
|
|
|
canContinue = false;
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!res.willCloseItself) {
|
|
|
|
res.responseData.forEach((blob) => request.response.add(blob));
|
|
|
|
await request.response.close();
|
|
|
|
}
|
2016-02-28 13:11:17 +00:00
|
|
|
});
|
|
|
|
});
|
2016-04-18 03:27:23 +00:00
|
|
|
|
|
|
|
return server;
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> _applyHandler(handler, RequestContext req,
|
|
|
|
ResponseContext res) async {
|
|
|
|
if (handler is Middleware) {
|
2016-04-21 20:37:02 +00:00
|
|
|
var result = await handler(req, res);
|
|
|
|
if (result is bool)
|
|
|
|
return result == true;
|
|
|
|
else if (result != null) {
|
|
|
|
res.json(result);
|
|
|
|
return false;
|
|
|
|
} else return true;
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (handler is RawRequestHandler) {
|
|
|
|
var result = await handler(req.underlyingRequest);
|
2016-04-21 20:37:02 +00:00
|
|
|
if (result is bool)
|
|
|
|
return result == true;
|
|
|
|
else if (result != null) {
|
|
|
|
res.json(result);
|
|
|
|
return false;
|
|
|
|
} else return true;
|
2016-04-18 03:27:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (handler is Function || handler is Future) {
|
|
|
|
var result = await handler();
|
2016-04-21 20:37:02 +00:00
|
|
|
if (result is bool)
|
|
|
|
return result == true;
|
|
|
|
else if (result != null) {
|
|
|
|
res.json(result);
|
|
|
|
return false;
|
|
|
|
} else return true;
|
2016-04-18 03:27:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (middleware.containsKey(handler)) {
|
|
|
|
return await _applyHandler(middleware[handler], req, res);
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
res.willCloseItself = true;
|
|
|
|
res.underlyingResponse.write(god.serialize(handler));
|
|
|
|
await res.underlyingResponse.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Applies an [AngelConfigurer] to this instance.
|
|
|
|
void configure(AngelConfigurer configurer) {
|
|
|
|
configurer(this);
|
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);
|
2016-02-28 13:11:17 +00:00
|
|
|
}, onError: onError);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Handles a server error.
|
2016-04-18 03:27:23 +00:00
|
|
|
var onError = (e, [StackTrace stackTrace]) {
|
|
|
|
stderr.write(e.toString());
|
|
|
|
if (stackTrace != null)
|
|
|
|
stderr.write(stackTrace.toString());
|
|
|
|
};
|
|
|
|
|
|
|
|
Angel() : super() {}
|
2016-02-28 13:11:17 +00:00
|
|
|
|
2016-04-18 03:27:23 +00:00
|
|
|
/// Creates an HTTPS server.
|
|
|
|
Angel.secure() : super() {}
|
2016-02-28 13:11:17 +00:00
|
|
|
}
|