Permalink
Browse files

Major refactorings, and now supports routable sessions

  • Loading branch information...
chrisbu committed Mar 25, 2012
1 parent 803993b commit 05929f374fca6ab19abaefd356b5a7f313e1dce1
View
@@ -1,6 +1,6 @@
(The MIT License)
-Copyright (c) 2012 Chris Buckett
+Copyright (c) 2012 Chris Buckett (chrisbuckett@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
View
@@ -0,0 +1 @@
+crimson.dart
View
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>core</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.google.dart.tools.core.dartBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.google.dart.tools.core.dartNature</nature>
+ </natures>
+</projectDescription>
View
@@ -1,74 +1,99 @@
+// Copyright (C) 2012 Chris Buckett
+// Use of this source code is governed by a MIT style licence
+// can be found in the LICENSE file.
+// website:
+
+
#library("crimson:core");
-//#import("lib/http.dart");
-#import('../log4dart/LogLib.dart');
-#import("dart:builtin");
+#import('../../log4dart/LogLib.dart');
#import("dart:io");
+#import("dart:uri");
+#import("dart:isolate", prefix:"isolate");
#source('crimson_impl.dart');
#source('crimson_utils.dart');
+#source('crimsonPrivate.dart');
+#source('crimsonHttpServer.dart');
+#source('crimsonModule.dart');
+
-#source('endpoints/favicon.dart');
-#source('endpoints/staticFile.dart');
-#source('filters/cookieSession.dart');
+
+//#source('endpoints/favicon.dart');
+//#source('endpoints/staticFile.dart');
+//#source('filters/cookieSession.dart');
/// Crimson is a set of HTTP middleware for Dart, loosely based upon
/// sencha connect [https://github.com/senchalabs/connect]
/// ---
-/// It allows for filters and endpoints (collectively known as handlers, which
+/// It allows for filters and endpoints (collectively known as *handlers*, which
/// implement [CrimsonHandler]) to be added to the http request response
/// cycle.
-/// Filters (which implement [CrimsonFilter]) make use of the request
-/// response (possibly adding to the request or response,
-/// but don't end the flow.
///
-/// Endpoints (which implement [CrimsonEndpoint]) make use of the request
-/// response, and also end the flow (ie, an endpoint will end the response).
+/// There are two interfaces which extend [CrimsonHandler]: [CrimsonFilter] and
+/// [CrimsonEndpoint]
+///
+/// Filters (which implement [CrimsonFilter]) make use of data on the request
+/// and possibly add to the repsonse, but don't close the response.
+///
+/// Endpoints (which implement [CrimsonEndpoint]) also can make use of the data
+/// on the request, but when an endpoint writes to the response, it the
+/// response will be closed. Endpoints try to match the data passed in on the
+/// request, and if that endpoint matches the data, it will handle the request.
+///
+/// The filters and endpoints are executed in the order that they are added to
+/// the server. The first endpoint to match, will handle the response, and the
+/// flow will end.
///
-/// All filters will be called, but Endpoints will stop on the first matching
-/// endpoint.
-/// Usage:
+/// *Usage:*
/// CrimsonHttp http = new CrimsonHttp();
-/// http.filters
-/// .add(new Logger())
-/// .add(new Session("mySessionKey"))
-/// .add(/*...any CrimsonFilter implementation...*/);
-/// http.endpoint
-/// .add(new Favicon())
-/// .add(new StaticFiles("/public"))
-/// .add(new Route("/details/{id}",(req,res) => someCallback(req,res));
-/// .add(/*...any CrimsonEndpoint implementation...*/);
+/// http.use(new Logger())
+/// .endpoint(new Favicon())
+/// .filter(new Session("mySessionKey"))
+/// .filter(/*...any CrimsonFilter implementation...*/);
+/// .endpoint(new StaticFiles("/public"))
+/// .endpoint(new Route("/details/{id}",(req,res) => someCallback(req,res));
+/// .endpoint(/*...any CrimsonEndpoint implementation...*/);
+///
+/// There is no actual distinction between the [endpoint(CrimsonEndpoint)]
+/// and the [filter(CrimsonFilter)] functions - they are included to aid
+/// readability. You can also use the more generic more generic
+/// [add(CrimsonHandler)] function.
interface CrimsonHttpServer default _CrimsonHttpServer {
/// Creates the [CrimsonHttpServer]. Takes an optional
- /// [HTTPServer] implementation which may have already been created.
+ /// [HTTPServer] implementation.
/// If none is supplied, then it creates it's own internal instance
/// of [HTTPServer]
CrimsonHttpServer([HttpServer httpServer]);
-
- /// Contains a list of [CrimsonFilter] implementations.
- /// Filters will be called in turn before the endpoints are called.
- /// Each [CrimsonFilter] will be called in turn, and will hand over
- /// to the next filter.
- CrimsonHandlerList<CrimsonFilter> get filters();
-
- /// Contains a list of [CrimsonEndpoint] implementations.
- /// Each endpoint will be called in turn, and try to match the data
- /// provided in the request.
- /// The first endpoint to match will handle the request and close
- /// the response
- CrimsonHandlerList<CrimsonEndpoint> get endpoints();
-
-
/// Starts the server listening for requests
listen(String host, int port);
+ /// A list of independent modules. Each module runs in its own
+ /// isolate, and is matched based upon the uri passed in.
+ /// The key should somehow match the request url or url/path
+ /// to allow hosting multiple, independent sites on the same server
+ /// The value is the path to the isolate
+ /// TODO: Allow this to be loaded from config
+ LinkedHashMap<String, CrimsonModule> get modules();
+
/// A [Logger] implementation that the [CrimsonHttpServer] and its
/// handlers can make use of.
Logger logger;
}
+//
+///// The CrimsonModule is an [Isolate] which maintains
+//interface CrimsonModule {
+//
+// /// Contains a list of [CrimsonHandler] implementations.
+// /// Handler can be either one of a [CrimsonFilter] or [CrimsonEndpoint]
+// /// implementation. Each filter or endpoint will be called in turn
+// /// until an endpoint matches and handles the request
+// CrimsonHandlerList<CrimsonHandler> get modules();
+//
+//}
/// Contains a list of [CrimsonHandler]. Is used by [CrimsonHttpServer] to
/// contain the list of filters and endpoints
@@ -86,7 +111,11 @@ interface CrimsonHandlerList<E extends CrimsonHandler>
/// .add(...)
/// .add(...)
/// .add(...etc...);
- CrimsonHandlerList add(CrimsonHandler handler);
+ CrimsonHandlerList add(CrimsonHandler handler);
+
+ CrimsonHandlerList addEndpoint(CrimsonEndpoint endpoint);
+
+ CrimsonHandlerList addFilter(CrimsonFilter filter);
}
/// A [CrimsonHandler]
@@ -99,14 +128,16 @@ interface CrimsonHandler {
/// The [CrimsonHttpServer] to which this handler belongs.
/// The handler can use this reference to access things like logger etc...
CrimsonHttpServer server;
+
+ Future<CrimsonData> handle(HttpRequest req, HttpResponse res, CrimsonData data);
}
/// Filters (which implement [CrimsonFilter]) make use of the request &
/// response (possibly adding to the request or response),
/// but don't end the flow.
interface CrimsonFilter extends CrimsonHandler {
-/// Takes the [request] and [response] from the HTTPServer implementation
+ /// Takes the [request] and [response] from the HTTPServer implementation
/// and a next function which should be called when the handler has completed
/// This allows that async handlers can guarentee to have finished
/// before the next handler in the chain is called.
@@ -116,8 +147,7 @@ interface CrimsonFilter extends CrimsonHandler {
/// If [next] is not called, then no more handlers will be called, and the
/// response will be sent back to the client. This is desirable for an
/// endpoint which has not handled the request.
- void handle(HttpRequest request, HttpResponse response,
- void next(CrimsonHttpException error));
+
}
@@ -136,9 +166,11 @@ interface CrimsonEndpoint extends CrimsonHandler {
/// endpoint which has not handled the request.
/// optional [success] callback when a handler successfully handles
/// the request.
- void handle(HttpRequest request, HttpResponse response,
- void next(CrimsonHttpException error),
- void success());
+
+
+}
+
+interface CrimsonData extends Map {
}
View
@@ -0,0 +1,105 @@
+
+class _CrimsonHttpServer implements CrimsonHttpServer {
+
+ /// Contains the [Logger]
+ Logger logger;
+
+
+ /// Constructor
+ _CrimsonHttpServer([HttpServer httpServer]) :
+ //if the optional parameter hasn't been passed, then create
+ //a new HttpServer
+ _httpServer = httpServer == null ? new HttpServer() : httpServer,
+ _modules = new LinkedHashMap<String,CrimsonModule>(),
+ //get a logger for the current class
+ logger = LoggerFactory.getLogger("_CrimsonHttpServer")
+ {
+ //setup the error and request handlers
+ _httpServer.onError = _httpErrorHandler;
+ _httpServer.onRequest = _onRequestHandler;
+ }
+
+ /// Start listening on the [host] and [port] provided.
+ /// Calls the internal [_onRequestHandler] method when a request is received
+ /// which internally processes all the filters and tries to find an endpoint.
+ listen(String host, int port) {
+ // start the server listening
+ _httpServer.listen(host, port);
+ print("Listening on ${host}:${port}");
+ }
+
+
+
+ /// This is the core of method of [CrimsonHttpServer]
+ /// It first loops through each of the [filters] in turn, calling handle
+ /// on each, and then loops through each of the [endpoints] until an endpoint
+ /// has handled the request and populated the response.
+ /// ---
+ /// The loops need to be able to run async, and as such, each of the handle
+ /// calls need to call next();
+ _onRequestHandler(HttpRequest req, HttpResponse res) {
+ //find the module that matches the request
+ logger.debug("${req.method}: ${req.uri}");
+
+ CrimsonModule module = null;
+ for (String key in modules.getKeys()) {
+ if (key.startsWith(req.path)) {
+ module = modules[key];
+ break;
+ }
+ }
+
+ //if no module found by path, and there is a default module, then use it.
+ if (module == null && modules.containsKey("*")) {
+ module = modules["*"];
+ }
+
+ if (module != null) {
+ try {
+ Future<CrimsonData> handled = module.handle(req,res);
+ handled.then((result) {
+ res.outputStream.close();
+ });
+ handled.handleException((error) {
+ res.outputStream.close();
+ _httpErrorHandler(error);
+ });
+ }
+ catch (var ex, var stack) {
+ logger.error("${ex}, ${stack}");
+ }
+ }
+ else {
+ logger.debug("404, not found");
+ res.statusCode = HttpStatus.NOT_FOUND;
+ res.outputStream.close();
+ }
+
+ }
+
+
+ _httpErrorHandler(Exception error) {
+ this.logger.error(error.toString());
+ //CrimsonHttpException ex = new CrimsonHttpException(HttpStatus.INTERNAL_SERVER_ERROR, error);
+ //_crimsonErrorHandler(ex, null, null);
+ }
+
+// _crimsonErrorHandler(CrimsonHttpException error, HttpRequest req, HttpResponse res) {
+// res.statusCode = error.status;
+// res.setHeader("Content-Type", "text/plain");
+// res.outputStream.writeString("CrimsonHttp: Error"
+// "${error}"
+// "Method: ${req.method}: ${req.uri}");
+// res.outputStream.close();
+// //TODO: If an error handler filter has been registered, then use that.
+// //Might be better to have an errorHandler collection in the same
+// //way that we have filters and endpoints.
+// }
+
+
+
+ LinkedHashMap<String,CrimsonModule> get modules() => _modules;
+
+ final HttpServer _httpServer;
+ final LinkedHashMap<String, CrimsonModule> _modules;
+}
Oops, something went wrong.

0 comments on commit 05929f3

Please sign in to comment.