├── index.js
├── .gitmodules
├── examples
├── public
│ ├── lobby.html
│ ├── error
│ │ ├── 404.html
│ │ └── 500.html
│ └── index.html
├── simple_server.js
├── clustering.js
├── clustering_with_sessions.js
├── routes.js
└── sessions.js
├── .travis.yml
├── .gitignore
├── changelist.md
├── docs
├── routes.md
└── benchmarks.md
├── lib
├── util.js
├── redirections.js
├── routes
│ ├── route_data.js
│ └── action.js
├── cluster_server.js
├── config.js
├── index.js
├── session.js
├── routes.js
├── mime.js
└── http_server.js
├── package.json
├── test
└── serveme_tests.js
└── README.md
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib');
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/utyl"]
2 | path = lib/utyl
3 | url = https://github.com/muit/utyl.git
4 |
--------------------------------------------------------------------------------
/examples/public/lobby.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Lobby
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/public/error/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ERROR: 404
5 |
6 | Page not found.
7 |
8 |
--------------------------------------------------------------------------------
/examples/public/error/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ERROR: 500
5 | Internal server error.
6 |
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.12.7'
4 | before_install:
5 | - currentfolder=${PWD##*/}
6 | - if [ "$currentfolder" != 'serveMe' ]; then cd .. && eval "mv $currentfolder serveMe" && cd serveMe; fi
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | lib-cov
3 | *.seed
4 | *.log
5 | *.csv
6 | *.dat
7 | *.out
8 | *.pid
9 | *.gz
10 |
11 | pids
12 | logs
13 | results
14 |
15 | node_modules
16 | npm-debug.log
17 | npm-debug.log.*
18 | *.swp
19 | *.pem
20 | connect.sh
21 | password*
--------------------------------------------------------------------------------
/changelist.md:
--------------------------------------------------------------------------------
1 | # Serveme list of changes
2 | You will find all the new changes ordered by version here.
3 | ### 0.8.2
4 | - Listening callback and event.
5 | - Readme fix
6 |
7 | ### 0.8.1
8 | - Implemented require on the routes
9 | - Route callbacks are now optional
10 | - Testing new features
11 | - Fixes
12 |
13 | ### 0.8
14 | - Implemented multiple route algorithm (Performance)
15 | - Internal documentation (Docs)
16 | - Changed routes protected word "data" to "_action" (Use)
17 | - Core refactorized and simplified (Performance)
18 | - New logging messages (Use)
19 | - More fixes and codestyle
--------------------------------------------------------------------------------
/docs/routes.md:
--------------------------------------------------------------------------------
1 | # Internal route structure
2 | This document defines how do the internal routes work and how is their structure.
3 |
4 |
5 | ### Routes without parameters
6 |
7 | ```javascript
8 | //route: /arg0/arg1
9 |
10 | simpleRoutesDB = {
11 | "/arg0/arg1": {
12 | "_action": new Action(callback)
13 | }
14 | }
15 | ```
16 |
17 | ### Routes with parameters
18 | The path of a route will be split in arguments by "/". Each argument will become a new level on the routeDB.
19 |
20 | ```javascript
21 | //route: /arg0/arg1/:param
22 |
23 | complexRouteDB = {
24 | "arg0": {
25 | "arg1": {
26 | "_action": new Action(callback)
27 | }
28 | }
29 | }
30 | ```
--------------------------------------------------------------------------------
/examples/simple_server.js:
--------------------------------------------------------------------------------
1 | //Require serve-me package
2 | var ServeMe = require('..');
3 |
4 | //*******************************
5 | // HTTP SERVER
6 | // Only server the html & other files
7 | //*******************************
8 | var port = 3000;
9 | var options = {
10 | directory: "./examples/public",
11 | debug: false,
12 | log: true,
13 | secure: false
14 | };
15 |
16 | //Start the Server
17 | var serveMe = ServeMe(options, port);
18 | serveMe.start();
19 |
20 | //Route example
21 | serveMe.routes.get("/hello", function() {
22 | return "hello world!";
23 | });
24 |
25 | //Event example
26 | serveMe.on("http_request", function() {
27 | console.log("Hey! One more friend...");
28 | });
--------------------------------------------------------------------------------
/lib/util.js:
--------------------------------------------------------------------------------
1 |
2 | //ES6 Temporal implementation
3 | Object.assign = function(target, object) {
4 | if (typeof object == 'undefined') {
5 | return object;
6 | } else if (typeof target == 'undefined') {
7 | return target;
8 | }
9 | var item, tItem;
10 | for (var prop in object) {
11 | item = object[prop];
12 | if (typeof item == 'object' && item !== null) {
13 | tItem = target[prop];
14 | if (typeof tItem == 'object' && tItem !== null) {
15 | target[prop] = Object.assign(tItem, item);
16 | } else {
17 | target[prop] = item;
18 | }
19 | } else if (item !== null) {
20 | target[prop] = item;
21 | }
22 | }
23 | return target;
24 | }
25 |
--------------------------------------------------------------------------------
/lib/redirections.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /******************************
4 | * ServeMe
5 | * Simple Web Server.
6 | ******************************/
7 |
8 |
9 | /******************************
10 | * Redirections Class
11 | *
12 | * @api public
13 | ******************************/
14 | var Redirection = {
15 | get: function(url, method) {
16 | if (!url) return null;
17 | return this.all[method+"<#?>"+url];
18 | },
19 |
20 | add: function(url, method, redirectionUrl) {
21 | if (!url) return;
22 | this.all[method+"<#?>"+url] = redirectionUrl;
23 | },
24 |
25 | contains: function(url, method) {
26 | return !!this.all[method+"<#?>"+url];
27 | },
28 |
29 | all: {},
30 | };
31 |
32 | module.exports = exports = Redirection;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "serve-me",
3 | "version": "0.8.2",
4 | "description": "Simple NodeJs web server API.",
5 | "license": "MIT",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/muit/serveMe"
9 | },
10 | "homepage": "https://github.com/muit/serveMe",
11 | "scripts": {
12 | "test": "node_modules/mocha/bin/mocha"
13 | },
14 | "keywords": [
15 | "server",
16 | "http",
17 | "https",
18 | "api",
19 | "simple",
20 | "web"
21 | ],
22 | "author": "Muitxer (https://github.com/muit)",
23 | "devDependencies": {
24 | "mocha": "2.0.1",
25 | "request": "2.47.0",
26 | "expect.js": "^0.3.1"
27 | },
28 | "dependencies": {
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/examples/clustering.js:
--------------------------------------------------------------------------------
1 | /*************************
2 | *Clustering Example
3 | *************************/
4 |
5 | //Require serve-me package
6 | var ServeMe = require('..');
7 |
8 | //*******************************
9 | // HTTP SERVER
10 | // Only server the html & other files
11 | //*******************************
12 | var port = 3000;
13 | var options = {
14 | directory: "./examples/public",
15 | debug: false,
16 | log: true,
17 |
18 | cluster: {
19 | //Enabling clustering
20 | enabled: true,
21 |
22 | cpus: "max", //(Optional, Default: "max") Number of cpus of the cluster
23 | auto_reload: true //(Optional, Default: true) Set it to true to reload cluster workers if died
24 | }
25 | };
26 |
27 | //ATENTION: Cluster functionality stills in development. Its stability is limited.
28 |
29 | //Start the Server
30 | var serveMe = ServeMe(options, port);
31 | serveMe.start();
32 |
33 | //Route example
34 | serveMe.routes.get("/hello", function() {
35 | return "hello world!";
36 | });
--------------------------------------------------------------------------------
/lib/routes/route_data.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /**
4 | * Module exports.
5 | */
6 | module.exports = exports = RouteData;
7 |
8 | function RouteData(routes, action) {
9 | this.routes = routes;
10 | this.action = action;
11 | }
12 |
13 | RouteData.prototype = {
14 | routes: null,
15 | action: null,
16 |
17 | require: function(check, fail) {
18 | this.action.addRequire(check, fail);
19 | },
20 |
21 |
22 | //Direct handlers to create new routes
23 | get: function(url, callback) {
24 | return this.add(url, "GET", callback);
25 | },
26 |
27 | set: function(url, callback) {
28 | return this.add(url, "SET", callback);
29 | },
30 |
31 | post: function(url, callback) {
32 | return this.add(url, "POST", callback);
33 | },
34 |
35 | update: function(url, callback) {
36 | return this.add(url, "UPDATE", callback);
37 | },
38 |
39 | patch: function(url, callback) {
40 | return this.add(url, "PATCH", callback);
41 | },
42 |
43 | delete: function(url, callback) {
44 | return this.add(url, "DELETE", callback);
45 | },
46 |
47 | add: function(url, method, callback) {
48 | if (typeof callback == "string") {
49 | //Temporal disable redirections here
50 | //Redirection.add(url, method, callback);
51 | return this.routes;
52 | } else {
53 | return this.routes.add(url, method, callback);
54 | }
55 | },
56 |
57 | };
--------------------------------------------------------------------------------
/lib/routes/action.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | /**
4 | * Module exports.
5 | */
6 | module.exports = exports = Action;
7 |
8 | function Action(cb) {
9 | if(!cb) return;
10 | this.callbacks = (cb instanceof Array)? cb : [cb];
11 | }
12 |
13 | Action.prototype = {
14 | require: null,
15 | callbacks: [],
16 | fails: [],
17 |
18 | execute: function(req, res, next) {
19 | if (this.require && this.require(req, res, next) == false) {
20 |
21 | var len = this.fails.length;
22 |
23 | if (len == 1) {
24 | return this.fails[0](req, res, next);
25 | }
26 |
27 | /* Not yet implemented multiple fails
28 | for(var i = 0; i < len; i++) {
29 | this.fails[i](req, res, next);
30 | }
31 | */
32 | return;
33 | }
34 |
35 | var len = this.callbacks.length;
36 |
37 | if (len == 1) {
38 | return this.callbacks[0](req, res, next);
39 | }
40 |
41 | /* Not yet implemented multiple routes
42 | for(var i = 0; i < len; i++) {
43 | this.callbacks[i](req, res, next);
44 | }
45 | */
46 | return;
47 | },
48 |
49 | addCallback: function(callback) {
50 | if (callback)
51 | this.callbacks.push(callback);
52 | },
53 |
54 | addRequire: function(check, fail) {
55 | this.require = check;
56 | if(!fail) return;
57 |
58 | this.fails = (fail instanceof Array)? fail : [fail];
59 | },
60 |
61 | haveCallbacks: function() {
62 | return this.callbacks.length > 0;
63 | }
64 | }
--------------------------------------------------------------------------------
/examples/clustering_with_sessions.js:
--------------------------------------------------------------------------------
1 | /*************************
2 | *Clustering Example
3 | *************************/
4 |
5 | //Require serve-me package
6 | var ServeMe = require('..');
7 |
8 | //*******************************
9 | // HTTP SERVER
10 | // Only server the html & other files
11 | //*******************************
12 | var port = 3000;
13 | var options = {
14 | directory: "./examples/public",
15 | debug: false,
16 | log: false,
17 |
18 | cluster: {
19 | //Enabling clustering
20 | enabled: true,
21 |
22 | cpus: "max", //(Optional, Default: "max") Number of cpus of the cluster
23 | auto_reload: true //(Optional, Default: true) Set it to true to reload cluster workers if died
24 | },
25 |
26 | session: {
27 | enabled: true,
28 | //OPTIONAL:
29 | //Session url - Sessions will be created when any client visit this url.
30 | new_session_url: "/"
31 | }
32 | };
33 |
34 | //ATENTION: Cluster functionality stills in development. Its stability is limited.
35 |
36 | //Start the Server
37 | var serveMe = ServeMe(options, port);
38 | serveMe.start();
39 |
40 | //New session event
41 | serveMe.on("new_session", function() {
42 | console.log("Hey! One more friend...");
43 | return true;
44 | });
45 |
46 | serveMe.on("session", function() {
47 | console.log("Oh! You again.");
48 | });
49 |
50 | serveMe.on("end_session", function() {
51 | console.log("Bye Bye My friend. ;(");
52 | });
--------------------------------------------------------------------------------
/lib/cluster_server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var cluster = require('cluster');
4 | var os = require('os');
5 |
6 | module.exports = exports = ClusterServer;
7 |
8 | var HttpServer = require('./http_server');
9 |
10 | function ClusterServer(main, port, onStarted) {
11 | //Limit the cluster to the cpus amount
12 | if (main.config.cluster.cpus == "max" || main.config.cluster.cpus >= os.cpus().length) {
13 | main.config.cluster.cpus = os.cpus().length;
14 | }
15 |
16 | this.name = 'ServeMe.Cluster';
17 | this.start(main, port, onStarted);
18 | };
19 |
20 | ClusterServer.prototype.start = function(main, port, onStarted) {
21 | var self = this;
22 |
23 | if (cluster.isMaster) {
24 | for (var i = 0; i < main.config.cluster.cpus; i++) {
25 | cluster.fork();
26 | }
27 |
28 | cluster.on('death', function(worker) {
29 | // Log process deaths.
30 | main.impLog(self.name + '> worker ' + worker.pid + ' died.');
31 |
32 | // If autoRestart is true, spin up another to replace it
33 | if (main.config.cluster.auto_restart) {
34 | main.impLog(self.name + '> Restarting worker thread...');
35 | cluster.fork();
36 | }
37 | });
38 |
39 | cluster.on('fork', function(worker) {
40 | main.impLog(self.name + '> starting worker thread #' + worker.id);
41 | });
42 |
43 | //self.server = new HttpServer(main, port);
44 | } else {
45 | // Worker threads run the server
46 | self.server = new HttpServer(main, port, onStarted);
47 | }
48 | };
49 |
50 | ClusterServer.prototype.stop = function() {
51 | for (var id in cluster.workers) {
52 | cluster.workers[id].kill();
53 | }
54 | };
--------------------------------------------------------------------------------
/examples/routes.js:
--------------------------------------------------------------------------------
1 | //Require serve-me package
2 | var ServeMe = require('..');
3 |
4 | //*******************************
5 | // HTTP SERVER
6 | // Only server the html & other files
7 | //*******************************
8 | var port = 3000;
9 | var options = {
10 | directory: "./examples/public",
11 | debug: false,
12 | log: true,
13 | };
14 |
15 | //Start the server
16 | var server = ServeMe(options, port);
17 |
18 | //Lets count visits!
19 | var counter = 0;
20 |
21 | server.get("/", function() {
22 | counter += 1;
23 | return "" + counter;
24 | });
25 | //Each time localhost:3000/ is visited the counter value is shown.
26 |
27 | //Same routes with different methods
28 | server.get("/user", function() {
29 | return "All the users are here :3";
30 | })
31 | .post("/user", function() { //Routes can be anidated.
32 | return "New user";
33 | });
34 |
35 | //You can use dynamic routes.
36 | server.get("/user/:name/profile/:id", function(req) {
37 | return "This is the profile of " + req.params.name + " with id " + req.params.id;
38 | });
39 |
40 |
41 | //Answer with a different status
42 | server.get("/admin", function() {
43 | return {
44 | status: 403,
45 | body: "Cant access here!"
46 | };
47 | });
48 |
49 | //Need async on your routes?
50 | server.get("/chicken", function(req, next) {
51 | next({
52 | status: 404,
53 | body: "There is no chicken",
54 | });
55 | });
56 |
57 |
58 | server.get("/index.html").require(
59 | function() {
60 | //Authenticated?
61 | return false;
62 | },
63 | function(req, res){
64 | return "Cant enter here!";
65 | }
66 | );
67 |
68 | //Create a fast redirection
69 | server.get("/lobby", "/lobby.html");
70 |
71 | server.start();
--------------------------------------------------------------------------------
/examples/sessions.js:
--------------------------------------------------------------------------------
1 | //Require serve-me package
2 | var ServeMe = require('..');
3 |
4 | //*******************************
5 | // HTTP SERVER
6 | // Only server the html & other files
7 | //*******************************
8 | var port = 3000;
9 | var options = {
10 | home: "index.html",
11 | directory: "./examples/public",
12 | debug: false,
13 | log: false,
14 | session: {
15 | enabled: true,
16 | //OPTIONAL:
17 | //Session url - Sessions will be created when any client visit this url.
18 | new_session_url: "/login",
19 | global_path: true
20 | }
21 | };
22 | // Load ServeMe
23 | var serveMe = ServeMe(options, port);
24 |
25 | var username = "bear",
26 | password = "drowssap";
27 |
28 |
29 | serveMe.on("new_session", function(evt) {
30 | var session = evt.session;
31 | // Will be called each new session petition reaches.
32 | serveMe.log("\nNew user...");
33 | serveMe.log(session.data);
34 | // if user is correct & password too
35 | if (username == session.data.username && password == session.data.password) {
36 | serveMe.log(" " + session.data.username + " has logged in.\n");
37 | return "Happy to see you " + session.data.username; // return true or a string to accept the new session
38 | //The string returned will be the response data.
39 | }
40 | // else return false (or nothing)
41 | serveMe.log(" Couldn't log in.\n");
42 | return false;
43 | });
44 | /**A session will be created visiting that url:
45 | * Name: 'bear'
46 | * Password: 'drowssap'
47 | * localhost:3000/session?user=bear&password=drowssap
48 | *
49 | * If you look then the console, you can see how 'bear' has logged in.
50 | */
51 |
52 | serveMe.on("session", function(evt) {
53 | var session = evt.session;
54 | // Will be called each existing session enters.
55 |
56 | // If you recharge the webpage before, this message will be printed.
57 | serveMe.log(" " + session.data.username + " entered again!");
58 | return "Hi again, " + session.data.username;
59 | });
60 |
61 | // Start the server
62 | serveMe.start(port);
--------------------------------------------------------------------------------
/docs/benchmarks.md:
--------------------------------------------------------------------------------
1 | # Benchmarks
2 |
3 | These are local benchmarks. Will show only the diference between Serve-Me modes, not the global performance of Serve-Me.
4 |
5 | ### Debug Disabled
6 |
7 | | Benchmark | Value |
8 | | :-------------------- |:----------------:|
9 | |Transactions | 55883 hits |
10 | |Availability | 100.00 % |
11 | |Elapsed time | 89.36 secs |
12 | |Data transferred | 955.10 MB |
13 | |Response time | 0.02 secs |
14 | |Transaction rate | 625.35 trans/sec |
15 | |Throughput | 10.69 MB/sec |
16 | |Concurrency | 10.93 |
17 | |Successful transactions| 55887 |
18 | |Failed transactions | 0 |
19 | |Longest transaction | 0.06 |
20 | |Shortest transaction | 0.00 |
21 |
22 |
23 |
24 | ### Debug Enabled
25 |
26 | | Benchmark | Value |
27 | | :-------------------- |:----------------:|
28 | |Transactions | 48285 hits |
29 | |Availability | 100.00 % |
30 | |Elapsed time | 89.40 secs |
31 | |Data transferred | 825.22 MB |
32 | |Response time | 0.02 secs |
33 | |Transaction rate | 540.09 trans/sec |
34 | |Throughput | 10.89 MB/sec |
35 | |Concurrency | 10.89 |
36 | |Successful transactions| 48287 |
37 | |Failed transactions | 0 |
38 | |Longest transaction | 0.06 |
39 | |Shortest transaction | 0.00 |
40 |
41 |
42 | ### Cluster(beta) Enabled & Debug Disabled
43 |
44 | ATENTION: This benchmark is using cluster in beta version (0.4.3). Results may change.
45 |
46 | | Benchmark | Value |
47 | | :-------------------- |:----------------:|
48 | |Transactions | 60744 hits |
49 | |Availability | 100.00 % |
50 | |Elapsed time | 89.37 secs |
51 | |Data transferred | 1038.16 MB |
52 | |Response time | 0.02 secs |
53 | |Transaction rate | 679.68 trans/sec |
54 | |Throughput | 11.62 MB/sec |
55 | |Concurrency | 12.51 |
56 | |Successful transactions| 60747 |
57 | |Failed transactions | 0 |
58 | |Longest transaction | 0.14 |
59 | |Shortest transaction | 0.00 |
--------------------------------------------------------------------------------
/lib/config.js:
--------------------------------------------------------------------------------
1 | module.exports = exports = Config;
2 |
3 | function Config(options) {
4 | if (!(this instanceof Config)) {
5 | return Config.instance = new Config(options);
6 | }
7 |
8 | options = options || {};
9 | for (p in options) {
10 | this[p] = options[p];
11 | }
12 |
13 | this.hostname = process.env[this.hostname_env_var]
14 |
15 | //Default Values:
16 | this.log = (options.log === undefined) ? true : options.log;
17 | this.debug = options.debug || false;
18 | this.home = options.home || "index.html";
19 | this.directory = generate_public_path(options.directory || "./public");
20 | this.favicon = options.favicon || "/favicon.ico";
21 |
22 |
23 | //Https defaults
24 | this.secure = options.secure || false;
25 | this.key = options.key || "./keys/key.pem";
26 | this.cert = options.cert || "./keys/cert.pem";
27 | this.error = options.error || {
28 | 404: "404.html",
29 | 500: "500.html",
30 | };
31 | this.cluster = options.cluster || {
32 | enabled: false,
33 | cpus: 1
34 | };
35 |
36 |
37 | //Session defaults
38 | this.session = options.session || {
39 | enabled: false,
40 | persistence: false,
41 | lifetime: 7200,
42 | new_session_url: "/session",
43 | global_path: false
44 | };
45 | this.session.enabled = this.session.enabled || false;
46 | this.session.persistence = this.session.persistence || false;
47 | this.session.lifetime = this.session.lifetime || 7200;
48 | this.session.new_session_url = this.session.new_session_url || "/session";
49 | this.session.global_path = this.session.global_path || false;
50 |
51 |
52 | //Cluster defaults
53 | this.cluster = options.cluster || {
54 | enabled: false,
55 | cpus: "max",
56 | auto_restart: true
57 | };
58 | this.cluster.enabled = this.cluster.enabled || false;
59 | this.cluster.cpus = this.cluster.cpus || "max";
60 | this.cluster.auto_restart = this.cluster.auto_restart || true;
61 | }
62 |
63 | /**
64 | * @api private
65 | */
66 | function generate_public_path(relative_path) {
67 | if (typeof relative_path === "string") {
68 | if (relative_path[0] === ".") {
69 | return [process.cwd().replace("\\", "/"), relative_path.slice(1)].join('');
70 | }
71 | }
72 | return relative_path;
73 | }
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Module dependencies.
3 | */
4 | var ClusterServer = require("./cluster_server"),
5 | HttpServer = require('./http_server'),
6 | Routes = require("./routes"),
7 | Redirection = require("./redirections"),
8 | Sessions = require("./session"),
9 | Config = require("./config"),
10 | Package = require('../package.json');
11 |
12 | /**
13 | * Module exports.
14 | */
15 | module.exports = ServeMe;
16 |
17 | /**
18 | * Server constructor.
19 | *
20 | * @param {Number} port
21 | * @param {Object} options
22 | * @api public
23 | */
24 | function ServeMe(options, port) {
25 | if (!(this instanceof ServeMe))
26 | return ServeMe.instance = new ServeMe(options, port);
27 |
28 | this.config = (options instanceof Config) ? options : new Config(options);
29 | Sessions(this);
30 |
31 | if (Routes) {
32 | this.routes = new Routes(this);
33 | }
34 | this.Redirection = Redirection;
35 |
36 | this.port = port;
37 | var self = this;
38 |
39 | this.version = Package.version;
40 | }
41 |
42 | ServeMe.prototype = {
43 | server: null,
44 | /**
45 | * Starts the server
46 | * @param {Number} port [http server port]
47 | * @return {Object} [server object]
48 | */
49 | start: function(port, onStarted) {
50 | //If port is the callback
51 | if(typeof port == "function") {
52 | onStarted = port;
53 | port = null;
54 | }
55 |
56 | if (this.config.cluster.enabled) {
57 | return this.server = new ClusterServer(this, port || this.port, onStarted);
58 | } else {
59 | return this.server = new HttpServer(this, port || this.port, onStarted);
60 | }
61 | },
62 |
63 | /**
64 | * Resets the server
65 | */
66 | reset: ServeMe.reset = function() {
67 | this.stop();
68 | this.start(this.port);
69 | Sessions(this);
70 | },
71 |
72 | /**
73 | * Stops the server
74 | */
75 | stop: function() {
76 | if (this.server) {
77 | this.server.stop();
78 | }
79 |
80 | if (this.routes) {
81 | //this.routes.reset("all");
82 | }
83 |
84 | Sessions.reset();
85 | },
86 |
87 | //Routes
88 | routes: null,
89 | Redirection: null,
90 |
91 | get: function(url, callback) {
92 | return this.add(url, "GET", callback);
93 | },
94 |
95 | set: function(url, callback) {
96 | return this.add(url, "SET", callback);
97 | },
98 |
99 | post: function(url, callback) {
100 | return this.add(url, "POST", callback);
101 | },
102 |
103 | update: function(url, callback) {
104 | return this.add(url, "UPDATE", callback);
105 | },
106 |
107 | patch: function(url, callback) {
108 | return this.add(url, "PATCH", callback);
109 | },
110 |
111 | delete: function(url, callback) {
112 | return this.add(url, "DELETE", callback);
113 | },
114 |
115 | add: function(url, method, callback) {
116 | if (typeof callback == "string") {
117 | Redirection.add(url, method, callback);
118 | return this;
119 | } else {
120 | return this.routes.add(url, method, callback);
121 | }
122 | },
123 |
124 |
125 | //Session
126 | Session: ServeMe.Session = Sessions,
127 |
128 | /**
129 | * On Event
130 | * @param {String} identifier [name of the event]
131 | * @param {Function} callback [method called when event is active]
132 | */
133 | on: function(identifier, callback) {
134 | this.events[identifier] = callback;
135 | },
136 |
137 | /**
138 | * Call Event
139 | * @param {String} identifier [name of the event]
140 | */
141 | call: function(identifier, data) {
142 | var event = this.events[identifier];
143 | if (typeof event == "function") {
144 | return event(data);
145 | }
146 | return undefined;
147 | },
148 |
149 | events: {},
150 |
151 | impLog: function(msg, enter) {
152 | if(msg.replace(" ", "").length > 0)
153 | msg = ">> "+msg;
154 | if(enter)
155 | msg = "\n"+msg;
156 |
157 | console.log(msg);
158 | },
159 |
160 | log: function(msg, enter) {
161 | if (this.config && this.config.log !== false)
162 | this.impLog(msg, enter);
163 | }
164 | };
--------------------------------------------------------------------------------
/lib/session.js:
--------------------------------------------------------------------------------
1 | var urlParser = require('url');
2 |
3 | /**
4 | * Module exports.
5 | */
6 | module.exports = exports = Session;
7 |
8 | var ServeMe;
9 |
10 | function Session(arg, opts) {
11 | if (!(this instanceof Session)) {
12 | ServeMe = arg;
13 |
14 | if (ServeMe.config.session.enabled) {
15 | if (ServeMe.config.session.lifetime) {
16 | ServeMe.config.session.persistence = ServeMe.config.session.persistence ? ServeMe.config.session.persistence : true;
17 | } else {
18 | ServeMe.config.session.persistent = false;
19 | ServeMe.config.session.lifetime = 3600;
20 | }
21 | }
22 | } else {
23 | if (!ServeMe) {
24 | throw new SessionNotLoaded("Session must be loaded.");
25 | }
26 | if (typeof opts != "object") {
27 | throw new IncorrectArgument("Session options cant be a " + typeof opts + ".");
28 | }
29 | if (typeof arg != "string") {
30 | throw new IncorrectArgument("Session id cant be a " + typeof arg + ".");
31 | }
32 |
33 | this.id = arg;
34 | this.data = opts.data || {};
35 | this.path = opts.path;
36 | if (opts.domain != "localhost") {
37 | this.domain = opts.domain;
38 | }
39 | }
40 | }
41 |
42 | Session._sessions = {};
43 | Session.timeout = undefined;
44 |
45 | Session.lookupOrCreate = function(url, request) {
46 | if (!ServeMe.config.session.enabled) {
47 | return;
48 | }
49 |
50 | var id = Session.getIdFromRequest(request, {}),
51 | session,
52 | data;
53 |
54 | if (session = Session._sessions[id]) {
55 | data = ServeMe.call("session", {
56 | session: session
57 | });
58 | } else if (url.pathname == ServeMe.config.session.new_session_url) {
59 | session = new Session(id, {
60 | path: ServeMe.config.session.global_path ? undefined : url.pathname,
61 | data: url.query,
62 | domain: request.domain
63 | });
64 |
65 | var result = ServeMe.call("new_session", {
66 | session: session,
67 | request: request
68 | });
69 |
70 | if (result !== false && result !== undefined) {
71 | Session._sessions[id] = session;
72 | data = result;
73 | } else {
74 | session = undefined;
75 | }
76 | }
77 |
78 | if (!this.timeout) {
79 | this.timeout = setTimeout(Session.cleanUp, 60000);
80 | }
81 | if (session) {
82 | session.expiration = (+new Date()) + ServeMe.config.session.lifetime * 1000;
83 | }
84 |
85 | return {
86 | session: session,
87 | data: data
88 | };
89 | };
90 |
91 | Session.lookup = function(url, request) {
92 | if (!ServeMe.config.session.enabled) {
93 | return;
94 | }
95 |
96 | var id = Session.getIdFromRequest(request, {}),
97 | session;
98 |
99 | if (session = Session._sessions[id]) {
100 | ServeMe.call("session", {
101 | session: session,
102 | request: request
103 | });
104 | }
105 |
106 | if (!this.timeout) {
107 | this.timeout = setTimeout(Session.cleanUp, 60000);
108 | }
109 | if (session) {
110 | session.expiration = (+new Date()) + ServeMe.config.session.lifetime * 1000;
111 | }
112 |
113 | return session;
114 | };
115 |
116 | Session.cleanUp = function() {
117 | ServeMe.log("ServeMe: Cleaning Sessions.")
118 | var now = +new Date();
119 | var next = Infinity;
120 | this.timeout = null;
121 |
122 | for (var id in Session._sessions) {
123 | if (hasOwnProp(Session._sessions, id)) {
124 | var session = Session._sessions[id];
125 | if (session.expiration < now) {
126 | ServeMe.call("end_session", session);
127 | delete session;
128 | } else
129 | next = (next < session.expiration) ? next : session.expiration;
130 | }
131 | }
132 |
133 | if (next < Infinity)
134 | this.timeout = setTimeout(Session.cleanUp, next - (+new Date()) + 1000);
135 | };
136 |
137 | Session.reset = function() {
138 | Session._sessions = {};
139 | ServeMe = undefined;
140 | };
141 |
142 | Session.getIdFromRequest = function(req, opts) {
143 | if (req.headers.cookie) {
144 | var m = /SID=([^ ,;]*)/.exec(req.headers.cookie);
145 | if (m && hasOwnProp(this._sessions, m[1])) {
146 | return m[1];
147 | }
148 | }
149 |
150 | if (opts.sessionID) {
151 | return opts.sessionID;
152 | }
153 | return randomString(64);
154 | };
155 |
156 | Session.prototype.getSetCookieHeaderValue = function() {
157 | var parts = ['SID=' + this.id];
158 | if (this.path) {
159 | parts.push('path=' + this.path);
160 | }
161 |
162 | if (this.domain) {
163 | parts.push('domain=' + this.domain);
164 | }
165 |
166 | if (ServeMe.config.session.persistence ? ServeMe.config.session.persistence : true) {
167 | parts.push('expires=' + msUTCString(this.expiration));
168 | }
169 |
170 | return parts.join('; ');
171 | };
172 |
173 | Session.prototype.destroy = function() {
174 | delete Session._sessions[this.id];
175 | };
176 |
177 |
178 |
179 | function msUTCString(ms) {
180 | return (new Date(ms)).toUTCString();
181 | }
182 |
183 | function hasOwnProp(o, p) {
184 | return Object.prototype.hasOwnProperty.call(o, p);
185 | }
186 |
187 | function randomString(bits) {
188 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
189 | rand,
190 | i,
191 | ret = '';
192 |
193 | while (bits > 0) {
194 | rand = Math.floor(Math.random() * 0x100000000);
195 | for (i = 26; i > 0 && bits > 0; i -= 6, bits -= 6)
196 | ret += chars[0x3F & rand >>> i];
197 | }
198 | return ret;
199 | }
200 |
201 | function IncorrectArgument(message) {
202 | this.name = "IncorrectArgument";
203 | this.message = (message || "");
204 | }
205 | IncorrectArgument.prototype = Error.prototype;
206 |
207 | function SessionNotLoaded(message) {
208 | this.name = "SessionNotLoaded";
209 | this.message = (message || "");
210 | }
211 | SessionNotLoaded.prototype = Error.prototype;
--------------------------------------------------------------------------------
/lib/routes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Module exports.
3 | */
4 | module.exports = exports = Routes;
5 |
6 | require("./util");
7 | var Action = require("./routes/action.js");
8 | var RouteData = require("./routes/route_data.js")
9 |
10 | function Routes(core) {
11 | this.core = core;
12 | }
13 |
14 | Routes.prototype = {
15 | core: null,
16 | simpleRouteDB: {},
17 | complexRouteDB: {},
18 |
19 | add: function(url, method, callback) {
20 | if (typeof url != "string") {
21 | throw new IncorrectArgumentType("Routes.add: Url must be string, not " + typeof url + ".");
22 | }
23 | if (typeof method === "function") {
24 | throw new IncorrectArgumentType("Routes.add: add(url, callback) is decreaped. Use set, get, update, post or delete instead.");
25 | } else if (typeof method != "string") {
26 | throw new IncorrectArgumentType("Routes.add: Method must be string, not " + typeof method + ".");
27 | }
28 |
29 | var action;
30 |
31 | if (urlHaveParams(url)) {
32 | action = this.addComplex(url, method, callback);
33 | } else {
34 | this.simpleRouteDB[url] = this.simpleRouteDB[url] || {
35 | _action: {}
36 | };
37 | action = new Action(callback);
38 | this.simpleRouteDB[url]._action[method] = action;
39 | }
40 |
41 | return new RouteData(this, action);
42 | },
43 |
44 | addComplex: function(url, method, callback) {
45 | var segs = url.split("/");
46 | segs = segs.reverse();
47 |
48 | var hash = {
49 | _action: {},
50 | };
51 | var action = hash._action[method] = new Action(callback);
52 |
53 | var seg, next;
54 | for (var i = 0; i < segs.length; i++) {
55 | seg = segs[i];
56 | if (seg === "") {
57 | continue;
58 | }
59 |
60 | next = {};
61 | if (seg.indexOf(":") === 0) {
62 | seg = seg.replace(":", "");
63 |
64 | if (!next.param) {
65 | next.param = {};
66 | }
67 | next.param = hash;
68 | next.param.paramId = seg;
69 | } else {
70 | next[seg] = hash;
71 | }
72 |
73 | //Next state assign
74 | hash = next;
75 | }
76 | this.complexRouteDB = Object.assign(this.complexRouteDB, hash);
77 |
78 | return action;
79 | },
80 |
81 | get: function(url, callback) {
82 | return this.add(url, "GET", callback);
83 | },
84 | set: function(url, callback) {
85 | return this.add(url, "SET", callback);
86 | },
87 | post: function(url, callback) {
88 | return this.add(url, "POST", callback);
89 | },
90 | update: function(url, callback) {
91 | return this.add(url, "UPDATE", callback);
92 | },
93 | patch: function(url, callback) {
94 | return this.add(url, "PATCH", callback);
95 | },
96 | delete: function(url, callback) {
97 | return this.add(url, "DELETE", callback);
98 | },
99 |
100 | take: function(method, url, params) {
101 | if (typeof url != "string") {
102 | throw new IncorrectArgumentType("Routes.take: Url must be string, not " + typeof url + ".");
103 | }
104 |
105 | var route = this.simpleRouteDB[url];
106 | if (route) {
107 | var action = route._action[method];
108 | if (action) {
109 | action.params = params;
110 | return action;
111 | }
112 | }
113 |
114 | return this.takeComplex(method, url, params);
115 | },
116 |
117 | takeComplex: function(method, url, params) {
118 | var action = null;
119 | params = params || {};
120 |
121 | //Check for the perfect route
122 | var segs = url.split("/");
123 | var seg;
124 | var hash = this.complexRouteDB;
125 | for (var i = 0, len = segs.length; i < len; i++) {
126 | seg = segs[i];
127 |
128 | if (seg === "") {
129 | continue;
130 | }
131 |
132 | if (seg == "_action" || seg == "paramId") {
133 | return route;
134 | }
135 |
136 | if (hash[seg] === undefined) {
137 | //Detect parameters
138 | if (hash.param) {
139 | action = hash.param._action || action;
140 | params[hash.param.paramId] = seg;
141 |
142 | hash = hash.param;
143 | } else {
144 | return action;
145 | }
146 | } else {
147 | hash = hash[seg];
148 | }
149 | }
150 |
151 | if (hash._action) {
152 | //return a route
153 | action = hash._action[method];
154 | }
155 | //return the possible params
156 | if (action) {
157 | action.params = params;
158 | }
159 | return action;
160 | },
161 |
162 | serve: function(url, request, response) {
163 | var action = this.take(request.method, url.pathname, url.query);
164 |
165 | if (action) {
166 | request.params = action.params;
167 | var data = action.execute(request, response, function(data) {
168 | next(response, data);
169 | });
170 | next(response, data);
171 |
172 | return action.haveCallbacks();
173 | }
174 | return false;
175 | },
176 |
177 | reset: function() {
178 | delete this.complexRouteDB;
179 | this.complexRouteDB = {};
180 | delete this.simpleRouteDB;
181 | this.simpleRouteDB = {};
182 | }
183 | };
184 |
185 | function next(res, data) {
186 | if (data == undefined) return;
187 |
188 | var status = data.status || 200;
189 | var body = data.body || JSON.stringify(data.json) || "";
190 |
191 | if (typeof data == "string") {
192 | body = data;
193 | }
194 |
195 | res.writeHead(status, {
196 | 'Content-Length': body.length,
197 | 'Content-Type': data.body || !data.json? 'text/plain' : 'application/json'
198 | });
199 | res.end(body);
200 | }
201 |
202 | function IncorrectArgumentType(message) {
203 | this.name = "IncorrectArgumentType";
204 | this.message = message || "";
205 | }
206 |
207 | IncorrectArgumentType.prototype = Error.prototype;
208 |
209 | function urlHaveParams(url) {
210 | return url.contains("/:");
211 | }
--------------------------------------------------------------------------------
/lib/mime.js:
--------------------------------------------------------------------------------
1 | exports.get = function(extension) {
2 | return mime[extension] || "text/plain";
3 | };
4 |
5 | var mime = {
6 | //Text Files
7 | 'css': 'text/css',
8 | '323': 'text/h323',
9 | 'htm': 'text/html',
10 | 'html': 'text/html',
11 | 'stm': 'text/html',
12 | 'uls': 'text/iuls',
13 | 'bas': 'text/plain',
14 | 'c': 'text/plain',
15 | 'h': 'text/plain',
16 | 'txt': 'text/plain',
17 | 'rtx': 'text/richtext',
18 | 'sct': 'text/scriptlet',
19 | 'tsv': 'text/tab-separated-values',
20 | 'htt': 'text/webviewhtml',
21 | 'htc': 'text/x-component',
22 | 'etx': 'text/x-setext',
23 | 'vcf': 'text/x-vcard',
24 |
25 | //'application Files
26 | 'otf': 'application/octet-stream',
27 | 'ttf': 'application/octet-stream',
28 | 'eot': 'application/vnd.ms-fontobject',
29 | 'evy': 'application/envoy',
30 | 'fif': 'application/fractals',
31 | 'spl': 'application/futuresplash',
32 | 'hta': 'application/hta',
33 | 'acx': 'application/internet-property-stream',
34 | 'hqx': 'application/mac-binhex40',
35 | 'doc': 'application/msword',
36 | 'dot': 'application/msword',
37 | '*': 'application/octet-stream',
38 | 'bin': 'application/octet-stream',
39 | 'class': 'application/octet-stream',
40 | 'dms': 'application/octet-stream',
41 | 'exe': 'application/octet-stream',
42 | 'lha': 'application/octet-stream',
43 | 'lzh': 'application/octet-stream',
44 | 'oda': 'application/oda',
45 | 'axs': 'application/olescript',
46 | 'pdf': 'application/pdf',
47 | 'prf': 'application/pics-rules',
48 | 'p10': 'application/pkcs10',
49 | 'crl': 'application/pkix-crl',
50 | 'ai': 'application/postscript',
51 | 'eps': 'application/postscript',
52 | 'ps': 'application/postscript',
53 | 'rtf': 'application/rtf',
54 | 'setpay':'application/set-payment-initiation',
55 | 'setreg':'application/set-registration-initiation',
56 | 'xla': 'application/vnd.ms-excel',
57 | 'xlc': 'application/vnd.ms-excel',
58 | 'xlm': 'application/vnd.ms-excel',
59 | 'xls': 'application/vnd.ms-excel',
60 | 'xlt': 'application/vnd.ms-excel',
61 | 'xlw': 'application/vnd.ms-excel',
62 | 'msg': 'application/vnd.ms-outlook',
63 | 'sst': 'application/vnd.ms-pkicertstore',
64 | 'cat': 'application/vnd.ms-pkiseccat',
65 | 'stl': 'application/vnd.ms-pkistl',
66 | 'pot': 'application/vnd.ms-powerpoint',
67 | 'pps': 'application/vnd.ms-powerpoint',
68 | 'ppt': 'application/vnd.ms-powerpoint',
69 | 'mpp': 'application/vnd.ms-project',
70 | 'wcm': 'application/vnd.ms-works',
71 | 'wdb': 'application/vnd.ms-works',
72 | 'wks': 'application/vnd.ms-works',
73 | 'wps': 'application/vnd.ms-works',
74 | 'hlp': 'application/winhlp',
75 | 'bcpio': 'application/x-bcpio',
76 | 'cdf': 'application/x-cdf',
77 | 'z': 'application/x-compress',
78 | 'tgz': 'application/x-compressed',
79 | 'cpio': 'application/x-cpio',
80 | 'csh': 'application/x-csh',
81 | 'dcr': 'application/x-director',
82 | 'dir': 'application/x-director',
83 | 'dxr': 'application/x-director',
84 | 'dvi': 'application/x-dvi',
85 | 'gtar': 'application/x-gtar',
86 | 'gz': 'application/x-gzip',
87 | 'hdf': 'application/x-hdf',
88 | 'ins': 'application/x-internet-signup',
89 | 'isp': 'application/x-internet-signup',
90 | 'iii': 'application/x-iphone',
91 | 'js': 'application/x-javascript',
92 | 'mjs': 'application/x-javascript',
93 | 'latex': 'application/x-latex',
94 | 'mdb': 'application/x-msaccess',
95 | 'crd': 'application/x-mscardfile',
96 | 'clp': 'application/x-msclip',
97 | 'dll': 'application/x-msdownload',
98 | 'm13': 'application/x-msmediaview',
99 | 'm14': 'application/x-msmediaview',
100 | 'mvb': 'application/x-msmediaview',
101 | 'wmf': 'application/x-msmetafile',
102 | 'mny': 'application/x-msmoney',
103 | 'pub': 'application/x-mspublisher',
104 | 'scd': 'application/x-msschedule',
105 | 'trm': 'application/x-msterminal',
106 | 'wri': 'application/x-mswrite',
107 | 'ncdf': 'application/x-netcdf',
108 | 'nc': 'application/x-netcdf',
109 | 'pma': 'application/x-perfmon',
110 | 'pmc': 'application/x-perfmon',
111 | 'pml': 'application/x-perfmon',
112 | 'pmr': 'application/x-perfmon',
113 | 'pmw': 'application/x-perfmon',
114 | 'p12': 'application/x-pkcs12',
115 | 'pfx': 'application/x-pkcs12',
116 | 'p7b': 'application/x-pkcs7-certificates',
117 | 'spc': 'application/x-pkcs7-certificates',
118 | 'p7r': 'application/x-pkcs7-certreqresp',
119 | 'p7c': 'application/x-pkcs7-mime',
120 | 'p7m': 'application/x-pkcs7-mime',
121 | 'p7s': 'application/x-pkcs7-signature',
122 | 'sh': 'application/x-sh',
123 | 'shar': 'application/x-shar',
124 | 'swf': 'application/x-shockwave-flash',
125 | 'sit': 'application/x-stuffit',
126 | 'sv4cpio':'application/x-sv4cpio',
127 | 'sv4crc':'application/x-sv4crc',
128 | 'tar': 'application/x-tar',
129 | 'tcl': 'application/x-tcl',
130 | 'tex': 'application/x-tex',
131 | 'texi': 'application/x-texinfo',
132 | 'texinfo':'application/x-texinfo',
133 | 'roff': 'application/x-troff',
134 | 't': 'application/x-troff',
135 | 'tr': 'application/x-troff',
136 | 'man': 'application/x-troff-man',
137 | 'me': 'application/x-troff-me',
138 | 'ms': 'application/x-troff-ms',
139 | 'ustar': 'application/x-ustar',
140 | 'src': 'application/x-wais-source',
141 | 'cer': 'application/x-x509-ca-cert',
142 | 'crt': 'application/x-x509-ca-cert',
143 | 'der': 'application/x-x509-ca-cert',
144 | 'pko': 'application/ynd.ms-pkipko',
145 | 'zip': 'application/zip',
146 |
147 | //Image Files
148 | 'bmp': 'image/bmp',
149 | 'cod': 'image/cis-cod',
150 | 'gif': 'image/gif',
151 | 'ief': 'image/ief',
152 | 'jpe': 'image/jpeg',
153 | 'jpeg': 'image/jpeg',
154 | 'jpg': 'image/jpeg',
155 | 'jfif': 'image/pipeg',
156 | 'svg': 'image/svg+xml',
157 | 'tif': 'image/tiff',
158 | 'tiff': 'image/tiff',
159 | 'ras': 'image/x-cmu-raster',
160 | 'cmx': 'image/x-cmx',
161 | 'ico': 'image/x-icon',
162 | 'pnm': 'image/x-portable-anymap',
163 | 'pbm': 'image/x-portable-bitmap',
164 | 'pgm': 'image/x-portable-graymap',
165 | 'ppm': 'image/x-portable-pixmap',
166 | 'rgb': 'image/x-rgb',
167 | 'xbm': 'image/x-xbitmap',
168 | 'xpm': 'image/x-xpixmap',
169 | 'xwd': 'image/x-xwindowdump',
170 | };
171 |
--------------------------------------------------------------------------------
/lib/http_server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /******************************
4 | * ServeMe
5 | * Simple Web Server.
6 | ******************************/
7 |
8 | var urlParser = require('url');
9 | var fs = require('fs');
10 | var mime = require("./mime.js");
11 | var http = require('http');
12 | var https = require('https');
13 | if (typeof Utyl == "undefined") require("./utyl/utyl.js");
14 |
15 | var ServeMe;
16 | var config;
17 |
18 | module.exports = exports = HttpServer;
19 | /******************************
20 | * Http Server Class
21 | *
22 | * @api public
23 | ******************************/
24 | function HttpServer(main, port, onStarted) {
25 | ServeMe = main;
26 | config = ServeMe.config;
27 |
28 | var self = this;
29 |
30 | if (!port) {
31 | //Default port
32 | port = 8080;
33 | ServeMe.log("Need a port. Using " + port + " by default.");
34 | }
35 |
36 | if (!config.secure) {
37 | this.server = http.createServer(function(request, response) {
38 | self.serve(self, request, response);
39 | });
40 | } else {
41 | if (!config.key) {
42 | throw new Error("ServeMe: SSH Key path is missing.");
43 | }
44 | if (!config.cert) {
45 | throw new Error("ServeMe: SSH Certificate path is missing.");
46 | }
47 |
48 | var ssl = {
49 | key: fs.readFileSync(config.key),
50 | cert: fs.readFileSync(config.cert),
51 | };
52 | this.server = https.createServer(ssl, function(request, response) {
53 | self.serve(self, request, response);
54 | });
55 | }
56 |
57 | this.server.listen(port, config.hostname, function() {
58 | ServeMe.impLog('ServeMe v' + ServeMe.version + "\n----------");
59 | ServeMe.impLog('Listening at port: ' + port);
60 | ServeMe.impLog('Secure mode(https) ' + (config.secure ? "enabled" : "disabled"));
61 | ServeMe.impLog('Debug mode ' + (config.debug ? "enabled" : "disabled"));
62 |
63 | if (config.session.enabled) {
64 | ServeMe.impLog('Sessions ' + config.session.enabled ? "enabled" : "disabled");
65 | }
66 |
67 | //Start Events
68 | if (typeof onStarted == "function") {
69 | onStarted();
70 | }
71 | ServeMe.call("listening");
72 | });
73 |
74 | this.server.on('error', function(e) {
75 | if (!ServeMe.call("error", e)) {
76 | throw new Error("An error ocurred in the server: " + e);
77 | }
78 | });
79 | }
80 |
81 |
82 | HttpServer.prototype = {
83 | /******************************
84 | * Cache Storage
85 | ******************************/
86 | cache: {},
87 |
88 | /******************************
89 | * Serve files or code
90 | * @api private
91 | ******************************/
92 | serve: function(self, request, response) {
93 | ServeMe.call("http_request", {
94 | request: request,
95 | response: response
96 | });
97 |
98 | var url = urlParser.parse(request.url, true);
99 | var client = request.headers['X-Forwarded-For'] || request.connection.remoteAddress;
100 |
101 | ServeMe.log("Serving (" + request.method + ")" + url.pathname + " to " + client, true);
102 | this.sessionBuffer = undefined;
103 |
104 | if (config.session.enabled === true) {
105 | this.sessionBuffer = ServeMe.Session.lookupOrCreate(url, request);
106 | }
107 |
108 | //Route detection
109 | if (self.serveRoute(url, request, response)) {
110 | ServeMe.log(" Served route.");
111 | return;
112 | }
113 |
114 | if (url.pathname.endsWith("/")) {
115 | //Serve a direction
116 | url.pathname += "index.html";
117 | self.serveFile(200, url, request, response);
118 | } else if (url.pathname.contains('..')) {
119 | //Restrict path
120 | self.serveError(404, url, request, response);
121 | } else {
122 | self.serveFile(200, url, request, response);
123 | }
124 | },
125 |
126 | /******************************
127 | * Serve index.html
128 | * @api private
129 | ******************************/
130 | serveHome: function(self, url, request, response) {
131 | url.pathname = '/' + config.home;
132 | self.serveFile(200, url, request, response);
133 | },
134 |
135 | /******************************
136 | * Serve js or css
137 | * @api private
138 | ******************************/
139 | serveFile: function(status, url, request, response) {
140 | var self = this;
141 | var path = config.directory + url.pathname.replace(/%20/g, ' ');
142 |
143 | if (config.debug || this.cache[path] === undefined) {
144 | this.readFile(path, function(err, data) {
145 | if (err) {
146 | ServeMe.log(" Couldn't serve file. 404.");
147 | self.serveError(404, url, request, response);
148 | return;
149 | }
150 | if (!config.debug) {
151 | self.cache[path] = data;
152 | }
153 | ServeMe.log(" Served file from disk.");
154 | writeResponse(status, url, data, request, response);
155 | });
156 | } else {
157 | ServeMe.log(" Served file from cache.");
158 | writeResponse(status, url, this.cache[path], request, response);
159 | }
160 | },
161 |
162 | /**
163 | * Will be called when an error appears
164 | * @api private
165 | */
166 | serveError: function(status, url, request, response) {
167 | if (this.sessionBuffer && this.sessionBuffer.session && this.sessionBuffer.data) {
168 | response.writeHead(200, {
169 | 'Content-Length': this.sessionBuffer.data.length,
170 | 'Content-Type': "text/plain",
171 | 'Set-Cookie': this.sessionBuffer.session.getSetCookieHeaderValue()
172 | });
173 | response.end(this.sessionBuffer.data);
174 | ServeMe.log(" Served Session response.");
175 | return;
176 | }
177 |
178 | if (!config.error[status]) {
179 | ServeMe.log("The error page " + status + " is not defined.\nYou must set 'options.error[" + status + "]' to something!");
180 | return
181 | //throw new Error("The error page " + status + " is not defined.\nYou must set 'options.error[" + status + "]' to something!");
182 | }
183 |
184 | var self = this;
185 | var file = config.error[status];
186 | var path = config.directory + '/error/' + file;
187 |
188 | if (config.debug || this.cache[path] === undefined) {
189 | this.readFile(path, function(err, data) {
190 | if (err) {
191 | writeResponse(500, url, "ERROR: 500 - Internal server error.", request, response, "");
192 | ServeMe.log(status + " error page may not exist.\n Could not found it on: " + path);
193 | return;
194 | //throw new Error(status + " error page may not exist.\nSpecified file path: " + path + "\n");
195 | }
196 |
197 | if (!config.debug)
198 | self.cache[path] = data;
199 |
200 | writeResponse(status, url, data, request, response, file);
201 | });
202 | } else
203 | writeResponse(status, url, this.cache[path], request, response, file);
204 | },
205 |
206 | serveRoute: function(url, request, response) {
207 | var redirectionUrl = ServeMe.Redirection.get(url.pathname, request.method);
208 | if (redirectionUrl) {
209 | url.pathname = redirectionUrl;
210 | this.serveFile(200, url, request, response);
211 |
212 | } else if (ServeMe.routes) {
213 | //Avoid route crash
214 | try {
215 | return ServeMe.routes.serve(url, request, response);
216 | } catch (e) {
217 | console.log(e.stack);
218 | return true;
219 | }
220 | }
221 | return false;
222 | },
223 |
224 | getFileData: function(file, request, response) {
225 | var path = config.directory + file;
226 | ServeMe.log("\nServing " + file);
227 |
228 | if (config.debug || this.cache[path] === undefined) {
229 | var self = this;
230 | this.readFile(path, function(err, data) {
231 | if (err) {
232 | self.serveError(404, undefined, request, response);
233 | ServeMe.log(" Couldn't serve file. 404.");
234 | return undefined;
235 | }
236 | if (!config.debug) {
237 | self.cache[path] = data;
238 | }
239 | ServeMe.log(" Served file from disk.");
240 | return data;
241 | });
242 | } else {
243 | ServeMe.log(" Served file from cache.");
244 | return this.cache[path];
245 | }
246 | },
247 |
248 | readFile: function(path, callback) {
249 | fs.readFile(path, callback);
250 | },
251 |
252 | /******************************
253 | * Stop server
254 | * @api public
255 | ******************************/
256 | stop: function() {
257 | this.server.close();
258 | }
259 | }
260 |
261 | /**
262 | * @api private
263 | */
264 | function writeResponse(status, url, data, request, response, file) {
265 | var type = mime.get((file ? file : url.pathname).split(".").pop()),
266 | head = {
267 | 'Content-Length': data.length,
268 | 'Content-Type': type
269 | };
270 |
271 | response.writeHead(status, head);
272 | response.end(data);
273 | }
--------------------------------------------------------------------------------
/test/serveme_tests.js:
--------------------------------------------------------------------------------
1 | var request = require('request'),
2 | expect = require('expect.js'),
3 | urlParser = require('url');
4 |
5 | describe('ServeMe HttpServer', function() {
6 | var ServeMe;
7 |
8 | before(function(done) {
9 | server = require("..")({
10 | log: false
11 | }, 3000);
12 | done();
13 | });
14 |
15 | it('can be loaded', function(done) {
16 | expect(server).not.to.be(undefined);
17 | expect(server.server).not.to.be(undefined);
18 |
19 | done();
20 | });
21 |
22 | it('can be started', function(done) {
23 | expect(server.start()).not.to.be(null);
24 | done();
25 | });
26 |
27 | it('can be reseted', function(done) {
28 | expect(function() {
29 | server.reset();
30 | }).not.to.throwException();
31 | done();
32 | });
33 |
34 | after(function() {
35 | server.stop();
36 | });
37 | });
38 |
39 | describe('ServeMe config', function() {
40 |
41 | before(function(done) {
42 | process.env['SERVE_ME_TEST_ENV_VAR_IP'] = '192.168.1.1';
43 | done();
44 | });
45 |
46 | after(function(done) {
47 | process.env['SERVE_ME_TEST_ENV_VAR_IP'] = undefined;
48 | done();
49 | });
50 |
51 | it('will load the hostname from an env variable', function(done) {
52 | server = require("..")({
53 | log: false,
54 | hostname_env_var : 'SERVE_ME_TEST_ENV_VAR_IP'
55 | }, 3000);
56 | expect(server).not.to.be(undefined);
57 | expect(server.server).not.to.be(undefined);
58 | expect(server.config.hostname).to.be('192.168.1.1');
59 |
60 | done();
61 | });
62 |
63 | it('will allow an undefined hostname', function(done) {
64 | server = require("..")({
65 | log: false
66 | }, 3000);
67 | expect(server).not.to.be(undefined);
68 | expect(server.server).not.to.be(undefined);
69 | expect(server.config.hostname).to.be(undefined);
70 |
71 | done();
72 | });
73 |
74 | it('will use an undefined hostname if env var is not available', function(done) {
75 | server = require("..")({
76 | log: false,
77 | hostname_env_var : 'SERVE_ME_TEST_ENV_VAR_IP_NOT_EXISTS'
78 | }, 3000);
79 | expect(server).not.to.be(undefined);
80 | expect(server.server).not.to.be(undefined);
81 | expect(server.config.hostname).to.be(undefined);
82 |
83 | done();
84 | });
85 |
86 | });
87 |
88 | describe('ServeMe Routes', function() {
89 | var server;
90 |
91 | before(function(done) {
92 | server = require("..")({
93 | log: false
94 | }, 3000);
95 | done();
96 | });
97 |
98 | it('can add a simple route and gets the callback', function(done) {
99 | var callback = function() {
100 | return "added a route";
101 | };
102 | server.get("/api/user", callback);
103 |
104 | expect(server.routes.simpleRouteDB["/api/user"]._action.GET.callbacks[0])
105 | .to.be(callback);
106 | done();
107 | });
108 |
109 | it('can add a complex route and gets the callback', function(done) {
110 | var callback = function() {
111 | return "added a route";
112 | };
113 | server.get("/api/user/:name", callback);
114 |
115 | expect(server.routes.complexRouteDB.api.user.param._action.GET.callbacks[0])
116 | .to.be(callback);
117 | done();
118 | });
119 |
120 | it('can´t get an uncreated route', function(done) {
121 | var action = server.routes.take("GET", "/user");
122 | expect(isEmpty(action)).to.be(true);
123 | done();
124 | });
125 |
126 | it('can get a simple route', function(done) {
127 | var callback = function() {
128 | return "added a route";
129 | };
130 | server.get("/api/user", callback);
131 |
132 | expect(server.routes.take("GET", "/api/user").callbacks[0]).to.be(callback);
133 | done();
134 | });
135 |
136 | it('can get a complex route', function(done) {
137 | var callback = function() {
138 | return "added a route";
139 | };
140 | server.get("/api/user/:name", callback);
141 |
142 | expect(server.routes.take("GET", "/api/user/:name").callbacks[0]).to.be(callback);
143 | done();
144 | });
145 |
146 | it('can require in a route', function(done) {
147 | var callback = function() {
148 | return "added a route";
149 | };
150 |
151 | var fail = function() {
152 | return "added a fail route";
153 | };
154 |
155 | server.get("/api/user", callback).require(
156 | function(){
157 | return true
158 | }, fail);
159 |
160 | expect(server.routes.take("GET", "/api/user").callbacks[0]).to.be(callback);
161 | expect(server.routes.take("GET", "/api/user").fails[0]).to.be(fail);
162 |
163 | done();
164 | });
165 |
166 | it('can reset all routes', function(done) {
167 | server.routes.get("/foo", function() {
168 | return "got a route";
169 | });
170 |
171 | expect(function() {
172 | server.routes.reset();
173 | }).not.to.throwException();
174 | expect(server.routes.simpleRouteDB).to.be.ok();
175 | expect(server.routes.complexRouteDB).to.be.ok();
176 | done();
177 | });
178 |
179 | it('can receive a route message', function(done) {
180 | server.reset();
181 |
182 | server.routes.get("/hello", function() {
183 | return "Hello World";
184 | });
185 |
186 | request.get('http://localhost:' + 3000 + '/hello', function(err, res, body) {
187 | expect(res.statusCode).to.equal(200);
188 | expect(res.body).to.equal("Hello World");
189 | done();
190 | });
191 | });
192 |
193 | after(function() {
194 | server.stop();
195 | });
196 |
197 | it('can success a require in a route', function(done) {
198 | server.reset();
199 |
200 | var callback = function() { return "added a success route"; };
201 |
202 | var fail = function() { return ""; };
203 |
204 | server.get("/api", callback).require(
205 | function(){
206 | return true;
207 | }, fail);
208 |
209 | request.get('http://localhost:' + 3000 + '/api', function(err, res, body) {
210 | expect(res.statusCode).to.equal(200);
211 | expect(res.body).to.equal("added a success route");
212 | done();
213 | });
214 | });
215 |
216 | it('can fail a require in a route', function(done) {
217 | server.reset();
218 |
219 | var callback = function() { return ""; };
220 |
221 | var fail = function() {
222 | return "added a fail route";
223 | };
224 |
225 | server.get("/api", callback).require(
226 | function(){
227 | return false;
228 | }, fail);
229 |
230 | request.get('http://localhost:' + 3000 + '/api', function(err, res, body) {
231 | expect(res.statusCode).to.equal(200);
232 | expect(res.body).to.equal("added a fail route");
233 | done();
234 | });
235 | });
236 |
237 | it('will set the correct content type for String data', function(done) {
238 | server.reset();
239 |
240 | server.routes.get("/hello", function() {
241 | return "Hello World";
242 | });
243 |
244 | request.get('http://localhost:' + 3000 + '/hello', function(err, res, body) {
245 | expect(res.statusCode).to.equal(200);
246 | expect(res.body).to.equal("Hello World");
247 | expect(res.headers['content-type']).to.equal('text/plain');
248 | done();
249 | });
250 | });
251 |
252 | it('will set the correct content type for JSON data', function(done) {
253 | server.reset();
254 |
255 | server.routes.get("/hello", function() {
256 | return {
257 | json: { test: true }
258 | };
259 | });
260 |
261 | request.get('http://localhost:' + 3000 + '/hello', function(err, res, body) {
262 | expect(res.statusCode).to.equal(200);
263 | expect(res.body).to.equal('{"test":true}');
264 | expect(res.headers['content-type']).to.equal('application/json');
265 | done();
266 | });
267 | });
268 |
269 | });
270 |
271 |
272 | describe('ServeMe Sessions', function() {
273 | var ServeMe;
274 |
275 | before(function(done) {
276 | ServeMe = require("..")({
277 | log: false,
278 | session: {
279 | enabled: true,
280 | persistence: false,
281 | lifetime: 86400
282 | }
283 | });
284 | done();
285 | });
286 |
287 | it('exists', function(done) {
288 | expect(ServeMe.Session).not.to.be(undefined);
289 | done();
290 | });
291 |
292 | it('cant create a session whitout loading Sessions', function(done) {
293 | ServeMe.stop();
294 | ServeMe = require("..");
295 |
296 | expect(function() {
297 | new ServeMe.Session("0", {
298 | path: "/session/login",
299 | domain: "localhost"
300 | });
301 | }).to.throwException();
302 |
303 | done();
304 |
305 | ServeMe = require("..")({
306 | session: {
307 | enabled: true,
308 | persistence: false,
309 | lifetime: 86400
310 | }
311 | });
312 | });
313 |
314 | it('can create a session', function(done) {
315 | var session = new ServeMe.Session("0", {
316 | path: "/session/login",
317 | domain: "localhost"
318 | });
319 | expect(session).to.be.a(ServeMe.Session);
320 |
321 | done();
322 | });
323 |
324 | it('created sessions contains correct arguments', function(done) {
325 | var session = new ServeMe.Session("0", {
326 | path: "/session/login",
327 | domain: "mysite.com"
328 | });
329 | expect(session.path).to.be("/session/login");
330 | expect(session.domain).to.be("mysite.com");
331 |
332 | done();
333 | });
334 |
335 | after(function() {
336 | ServeMe.stop();
337 | });
338 | });
339 |
340 | function isEmpty(obj) {
341 | for(var prop in obj) {
342 | if(obj.hasOwnProperty(prop))
343 | return false;
344 | }
345 | return true;
346 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Serve-Me
2 |
3 | [](https://nodei.co/npm/serve-me/)
4 |
5 | [](https://travis-ci.org/muit/serveMe)
6 | 
7 |
8 | ServeMe is a [DSL](http://en.wikipedia.org/wiki/Domain-specific_language) for creating simple web applications with nodejs.
9 | ```javascript
10 | ServeMe = require('serve-me')();
11 |
12 | ServeMe.start(3000);
13 | ```
14 |
15 | ## Installing
16 |
17 | ```
18 | npm install serve-me
19 | ```
20 |
21 | ## Setting up the options
22 | The options can be set in the ServeMe loading, but by default it will use the folder "./public" and the file "index.html".
23 | ```javascript
24 | //Require the module
25 | ServeMe = require("serve-me");
26 |
27 | //Set the options
28 | var serveMe = ServeMe({
29 | debug: true,
30 | /**If debug mode is enabled, it will load each file again every http request, else the files will wait in cache.
31 | * Also prints more logs
32 | **/
33 |
34 | log: true,
35 | //(Optional) If log is enabled the server reports all the files served and more information.
36 |
37 | home: "mypage.html",
38 | //(Optional) home will change the html file served in "/" (by default: 'index.html')
39 |
40 | directory: "./www",
41 | //(Optional) directory will change the default public folder ('./public')
42 |
43 | error: {
44 | 404: "404.html",
45 | 500: "500.html"
46 | /**Error pages depending on the error code.
47 | * That specified files must exist in the 'public/error' folder.
48 | * Model: 'errorcode': "file.html"
49 | **/
50 | },
51 |
52 | //(Optional)
53 | secure: false,
54 | //Will use https when enabled.
55 | //ATENTION: A key and a certificate must be provided.
56 | //By default serve-me will use:
57 | key: "./keys/key.pem",
58 | cert: "./keys/cert.pem",
59 | });
60 |
61 | //Start the server
62 | serveMe.start(3000);//3000 is the port. Of course you can change it.
63 |
64 | //Also you can add a callback to wait for the server to start.
65 | serveMe.start(3000, function(){
66 | console.log("Is Up!");
67 | });
68 | ```
69 |
70 | ## Routes
71 |
72 | To add specific actions to url paths ServeMe includes Routes.
73 |
74 | Create a route example:
75 | ```javascript
76 | serveMe.get("/hello", function(){
77 | return "Hello World!";
78 | });
79 | ```
80 |
81 | You can create get, post, update or other method routes, and add different behaviours to them.
82 | ```javascript
83 | serveMe.get("/user", function(){
84 | return "I get all the users";
85 | });
86 |
87 | serveMe.post("/user", function(){
88 | return "I create an user";
89 | });
90 | ```
91 |
92 | Routes overview:
93 | ```javascript
94 | serveMe.METHOD("path", function(request, response, next){
95 | var result = "" //Direct response as text
96 |
97 | result = { //Custom response
98 | status: 200, //Result status
99 | body: "", //Response as text
100 | json: {}, //Response as json
101 | }
102 |
103 | next result;
104 | //or
105 | return result;
106 | });
107 | ```
108 |
109 | ### Responding with custom status or json
110 | Just returning an object with some attributes, you will be able to customise your answer easily.
111 | ```javascript
112 | serveMe.get("/api/profile", function(request){
113 | return {
114 | status: 404,
115 |
116 | //return a json
117 | json: {},
118 | //or return a simple text
119 | body: ""
120 | };
121 | });
122 | ```
123 |
124 | PD: You can anidate routes to have a simpler syntax.
125 | ```javascript
126 | serveMe.get("/chicken", function(){
127 | return "fried_chicken";
128 | })
129 | .post("/cat", function(){
130 | return {
131 | json: {
132 | lives: 6
133 | }
134 | };
135 | })
136 | ```
137 |
138 | ### Dynamic routes
139 | Since v0.7.3 the dynamic routes are implemented. And.. nothing is better than some examples.
140 |
141 | ```javascript
142 | serveMe.get("/user/:name", function(req){
143 | return "This is the profile of " + req.params.name;
144 | });
145 |
146 | serveMe.get("/posts/:id", function(req){
147 | return "Post with id " + req.params.id;
148 | });
149 | ```
150 | With the dynamic routes you can have a dinamic content and rest api applications running easily on serve-me.
151 |
152 | ### Redirection Routes
153 | To link urls to a view or file, replace the "callback" of a route with the url:
154 | ```javascript
155 | serveMe.get("/lobby", "/lobby.html");
156 | ```
157 |
158 | ### Require
159 | To make the development easier Serve-me have the require method, witch allows you to execute a route only in some situations.
160 |
161 | ```javascript
162 | function fail(req, res, next) {
163 | return "Failed";
164 | }
165 |
166 | serveMe.get("/profile", doSomething).require( function(){
167 | //returns a boolean: true-> passed the require
168 | return true;
169 | }, fail);
170 | ```
171 | Fail will be calles when the require callback returns false.
172 |
173 |
174 | ### Reset
175 | For specific uses you can reset all the routes:
176 | ```javascript
177 | serveMe.routes.reset();
178 | ```
179 |
180 | ## Events
181 |
182 | To add actions to specific events yo can use the ServeMe Events.
183 | ```javascript
184 | serveMe.on("event_name", function(data){
185 | console.log("I am an event!");
186 | });
187 | ```
188 | "event_name" is the name required to select de action wanted.
189 |
190 | These are the available events for now:
191 | - "listening": Server is started and listening.
192 | - "http_request": Each http connection.
193 | - "new_session": A new session can be created.
194 | - "end_session": An existing session lifetime ends.
195 | - "session": An existing session connects.
196 | - "error": An error appears.
197 |
198 | If you want to create your own event, you can activate it with:
199 | ```javascript
200 | serveMe.call("event_name");
201 | ```
202 |
203 |
204 | ## Sessions
205 |
206 | The sessions have been implemented to facilitate the creation of user sessions or similar tasks, mainly using cookies.
207 |
208 | First of all we need to enable sessions in the serveme options:
209 | ```javascript
210 | var serveMe = ServeMe({
211 | debug: false,
212 |
213 | session:{
214 | enabled: true, //Enable sessions
215 | persistence: true, //if false disables the lifetime, and the session never ends (default true)
216 | lifetime: 86400, //Life of the session in seconds (default: 1 day)
217 | new_session_url: "/session/new", //Url selected to create sessions
218 | //new session petition will be created each visit
219 | }
220 | });
221 | ```
222 |
223 | Then "session/new" will reqistry each visitor in a new session. But one more step is needed. To alow the customization of the session registry you can use the "new_session" event like this:
224 | ```javascript
225 | var username = "bear";
226 | var password = "drowssap";
227 |
228 | serveMe.on("new_session", function(new_session){
229 | //new_session.data contains all the url arguments.
230 | //Login example:
231 | if( new_session.data.username == username &&
232 | new_session.data.password == password)
233 | {
234 | //if there are the correct credentials allow a new session creation, returning true.
235 | return true;
236 | }
237 | //else returning false.
238 | return false;
239 | });
240 | ```
241 |
242 | session.data contains all the url arguments, for example a session request like
243 | ```javascript
244 | /session/new?username=bear&password=drowssap
245 | ```
246 |
247 | will give us that session.data:
248 | ```javascript
249 | >{
250 | > username: "bear",
251 | > password: "drowssap"
252 | >}
253 | ```
254 |
255 | ## Cluster
256 | Cluster if the functionality that allows serve-me to run in multi-threading mode.
257 |
258 | It must be enabled in the initial options:
259 | ```javascript
260 | ServeMe = require("serve-me");
261 |
262 | var serveMe = ServeMe({
263 | cluster: {
264 | // Enabling cluster
265 | enabled: true,
266 |
267 | // Amount of cpus that will be used. By default is "max".
268 | cpus: "max"
269 |
270 | // 'cpus' needs to be a number. If not it will be the maximum amount of cpus ("max")
271 | },
272 | });
273 |
274 | serveMe.start(3000);
275 | ```
276 |
277 | ## Issues
278 |
279 | Let me know your suggestions and bugs found to improve Serve-me [here!](https://github.com/muit/serveMe/issues)!
280 |
281 | [https://github.com/muit/serveMe/issues](https://github.com/muit/serveMe/issues)
282 |
283 | ## License
284 |
285 | The MIT License (MIT)
286 |
287 | Copyright (c) 2014-2015 @muitxer (https://github.com/muit)
288 |
289 | Permission is hereby granted, free of charge, to any person obtaining a copy
290 | of this software and associated documentation files (the "Software"), to deal
291 | in the Software without restriction, including without limitation the rights
292 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
293 | copies of the Software, and to permit persons to whom the Software is
294 | furnished to do so, subject to the following conditions:
295 |
296 | The above copyright notice and this permission notice shall be included in
297 | all copies or substantial portions of the Software.
298 |
299 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
300 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
301 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
302 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
303 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
304 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
305 | THE SOFTWARE.
306 |
307 |
--------------------------------------------------------------------------------
/examples/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Lets make this heavy
7 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
8 |
9 |
10 |
--------------------------------------------------------------------------------