diff --git a/.gitignore b/.gitignore
index 518345916..3faf18871 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
build/
packages
+
# Or the files created by dart2js.
*.dart.js
*.js_
@@ -12,3 +13,6 @@ packages
# Include when developing application packages.
pubspec.lock
+
+workspace.xml
+Dart_Packages.xml
\ No newline at end of file
diff --git a/.idea/libraries/Dart_Packages.xml b/.idea/libraries/Dart_Packages.xml
deleted file mode 100644
index d0bfd7ece..000000000
--- a/.idea/libraries/Dart_Packages.xml
+++ /dev/null
@@ -1,332 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index 169e136af..000000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,1290 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Android Lint
-
-
- Code maturity issuesJava
-
-
- Data flow issuesJava
-
-
- General
-
-
- J2ME issuesJava
-
-
- Java
-
-
- Maven
-
-
- Performance issuesJava
-
-
- Plugin DevKit
-
-
- XPath
-
-
- XSLT
-
-
-
-
- Android
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1432926049388
-
- 1432926049388
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- No facets are configured
-
-
-
-
-
-
-
-
-
-
-
- Dart SDK
-
-
-
-
-
-
-
-
-
-
-
- 1.8
-
-
-
-
-
-
-
-
-
-
-
- monadart
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/lib/auth/auth_controller.dart b/lib/auth/auth_controller.dart
index 43411962d..5683c56f9 100644
--- a/lib/auth/auth_controller.dart
+++ b/lib/auth/auth_controller.dart
@@ -1,16 +1,12 @@
part of monadart;
class AuthController extends HttpController {
-
static String get RoutePattern => "/auth/token/[refresh]";
- static String get AuthenticationServerContextKey =>
- "AuthenticationServerContextKey";
-
- AuthenticationServer get authenticationServer =>
- request.context[AuthenticationServerContextKey];
+ AuthenticationServer authenticationServer;
- AuthController() {
+ AuthController(AuthenticationServer authServer) {
+ authenticationServer = authServer;
acceptedContentTypes = [new ContentType("application", "x-www-form-urlencoded")];
}
diff --git a/lib/auth/authenticator.dart b/lib/auth/authenticator.dart
index f87f4c1cc..2fd793e44 100644
--- a/lib/auth/authenticator.dart
+++ b/lib/auth/authenticator.dart
@@ -52,7 +52,7 @@ class Authenticator extends RequestHandler {
}
var permission = await server.verify(parser.bearerToken);
- req.context[PermissionKey] = permission;
+ req.permission = permission;
return req;
}
@@ -72,7 +72,7 @@ class Authenticator extends RequestHandler {
}
var perm = new Permission(client.id, null, server);
- req.context[PermissionKey] = perm;
+ req.permission = perm;
return req;
}
diff --git a/lib/base/application.dart b/lib/base/application.dart
index 72f04b758..d1329c5eb 100644
--- a/lib/base/application.dart
+++ b/lib/base/application.dart
@@ -100,9 +100,9 @@ abstract class ApplicationPipeline extends RequestHandler {
/// A container for web server applications.
///
/// Applications are responsible for managing starting and stopping of HTTP server instances across multiple isolates.
-/// Behavior specific to an application is implemented by setting the [Application]'s [configuration] and providing
-/// a [pipelineType] as a [ApplicationPipeline] subclass.
-class Application {
+/// Behavior specific to an application is implemented by setting the [Application]'s [configuration], and providing
+/// a [PipelineType] and [RequestType].
+class Application {
/// A list of items identifying the Isolates running a HTTP(s) listener and response handlers.
List<_ServerRecord> servers = [];
@@ -112,16 +112,11 @@ class Application {
ApplicationInstanceConfiguration configuration =
new ApplicationInstanceConfiguration();
- /// The type of [ApplicationPipeline] that configures how requests are handled.
- ///
- /// This must be configured prior to [start]ing the [Application]. Must be a subtype of [ApplicationPipeline].
- Type pipelineType;
-
/// Starts the application by spawning Isolates that listen for HTTP(s) requests.
///
/// Returns a [Future] that completes when all Isolates have started listening for requests.
/// The [numberOfInstances] defines how many Isolates are spawned running this application's [configuration]
- /// and [pipelineType].
+ /// and [PipelineType].
Future start({int numberOfInstances: 1}) async {
if (configuration.address == null) {
if (configuration.isIpv6Only) {
@@ -152,12 +147,14 @@ class Application {
ApplicationInstanceConfiguration config, int identifier) async {
var receivePort = new ReceivePort();
- var pipelineTypeMirror = reflectType(pipelineType);
+ var pipelineTypeMirror = reflectType(PipelineType);
var pipelineLibraryURI = (pipelineTypeMirror.owner as LibraryMirror).uri;
var pipelineTypeName = MirrorSystem.getName(pipelineTypeMirror.simpleName);
- var initialMessage = new _InitialServerMessage(pipelineTypeName,
- pipelineLibraryURI, config, identifier, receivePort.sendPort);
+ var initialMessage = new _InitialServerMessage(
+ pipelineTypeName,
+ pipelineLibraryURI,
+ config, identifier, receivePort.sendPort);
var isolate =
await Isolate.spawn(_Server.entry, initialMessage, paused: true);
isolate.addErrorListener(receivePort.sendPort);
@@ -175,9 +172,16 @@ class _Server {
ApplicationPipeline pipeline;
int identifier;
- _Server(this.pipeline, this.configuration, this.identifier,
+ _Server(this.pipeline,
+ this.configuration, this.identifier,
this.supervisingApplicationPort);
+
+
+ ResourceRequest createRequest(HttpRequest req) {
+ return new ResourceRequest(req);
+ }
+
Future start() async {
pipeline.options = configuration.pipelineOptions;
await pipeline.willOpen();
@@ -185,11 +189,14 @@ class _Server {
pipeline.nextHandler = pipeline.initialHandler();
var onBind = (s) {
+ new Logger("monadart").info("Server monadart/$identifier started.");
+
server = s;
server.serverHeader = "monadart/${this.identifier}";
- server.map((httpReq) => new ResourceRequest(httpReq)).listen((req) async {
+ server.map(createRequest).listen((req) async {
+ new Logger("monadart").info("Request received $req.");
await pipeline.willReceiveRequest(req);
pipeline.deliver(req);
});
@@ -229,7 +236,10 @@ class _Server {
var app = pipelineTypeMirror.newInstance(new Symbol(""), []).reflectee;
var server = new _Server(
- app, params.configuration, params.identifier, params.parentMessagePort);
+ app,
+ params.configuration,
+ params.identifier,
+ params.parentMessagePort);
server.start();
}
diff --git a/lib/base/http_controller.dart b/lib/base/http_controller.dart
index 0363cb85c..3ee954cf7 100644
--- a/lib/base/http_controller.dart
+++ b/lib/base/http_controller.dart
@@ -224,6 +224,7 @@ abstract class HttpController extends RequestHandler {
return response;
} on _InternalControllerException catch (e) {
+ logger.info("Request (${request.toDebugString()}) failed to process: ${e.message}.");
var response = new Response(e.statusCode, {}, null);
request.response.statusCode = e.statusCode;
@@ -240,6 +241,7 @@ abstract class HttpController extends RequestHandler {
return response;
} catch (e, stacktrace) {
+ logger.severe("HttpController: Uncaught error in request (${request.toDebugString()}): ${e}\n $stacktrace.");
if (_exceptionHandler != null) {
var response = _exceptionHandler(this.request, e, stacktrace);
return response;
diff --git a/lib/base/request_handler.dart b/lib/base/request_handler.dart
index 8409e5fac..69cd8c2f5 100644
--- a/lib/base/request_handler.dart
+++ b/lib/base/request_handler.dart
@@ -14,6 +14,8 @@ abstract class RequestHandlerResult {}
class RequestHandler {
Function _handler;
+ Logger get logger => new Logger("monadart");
+
/// The initializer for RequestHandlers.
///
/// To use a closure-based RequestHandler, you may specify [requestHandler] for
@@ -58,6 +60,7 @@ class RequestHandler {
/// and not [processRequest].
void deliver(ResourceRequest req) {
+ logger.finest("$this received request $req.");
processRequest(req)
.then((result) {
if (result is ResourceRequest && nextHandler != null) {
@@ -70,6 +73,7 @@ class RequestHandler {
if (err is HttpResponseException) {
req.respond(err.response());
} else {
+ logger.severe("$this generated internal error for request ${req.toDebugString()}. $err\n Stacktrace:\n${st.toString()}");
req.respond(new Response.serverError(headers: {HttpHeaders.CONTENT_TYPE : "application/json"},
body: JSON.encode({"error" : "Unexpected exception in ${this.runtimeType}.", "stacktrace" : st.toString()})));
}
@@ -92,9 +96,15 @@ class RequestHandler {
}
class RequestHandlerGenerator extends RequestHandler {
+ List arguments;
+ RequestHandlerGenerator({List arguments: const []}) {
+ this.arguments = arguments;
+ }
+
@override
void deliver(ResourceRequest req) {
- var handler = reflectClass(T).newInstance(new Symbol(""), []).reflectee
+ logger.finest("Generating handler $T with arguments $arguments.");
+ var handler = reflectClass(T).newInstance(new Symbol(""), arguments).reflectee
as RequestHandler;
handler.nextHandler = this.nextHandler;
handler.deliver(req);
diff --git a/lib/base/resource_request.dart b/lib/base/resource_request.dart
index 83e47e99f..992ec17ac 100644
--- a/lib/base/resource_request.dart
+++ b/lib/base/resource_request.dart
@@ -25,15 +25,21 @@ class ResourceRequest implements RequestHandlerResult {
/// path will have [segments] of ['users', '1'] and [variables] of {'id' : '1'}.
ResourcePatternMatch path;
- /// Optional data for members of a pipeline to attach to a request for later members to utilize.
+ /// Permission information associated with this request.
///
- /// This is purely contextual to the application. An example is pipeline that adds a database adapter
- /// to the request so that the handling [HttpController] has access to it.
- Map context = new Map();
+ /// When this request goes through an [Authenticator], this value will be set with
+ /// permission information from the authenticator. Use this to determine client, resource owner
+ /// or other properties of the authentication information in the request. This value will be
+ /// null if no permission has been set.
+ Permission permission;
+
+ int id = new DateTime.now().millisecondsSinceEpoch;
ResourceRequest(this.innerRequest) {}
void respond(Response respObj) {
+ new Logger("monadart").info("Request ($id) sending response $respObj.");
+
response.statusCode = respObj.statusCode;
if (respObj.headers != null) {
@@ -48,4 +54,17 @@ class ResourceRequest implements RequestHandlerResult {
response.close();
}
+
+ String toString() {
+ return "${this.innerRequest.uri} (${this.id})";
+ }
+
+ String toDebugString() {
+ var builder = new StringBuffer();
+ builder.writeln("${this.innerRequest.uri} (${this.id})");
+ this.innerRequest.headers.forEach((name, values) {
+ builder.write("$name $values,");
+ });
+ return builder.toString();
+ }
}
diff --git a/lib/base/response.dart b/lib/base/response.dart
index 02befebe2..69236e9ed 100644
--- a/lib/base/response.dart
+++ b/lib/base/response.dart
@@ -62,4 +62,8 @@ class Response implements RequestHandlerResult {
Response.serverError({Map headers, dynamic body})
: this(HttpStatus.INTERNAL_SERVER_ERROR, headers, body);
+
+ String toString() {
+ return "$statusCode $headers";
+ }
}
diff --git a/lib/base/router.dart b/lib/base/router.dart
index a0d9ec75a..6aceec933 100644
--- a/lib/base/router.dart
+++ b/lib/base/router.dart
@@ -90,12 +90,14 @@ class Router extends RequestHandler {
var routeMatch = route.pattern.matchUri(req.innerRequest.uri);
if (routeMatch != null) {
+ logger.finest("Router: match for ${req.innerRequest.uri}.");
req.path = routeMatch;
route.handler.deliver(req);
return;
}
}
+ logger.finest("Router: no matching route for ${req.innerRequest.uri}.");
_unhandledRequestHandler(req);
}
diff --git a/lib/monadart.dart b/lib/monadart.dart
index ff4ded712..d8cbbc951 100644
--- a/lib/monadart.dart
+++ b/lib/monadart.dart
@@ -10,8 +10,10 @@ import 'package:pbkdf2/pbkdf2.dart';
import 'dart:math';
import 'package:http_server/http_server.dart';
import 'package:http/http.dart' as http;
+import 'package:logging/logging.dart';
export 'package:http_server/http_server.dart';
+export 'package:logging/logging.dart';
part 'base/resource_pattern.dart';
part 'base/resource_request.dart';
diff --git a/pubspec.yaml b/pubspec.yaml
index e720f6533..eaaf1cfc6 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -6,6 +6,7 @@ dependencies:
pbkdf2: any
crypto: any
http: any
+ logging: any
dev_dependencies:
test: any
diff --git a/test/application_tests.dart b/test/application_tests.dart
index dbe936c63..cf95c477a 100644
--- a/test/application_tests.dart
+++ b/test/application_tests.dart
@@ -4,9 +4,8 @@ import 'dart:async';
import 'package:http/http.dart' as http;
main() {
- var app = new Application();
+ var app = new Application();
app.configuration.port = 8080;
- app.pipelineType = TPipeline;
test("Application starts", () async {
await app.start();
diff --git a/test/auth_controller_tests.dart b/test/auth_controller_tests.dart
index d44fafa5b..ec4870e61 100644
--- a/test/auth_controller_tests.dart
+++ b/test/auth_controller_tests.dart
@@ -30,8 +30,7 @@ void main() {
server = s;
server.listen((req) {
var resReq = new ResourceRequest(req);
- resReq.context[AuthController.AuthenticationServerContextKey] = authenticationServer;
- var authController = new AuthController();
+ var authController = new AuthController(authenticationServer);
authController.deliver(resReq);
});
});
diff --git a/test/integration_tests.dart b/test/integration_tests.dart
index fe6bf58c4..be436419e 100644
--- a/test/integration_tests.dart
+++ b/test/integration_tests.dart
@@ -7,10 +7,11 @@ import 'helpers.dart';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'dart:convert';
+
+
main() async {
- var app = new Application();
+ var app = new Application();
app.configuration.port = 8080;
- app.pipelineType = TPipeline;
await app.start();
var tc = new TestClient()
@@ -37,7 +38,7 @@ main() async {
class TPipeline extends ApplicationPipeline {
Router router = new Router();
- PostgresModelAdapter adapter = new PostgresModelAdapter(null, () async {
+ static PostgresModelAdapter adapter = new PostgresModelAdapter(null, () async {
var uri = 'postgres://dart:dart@localhost:5432/dart_test';
return await connect(uri);
});
@@ -51,14 +52,12 @@ class TPipeline extends ApplicationPipeline {
@override
Future willReceiveRequest(ResourceRequest req) async {
- req.context["adapter"] = adapter;
}
@override
Future willOpen() async {
await generateTemporarySchemaFromModels(adapter, [TestUser, Token]);
- adapter.loggingEnabled = true;
authenticationServer = new AuthenticationServer(
new AuthDelegate(adapter));
@@ -74,16 +73,13 @@ class TPipeline extends ApplicationPipeline {
}
class IdentityController extends HttpController {
- PostgresModelAdapter get adapter => request.context["adapter"];
- Permission get permission => request.context[Authenticator.PermissionKey];
-
@httpGet
Future getIdentity() async {
var q = new Query()
..resultKeys = ["username", "id"]
- ..predicateObject = (new TestUser()..id = permission.resourceOwnerIdentifier);
+ ..predicateObject = (new TestUser()..id = request.permission.resourceOwnerIdentifier);
- var user = await q.fetchOne(adapter);
+ var user = await q.fetchOne(TPipeline.adapter);
if (user == null) {
return new Response.notFound();
}
@@ -93,12 +89,9 @@ class IdentityController extends HttpController {
}
class UsersController extends HttpController {
- PostgresModelAdapter get adapter => request.context["adapter"];
- Permission get permission => request.context[Authenticator.PermissionKey];
-
@httpPost
Future createUser() async {
- if (permission.resourceOwnerIdentifier != null) {
+ if (request.permission.resourceOwnerIdentifier != null) {
return new Response.badRequest();
}
@@ -113,12 +106,12 @@ class UsersController extends HttpController {
var q = new Query()
..resultKeys = ["username", "id"]
..valueObject = u;
- u = await q.insert(adapter);
+ u = await q.insert(TPipeline.adapter);
- var token = await permission.grantingServer.authenticate(u.username,
+ var token = await request.permission.grantingServer.authenticate(u.username,
password,
- permission.clientID, "kilimanjaro");
+ request.permission.clientID, "kilimanjaro");
return AuthController.tokenResponse(token);
}
-}
+}
\ No newline at end of file
diff --git a/test/isolate_application_tests.dart b/test/isolate_application_tests.dart
index ad45bef0c..7cef65f65 100644
--- a/test/isolate_application_tests.dart
+++ b/test/isolate_application_tests.dart
@@ -4,9 +4,8 @@ import 'dart:async';
import 'package:http/http.dart' as http;
main() {
- var app = new Application();
+ var app = new Application();
app.configuration.port = 8080;
- app.pipelineType = TPipeline;
test("Application starts", () async {