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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 {