Permalink
Browse files

Added dynamic requests and routing

  • Loading branch information...
1 parent 8b67bd2 commit 3cd81c4bd76df5c95a43381794a5d21a8188c919 @pwlin committed May 13, 2012
Showing with 103 additions and 25 deletions.
  1. +1 −1 README.md
  2. +9 −2 config.sample.json
  3. +80 −20 lib/ananas/index.js
  4. +2 −2 server.js
  5. +11 −0 www-example/localhost.dev/example_dynamic/index.js
View
2 README.md
@@ -6,14 +6,14 @@
## Features
- Static file serving
+- Routing and dynamic requests
- Apache-like directory listing with appropriate content-type icons
- Virtual hosts
- Proper `Date`, `Last-Modified`, `Content-Type` and `Content-Length` headers
- Configuration keys for server name, ip, port, directory index file and private urls
##TODO
- Documentation
-- Routing & dynamic pages
## Usage
1. copy config.sample.json to config.json
View
11 config.sample.json
@@ -1,6 +1,7 @@
{
"bind_ip" : "127.0.0.1",
- "bind_port" : 8080,
+ "bind_port" : "8080",
+ "app_status" : "dev",
"server_name" : "Ananas",
/* "server_name" : "Apache/1.3.33 (Win32)", */
@@ -17,7 +18,13 @@
"www_root" : "./www-example/localhost.dev/",
"directory_listing" : true,
"directory_index" : "index.html",
- "private_uri" : "^\\/secret|^\\/folder1\\/secret"
+ "private_uri" : "^\\/secret|^\\/folder1\\/secret",
+ "routes" : {
+ "/dynamic1/test1.js" : {
+ "map_to" : "../../www-example/localhost.dev/example_dynamic/index.js",
+ "content_type" : "text/javascript; charset=utf-8"
+ }
+ }
},
"localhost2.dev" : {
View
100 lib/ananas/index.js
@@ -4,35 +4,38 @@ var http = require('http'),
fs = require('fs'),
url = require('url'),
path = require('path'),
+ querystring = require('querystring'),
utils = require('./utils'),
mime = require('./mime'),
+ configuration_file = '',
configuration_data = '',
vhosts = {};
var ananas = {
init : function(config_file) {
var self = this;
- fs.readFile(config_file, 'binary', function(err, data) {
+ return fs.readFile(config_file, 'binary', function(err, data) {
if(err) {
- console.error("\nAnanas Error:\n", "Sorry, the configuration file you passed as first argument (" + config_file + ") does not exists.\n",
+ console.error("\nAnanas Error:\n", "Sorry, the configuration file you passed to Ananas (" + config_file + ") does not exists.\n",
"Please check the file name and try again.\n", "Exiting...\n"
);
process.exit();
return false;
}
else {
+ configuration_file = config_file;
configuration_data = data;
return self.create_server();
}
});
},
create_server : function() {
- var self = this,
- config = JSON.parse(utils.remove_comments(configuration_data)),
- bind_ip = config['bind_ip'] && config['bind_ip'] != '*' ? config['bind_ip'] : null,
- bind_port = config['bind_port'] ? config['bind_port'] : '8080';
+ var self = this;
+ var config = JSON.parse(utils.remove_comments(configuration_data));
+ var bind_ip = config['bind_ip'] && config['bind_ip'] != '*' ? config['bind_ip'] : null;
+ var bind_port = config['bind_port'] ? config['bind_port'] : '8080';
http.createServer(function(request, response) {
return self.on_connection(request, response);
}).listen(bind_port, bind_ip);
@@ -41,16 +44,25 @@ var ananas = {
on_connection : function(request, response) {
var requested_host_name = request.headers.host.match(/\:/) ? request.headers.host.match(/(.*)\:(.*)/)[1].toLowerCase() : request.headers.host.toLowerCase();
- var config = this.find_vhost_config(requested_host_name);
- return this.handle_static(request, response, config);
+ var config = this.find_vhost_config(requested_host_name);
+ config['uri'] = utils.url_decode(url.parse(request.url).pathname);
+ return this.handle_dynamic(request, response, config) || this.handle_static(request, response, config);
},
- find_vhost_config : function(requested_host_name) {
- if (vhosts[requested_host_name]) {
+ find_vhost_config : function(requested_host_name, fresh) {
+ fresh = fresh || false;
+ if (vhosts[requested_host_name] && fresh === false) {
return vhosts[requested_host_name];
}
var config_vhost_found = false;
- var config = JSON.parse(utils.remove_comments(configuration_data));
+ var config = {};
+ if (fresh === false) {
+ config = JSON.parse(utils.remove_comments(configuration_data));
+ }
+ else {
+ config = fs.readFileSync(configuration_file, 'binary');
+ config = JSON.parse(utils.remove_comments(config));
+ }
var config_vhost = {};
for(var vhost in config['vhosts']) {
if(config['vhosts'].hasOwnProperty(vhost)) {
@@ -70,30 +82,79 @@ var ananas = {
if (config_vhost_found === false) {
config_vhost = config['vhosts']['default'];
}
+ config_vhost['app_status'] = config['app_status'];
config_vhost['vhost_name'] = requested_host_name;
config_vhost['server_name'] = config['server_name'];
config_vhost['bind_port'] = config['bind_port'];
vhosts[requested_host_name] = config_vhost;
return config_vhost;
},
+ handle_dynamic : function(request, response, config) {
+ if(!config['routes']) {
+ return false;
+ }
+ var status_code = 200;
+ var data = '';
+ var content_type = '';
+ var self = this;
+ var route_found = false;
+ if(config['app_status'].toLowerCase() == 'dev') {
+ config = this.find_vhost_config(config['vhost_name'], true);
+ config['uri'] = utils.url_decode(url.parse(request.url).pathname);
+ }
+ for(var route in config['routes']) {
+ if(config['routes'].hasOwnProperty(route)) {
+ if((route.replace(/\/$/,'')).toLowerCase() == (config['uri'].replace(/\/$/,'')).toLowerCase()) {
+ try {
+ var my_route_lib = require(config['routes'][route]['map_to']);
+ if(config['app_status'].toLowerCase() == 'dev') {
+ delete require.cache[require.resolve(config['routes'][route]['map_to'])];
+ }
+ data = my_route_lib.init({
+ 'request' : request,
+ 'config' : config,
+ 'querystring' : querystring.parse(url.parse(request.url).query)
+ });
+ }
+ catch(e) {
+ console.error("\nAnanas route error:\n", "Route:\n", route + "\n", "Strack trace:\n", e.stack + "\n");
+ }
+ content_type = config['routes'][route]['content_type'];
+ route_found = true;
+ break;
+ }
+ }
+ }
+ if (route_found === true) {
+ return self.serve({
+ 'request': request,
+ 'response' : response,
+ 'status_code' : status_code,
+ 'config' : config,
+ 'data' : data,
+ 'content_type' : content_type
+ });
+ } else {
+ return false;
+ }
+ },
+
handle_static : function(request, response, config) {
var filename = '';
- var private_uri_regexp_pattern = config['private_uri'] ? new RegExp(config['private_uri'], 'ig') : null;
- var uri = utils.url_decode(url.parse(request.url).pathname);
+ var private_uri_regexp_pattern = config['private_uri'] ? new RegExp(config['private_uri'], 'ig') : false;
var status_code = 200;
var data = '';
var content_type = '';
var self = this;
- config['uri'] = uri;
- if(uri.match(/\/\.|404\.html$/ig) || (config['private_uri'] && uri.match(private_uri_regexp_pattern))) {
+ if(config['uri'].match(/\/\.|404\.html$/ig) || (config['private_uri'] && config['uri'].match(private_uri_regexp_pattern))) {
filename = path.join(config['www_root'], '404.html');
status_code = 404;
}
else {
- filename = path.join(config['www_root'], uri);
+ filename = path.join(config['www_root'], config['uri']);
}
- fs.stat(filename, function(err, stats) {
+ return fs.stat(filename, function(err, stats) {
if (stats) {
if(stats.isDirectory()) {
if(config['directory_listing'] === true && !path.existsSync(path.join(filename , config['directory_index']))) {
@@ -113,7 +174,6 @@ var ananas = {
catch(e) {
//console.log(e);
}
-
if(stats) {
filename += '.html';
}
@@ -160,7 +220,7 @@ var ananas = {
'<tr><th scope="col">Name</th><th scope="col">Last Modified</th><th scope="col">Size</th><th scope="col">Description</th></tr>' +
'<tr><td class="parent bordertop"><img src="'+mime.mime_type('parent_dir')[1]+'"/><a href="../" title="Parent Directory">Parent Directory</a></td><td class="bordertop">-</td><td class="bordertop">-</td><td class="bordertop">-</td></tr>' +
'';
- var private_uri_regexp_pattern = opts['config']['private_uri'] ? new RegExp(opts['config']['private_uri'], 'ig') : null;
+ var private_uri_regexp_pattern = opts['config']['private_uri'] ? new RegExp(opts['config']['private_uri'], 'ig') : false;
opts['files'].forEach(function(file, i) {
var stat = fs.statSync(path.join(opts['parent'], file)),
is_dir = false,
@@ -224,7 +284,7 @@ var ananas = {
serve_file : function(opts) {
var self = this;
- fs.readFile(opts['filename'], 'binary', function(err, data) {
+ return fs.readFile(opts['filename'], 'binary', function(err, data) {
if(err) {
opts['data'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' +
'<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />' +
View
4 server.js
@@ -1,13 +1,13 @@
/**
*
- * Usage: node server.js [config_file]
+ * Usage: node server.js <config_file>
*
*/
//check if a configuration file is passed as argument
if (!process.argv[2]) {
console.error("\nAnanas Error:\n", "Sorry, no configuration file was passed as argument\n",
- "Usage: node server.js config_file\n", "Exiting...\n"
+ "Usage: node server.js <config_file>\n", "Exiting...\n"
);
process.exit();
} else {
View
11 www-example/localhost.dev/example_dynamic/index.js
@@ -0,0 +1,11 @@
+var example_lib = {
+
+ init : function(opts){
+ // opts : request, config, querystring
+ return JSON.stringify({'config' : opts['config']});
+ }
+
+};
+
+exports.init = function(opts){ return example_lib.init(opts); }
+

0 comments on commit 3cd81c4

Please sign in to comment.