<commit_before>/*jslint node: true, vars: true, nomen: true */
'use strict';

var express = require('express');
var Http = require('http');
var debug = require('debug')('xpl-db:server');
var async = require('async');
var noCache = require('connect-nocache')();
var bodyParser = require('body-parser');
var serve_static = require('serve-static');
var compress = require('compression');

class Server {
  constructor(configuration, store) {
    this.configuration = configuration || {};
    this.store = store;

    this.app = this.configuration.express || express();

    if (this.configuration.compression !== false) {
      try {
        var compression = require('compression');
        this.app.use(compression())
      } catch (x) {
        console.error("No compression module !");
      }
    }
  }

  listen(callback) {
    var app = this.app;

    app.enable('etag');
    app.use(compress());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      extended: true
    }));
//    app.use(noCache);

    app.get(/^\/last\/(.*)$/, this.getLast.bind(this));
    app.get(/^\/history\/(.*)$/, this.getHistory.bind(this));
    app.get(/^\/minMaxAvgSum\/(.*)$/, this.getMinMaxAvgSum.bind(this));

    app.post('/last', this.getLastSet.bind(this));
    app.post('/history', this.getHistorySet.bind(this));
    app.post('/minMaxAvgSum', this.getMinMaxAvgSumSet.bind(this));

    if (this.configuration.configPath) {

      var oneYear = 1000*60*60*24*365;

      app.use(express.static(__dirname + '/public', {  }));

      app.use("/config", serve_static(this.configuration.configPath, {
        index : false,
        maxAge: oneYear
      }));
    }

    app.use(function(req, res, next) {
      res.status(404).send('Sorry cant find that!');
    });

    var server = app.listen(this.configuration.httpPort || 8480, (error) => {
      if (error) {
        console.error("Server can not listen", error);
        return;
      }
      debug("listen", "Server is listening ", server.address());

      callback(error);
    });
  };

  _set(name, request, response, func) {
    var keys = Object.keys(request.body);
    debug("_set", name, "set keys=", keys);

    var options = formatOptions(request);

    var results = {};
    var lastDate=null;

    async.eachLimit(keys, 8, (key, callback) => {
      var reg=/(.*)\/([^/]+)$/.exec(key);
      if (reg) {
        key=reg[1]+"@"+reg[2]; // Transform @ to /
      }

      func.call(this.store, key, options, (error, value) => {
        debug("_set", name, " set key=", key, "value=", value, "error=", error);
        if (error) {
          return callback(error);
        }

        var reg2=/(.*)\@([^@]+)$/.exec(key);
        if (reg2) {
          key=reg2[1]+"/"+reg[2]; // Transform / to @
        }

        results[key] = value;

        if (value.date) {
          if (!lastDate || lastDate<value.date) {
            lastDate=value.date;
          }
        }

        callback();
      });

    }, (error) => {
      if (error) {
        // send 500
        if (error.code === 'NOT_FOUND') {
          response.status(404).body("Key '" + error.key + "' not found");
          return;
        }

        response.status(500).body(String(error));
        return;
      }

      if (lastDate) {
        if (!response.headersSent) {
          var since=request.getHeader('If-Modified-Since');
          console.log("Since=",since);

          if (since && since==lastDate) {
            response.status(304).body("Not modified");
            return;
          }
        }

        response.setHeader("Last-Modified", lastDate.toUTCString());
      }

      response.json(results);
    });
  };

  _get(name, request, response, func) {
    var key = request.params[0];
    debug("_get", "type=", name, "key=", key);

    var reg=/(.*)\/([^/]+)$/.exec(key);
    if (reg) {
      key=reg[1]+"@"+reg[2]; // Transform @ to /
    }

    var options = formatOptions(request);

    func.call(this.store, key, options, (error, values) => {
      debug("_get", "type=", name, "key=", key, "values=", values);
      if (error) {
        // send 500
        if (error.code === 'NOT_FOUND') {
          response.status(404).send('Key not found');
          return;
        }

        response.status(500).send(String(error));
        return;
      }

      response.json(values);
    });
  }

  getLast(request, response) {
    this._get("getLast", request, response, this.store.getLast);
  }

  getHistory(request, response) {
    this._get("getHistory", request, response, this.store.getHistory);
  }

  getMinMaxAvgSum(request, response) {
    this._get("getMinMaxAvgSum", request, response, this.store.getMinMaxAvgSum);
  }

  getLastSet(request, response) {
    this._set("getLastSet", request, response, this.store.getLast);
  }

  getHistorySet(request, response) {
    this._set("getHistorySet", request, response, this.store.getHistory);
  }

  getMinMaxAvgSumSet(request, response) {
    this._set("getMinMaxAvgSumSet", request, response, this.store.getMinMaxAvgSum);
  }
}

function formatOptions(request) {
  var ret = {};

  var query = request.query;
  if (query.limit) {
    ret.limit = parseInt(query.limit, 10);
  }

  if (query.minDate) {
    ret.minDate = new Date(query.minDate);
  }

  if (query.maxDate) {
    ret.maxDate = new Date(query.maxDate);
  }

  if (query.averageMs) {
    ret.averageMs = parseInt(query.averageMs, 10);
  }

  if (query.step) {
    ret.stepMs = parseInt(query.step, 10) * 1000;
  }

  return ret;
}

module.exports = Server;
<commit_msg>Implements last modified<commit_after>/*jslint node: true, vars: true, nomen: true */
'use strict';

var express = require('express');
var Http = require('http');
var debug = require('debug')('xpl-db:server');
var async = require('async');
var noCache = require('connect-nocache')();
var bodyParser = require('body-parser');
var serve_static = require('serve-static');
var compress = require('compression');

class Server {
  constructor(configuration, store) {
    this.configuration = configuration || {};
    this.store = store;

    this.app = this.configuration.express || express();

    if (this.configuration.compression !== false) {
      try {
        var compression = require('compression');
        this.app.use(compression())
      } catch (x) {
        console.error("No compression module !");
      }
    }
  }

  listen(callback) {
    var app = this.app;

    app.enable('etag');
    app.use(compress());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      extended: true
    }));
//    app.use(noCache);

    app.get(/^\/last\/(.*)$/, this.getLast.bind(this));
    app.get(/^\/history\/(.*)$/, this.getHistory.bind(this));
    app.get(/^\/minMaxAvgSum\/(.*)$/, this.getMinMaxAvgSum.bind(this));

    app.post('/last', this.getLastSet.bind(this));
    app.post('/history', this.getHistorySet.bind(this));
    app.post('/minMaxAvgSum', this.getMinMaxAvgSumSet.bind(this));

    if (this.configuration.configPath) {

      var oneYear = 1000*60*60*24*365;

      app.use(express.static(__dirname + '/public', {  }));

      app.use("/config", serve_static(this.configuration.configPath, {
        index : false,
        maxAge: oneYear
      }));
    }

    app.use(function(req, res, next) {
      res.status(404).send('Sorry cant find that!');
    });

    var server = app.listen(this.configuration.httpPort || 8480, (error) => {
      if (error) {
        console.error("Server can not listen", error);
        return;
      }
      debug("listen", "Server is listening ", server.address());

      callback(error);
    });
  };

  _set(name, request, response, func) {
    var keys = Object.keys(request.body);
    debug("_set", name, "set keys=", keys);

    var options = formatOptions(request);

    var results = {};
    var lastDate=null;

    async.eachLimit(keys, 8, (key, callback) => {
      var reg=/(.*)\/([^/]+)$/.exec(key);
      if (reg) {
        key=reg[1]+"@"+reg[2]; // Transform @ to /
      }

      func.call(this.store, key, options, (error, value) => {
        debug("_set", name, " set key=", key, "value=", value, "error=", error);
        if (error) {
          return callback(error);
        }

        var reg2=/(.*)\@([^@]+)$/.exec(key);
        if (reg2) {
          key=reg2[1]+"/"+reg[2]; // Transform / to @
        }

        results[key] = value;

        if (value.date) {
          if (!lastDate || lastDate<value.date) {
            lastDate=value.date;
          }
        }

        callback();
      });

    }, (error) => {
      if (error) {
        // send 500
        if (error.code === 'NOT_FOUND') {
          response.status(404).body("Key '" + error.key + "' not found");
          return;
        }

        response.status(500).body(String(error));
        return;
      }

      if (lastDate) {
        if (!response.headersSent) {
          var since=request.headers['if-modified-since'];
          console.log("Since=",since);

          if (since && since==lastDate) {
            response.status(304).body("Not modified");
            return;
          }
        }

        response.setHeader("Last-Modified", lastDate.toUTCString());
      }

      response.json(results);
    });
  };

  _get(name, request, response, func) {
    var key = request.params[0];
    debug("_get", "type=", name, "key=", key);

    var reg=/(.*)\/([^/]+)$/.exec(key);
    if (reg) {
      key=reg[1]+"@"+reg[2]; // Transform @ to /
    }

    var options = formatOptions(request);

    func.call(this.store, key, options, (error, values) => {
      debug("_get", "type=", name, "key=", key, "values=", values);
      if (error) {
        // send 500
        if (error.code === 'NOT_FOUND') {
          response.status(404).send('Key not found');
          return;
        }

        response.status(500).send(String(error));
        return;
      }

      response.json(values);
    });
  }

  getLast(request, response) {
    this._get("getLast", request, response, this.store.getLast);
  }

  getHistory(request, response) {
    this._get("getHistory", request, response, this.store.getHistory);
  }

  getMinMaxAvgSum(request, response) {
    this._get("getMinMaxAvgSum", request, response, this.store.getMinMaxAvgSum);
  }

  getLastSet(request, response) {
    this._set("getLastSet", request, response, this.store.getLast);
  }

  getHistorySet(request, response) {
    this._set("getHistorySet", request, response, this.store.getHistory);
  }

  getMinMaxAvgSumSet(request, response) {
    this._set("getMinMaxAvgSumSet", request, response, this.store.getMinMaxAvgSum);
  }
}

function formatOptions(request) {
  var ret = {};

  var query = request.query;
  if (query.limit) {
    ret.limit = parseInt(query.limit, 10);
  }

  if (query.minDate) {
    ret.minDate = new Date(query.minDate);
  }

  if (query.maxDate) {
    ret.maxDate = new Date(query.maxDate);
  }

  if (query.averageMs) {
    ret.averageMs = parseInt(query.averageMs, 10);
  }

  if (query.step) {
    ret.stepMs = parseInt(query.step, 10) * 1000;
  }

  return ret;
}

module.exports = Server;
<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var events = require('events');
var express = require('express');
var expressWs = require('express-ws');
var persistify = require('persistify');
var MsgBuilder = require('./msg-builder');
var MsgHandler = require('./msg-handler');
var Connection = require('./connection');

class Server {
	constructor(quad) {
		var that = this;

		this.quad = quad;

		var msgBuilder = new MsgBuilder(quad);
		var msgHandler = new MsgHandler(quad);
		this.msgBuilder = msgBuilder;
		this.msgHandler = msgHandler;

		var app = express();
		expressWs(app); // Enable WebSocket support
		this.app = app;

		this.clients = [];

		app.ws('/socket', function (ws, req) {
			// New client websocket connection
			var conn = new Connection(ws);

			conn.on('message', function (msg) {
				// Process message from client
				var res = msgHandler.handle(msg);

				if (res) {
					conn.send(res);
				}
			});

			that.clients.push(conn); // Add client to list

			conn.on('close', function () {
				// Remove client from list when disconnected
				that.clients.splice(that.clients.indexOf(conn), 1);
			});

			// Send initial data
			conn.send(msgBuilder.enabled());
			conn.send(msgBuilder.appInfo());
			conn.send(msgBuilder.motorsSpeed());
			conn.send(msgBuilder.motorsForces());
			conn.send(msgBuilder.osStats());
			conn.send(msgBuilder.config());
			conn.send(msgBuilder.power());
			conn.send(msgBuilder.orientation());
		});

		app.get('/camera', function (req, res) {
			if (!quad.camera.enabled) {
				return res.status(503).send('503 Service Unavailable');
			}

			res.type('video/h264');
			quad.camera.stream.pipe(res);
		});

		app.ws('/camera/socket', function (ws, req) {
			if (!quad.camera.enabled) {
				return ws.close();
			}

			var playing = true;
			quad.camera.stream.on('data', function (data) {
				if (!playing) return;

				try {
					ws.send(data);
				} catch (err) {
					console.error('Error sending camera data through websocket:', err);
				}
			});
			quad.camera.stream.on('end', function () {
				ws.close();
			});

			ws.on('message', function (msg) {
				msg = msg.toString();
				if (msg == 'play') {
					playing = true;
				}
				if (msg == 'pause') {
					playing = false;
				}
			});
			ws.on('close', function () {
				playing = false;
			});
		});

		var publicDir = __dirname+'/../public';

		// DEV ONLY
		/*app.use('/assets/main.js', browserify({
			entry: publicDir+'/js/index.js',
			onError: function (err) {
				console.warn(err);
			},
			debug: false // True to enable source maps
		}));*/

		var b = persistify({}, { watch: true });
		b.add(publicDir+'/js/index.js');

		app.use('/assets/main.js', function (req, res) {
			res.type('text/javascript');
			b.bundle().pipe(res);
		});

		app.use(express.static(publicDir));

		// Listen for quadcopter events
		quad.on('enabled', function () {
			that.broadcast(msgBuilder.enabled());
		});
		quad.on('power', function () {
			that.broadcast(msgBuilder.power());
		});
		// TODO: other events
	}

	broadcast(msg) {
		this.clients.forEach(function (conn) {
			try {
				conn.send(msg);
			} catch (err) {
				console.error('Cannot broadcast message to client:', err);
			}
		});
	}

	listen() {
		var that = this;

		return new Promise(function (resolve, reject) {
			var server = that.app.listen(process.env.PORT || that.quad.config.server.port, function () {
				console.log('Server listening on port ' + server.address().port);

				// Periodically broadcast status
				that._scheduleStatusBroadcast();

				resolve();
			});

			that.server = server;
		});
	}

	_scheduleStatusBroadcast() {
		var that = this;
		var config = this.quad.config;
		var msgBuilder = this.msgBuilder;

		if (this._osStatusInterval) clearInterval(this._osStatusInterval);
		if (this._orientationInterval) clearInterval(this._orientationInterval);

		this._osStatusInterval = setInterval(function () {
			that.broadcast(msgBuilder.osStats());
		}, config.broadcastInterval.osStatus * 1000);

		this._orientationInterval = setInterval(function () {
			that.broadcast(msgBuilder.orientation());
			that.broadcast(msgBuilder.motorsSpeed());
			that.broadcast(msgBuilder.motorsForces());
		}, config.broadcastInterval.orientation * 1000);
	}
}

module.exports = Server;
<commit_msg>Update server.js<commit_after>'use strict';

var util = require('util');
var events = require('events');
var express = require('express');
var expressWs = require('express-ws');
var persistify = require('persistify');
var MsgBuilder = require('./msg-builder');
var MsgHandler = require('./msg-handler');
var Connection = require('./connection');

class Server {
	constructor(quad) {
		var that = this;

		this.quad = quad;

		var msgBuilder = new MsgBuilder(quad);
		var msgHandler = new MsgHandler(quad);
		this.msgBuilder = msgBuilder;
		this.msgHandler = msgHandler;

		var app = express();
		expressWs(app); // Enable WebSocket support
		this.app = app;

		this.clients = [];

		app.ws('/socket', function (ws, req) {
			// New client websocket connection
			var conn = new Connection(ws);

			conn.on('message', function (msg) {
				// Process message from client
				var res = msgHandler.handle(msg);

				if (res) {
					conn.send(res);
				}
			});

			that.clients.push(conn); // Add client to list

			conn.on('close', function () {
				// Remove client from list when disconnected
				that.clients.splice(that.clients.indexOf(conn), 1);
			});

			// Send initial data
			conn.send(msgBuilder.enabled());
			conn.send(msgBuilder.appInfo());
			conn.send(msgBuilder.motorsSpeed());
			conn.send(msgBuilder.motorsForces());
			conn.send(msgBuilder.osStats());
			conn.send(msgBuilder.config());
			conn.send(msgBuilder.power());
			conn.send(msgBuilder.orientation());
		});

		app.get('/camera', function (req, res) {
			if (!quad.camera.enabled) {
				return res.status(503).send('503 Service Unavailable');
			}

			res.type('video/h264');
			quad.camera.stream.pipe(res);
		});

		app.ws('/camera/socket', function (ws, req) {
			if (!quad.camera.enabled) {
				return ws.close();
			}

			var playing = true;
			quad.camera.stream.on('data', function (data) {
				if (!playing) return;

				try {
					ws.send(data);
				} catch (err) {
					console.error('Error sending camera data through websocket:', err);
				}
			});
			quad.camera.stream.on('end', function () {
				ws.close();
			});

			ws.on('message', function (msg) {
				msg = msg.toString();
				if (msg == 'play') {
					playing = true;
				}
				if (msg == 'pause') {
					playing = false;
				}
			});
			ws.on('close', function () {
				playing = false;
			});
		});

		var publicDir = __dirname+'/../public';

		// DEV ONLY
		/*app.use('/assets/main.js', browserify({
			entry: publicDir+'/js/index.js',
			onError: function (err) {
				console.warn(err);
			},
			debug: false // True to enable source maps
		}));*/

		var b = persistify({}, { watch: true });
		b.add(publicDir+'/js/index.js');

		app.use('/assets/main.js', function (req, res) {
			res.type('text/javascript');
			b.bundle().pipe(res);
		});

		app.use(express.static(publicDir));

		// Listen for quadcopter events
		quad.on('enabled', function () {
			that.broadcast(msgBuilder.enabled());
		});
		quad.on('power', function () {
			that.broadcast(msgBuilder.power());
		});
		// TODO: other events
	}

	broadcast(msg) {
		this.clients.forEach(function (conn) {
			try {
				conn.send(msg);
			} catch (err) {
				console.error('Cannot broadcast message to client:', err);
			}
		});
	}

	listen() {
		var that = this;

		return new Promise(function (resolve, reject) {
			var server = that.app.listen(process.env.PORT || that.quad.config.server.port, function () {
				console.log('Server listening on port ' + server.address().port);

				// Periodically broadcast status
				that._scheduleStatusBroadcast();

				// Refresh broadcast interval on config change
				that.quad.on('config', function () {
					that._scheduleStatusBroadcast();
				});

				resolve();
			});

			that.server = server;
		});
	}

	_scheduleStatusBroadcast() {
		var that = this;
		var config = this.quad.config;
		var msgBuilder = this.msgBuilder;

		if (this._osStatusInterval) clearInterval(this._osStatusInterval);
		if (this._orientationInterval) clearInterval(this._orientationInterval);

		this._osStatusInterval = setInterval(function () {
			that.broadcast(msgBuilder.osStats());
		}, config.broadcastInterval.osStatus * 1000);

		this._orientationInterval = setInterval(function () {
			that.broadcast(msgBuilder.orientation());
			that.broadcast(msgBuilder.motorsSpeed());
			that.broadcast(msgBuilder.motorsForces());
		}, config.broadcastInterval.orientation * 1000);
	}
}

module.exports = Server;
<|endoftext|>


<commit_before>// Native
const server = require('http').Server

// Packages
const getRawBody = require('raw-body')
const typer = require('media-typer')
const isStream = require('isstream')
const Promise = require('bluebird')

const DEV = process.env.NODE_ENV === 'development'
const TESTING = process.env.NODE_ENV === 'test'

const serve = fn => server((req, res) => exports.run(req, res, fn))

module.exports = serve
exports = serve

exports.send = send
exports.sendError = sendError
exports.createError = createError

exports.run = (req, res, fn) =>
  new Promise(resolve => resolve(fn(req, res)))
    .then(val => {
      if (val === null) {
        send(res, 204, null)
        return
      }

      // Send value if it is not undefined, otherwise assume res.end
      // will be called later
      if (undefined !== val) {
        send(res, res.statusCode || 200, val)
      }
    })
    .catch(err => sendError(req, res, err))

// Maps requests to buffered raw bodies so that
// multiple calls to `json` work as expected
const rawBodyMap = new WeakMap()

const parseJSON = str => {
  try {
    return JSON.parse(str)
  } catch (err) {
    throw createError(400, 'Invalid JSON', err)
  }
}

exports.buffer = (req, { limit = '1mb', encoding } = {}) =>
  Promise.resolve().then(() => {
    const type = req.headers['content-type'] || 'text/plain'
    const length = req.headers['content-length']
    encoding = encoding === undefined
      ? typer.parse(type).parameters.charset
      : encoding

    const body = rawBodyMap.get(req)

    if (body) {
      return body
    }

    return getRawBody(req, { limit, length, encoding })
      .then(buf => {
        rawBodyMap.set(req, buf)
        return buf
      })
      .catch(err => {
        if (err.type === 'entity.too.large') {
          throw createError(413, `Body exceeded ${limit} limit`, err)
        } else {
          throw createError(400, 'Invalid body', err)
        }
      })
  })

exports.text = (req, { limit, encoding } = {}) =>
  exports.buffer(req, { limit, encoding }).then(body => body.toString(encoding))

exports.json = (req, opts) =>
  exports.text(req, opts).then(body => parseJSON(body))

function send(res, code, obj = null) {
  res.statusCode = code

  if (obj === null) {
    res.end()
    return
  }

  if (Buffer.isBuffer(obj)) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/octet-stream')
    }

    res.setHeader('Content-Length', obj.length)
    res.end(obj)
    return
  }

  if (isStream(obj)) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/octet-stream')
    }

    obj.pipe(res)
    return
  }

  let str = obj

  if (typeof obj === 'object' || typeof obj === 'number') {
    // We stringify before setting the header
    // in case `JSON.stringify` throws and a
    // 500 has to be sent instead

    // the `JSON.stringify` call is split into
    // two cases as `JSON.stringify` is optimized
    // in V8 if called with only one argument
    if (DEV) {
      str = JSON.stringify(obj, null, 2)
    } else {
      str = JSON.stringify(obj)
    }

    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/json')
    }
  }

  res.setHeader('Content-Length', Buffer.byteLength(str))
  res.end(str)
}

function sendError(req, res, { statusCode, status, message, stack }) {
  statusCode = statusCode || status

  if (statusCode) {
    send(res, statusCode, DEV ? stack : message)
  } else {
    send(res, 500, DEV ? stack : 'Internal Server Error')
  }

  if (!TESTING) {
    console.error(stack)
  }
}

function createError(code, msg, orig) {
  const err = new Error(msg)
  err.statusCode = code
  err.originalError = orig
  return err
}
<commit_msg>🐛 Export `micro` default (#253)<commit_after>// Native
const server = require('http').Server

// Packages
const getRawBody = require('raw-body')
const typer = require('media-typer')
const isStream = require('isstream')
const Promise = require('bluebird')

const DEV = process.env.NODE_ENV === 'development'
const TESTING = process.env.NODE_ENV === 'test'

const serve = fn => server((req, res) => exports.run(req, res, fn))

module.exports = serve
exports = serve
exports.default = serve

exports.send = send
exports.sendError = sendError
exports.createError = createError

exports.run = (req, res, fn) =>
  new Promise(resolve => resolve(fn(req, res)))
    .then(val => {
      if (val === null) {
        send(res, 204, null)
        return
      }

      // Send value if it is not undefined, otherwise assume res.end
      // will be called later
      if (undefined !== val) {
        send(res, res.statusCode || 200, val)
      }
    })
    .catch(err => sendError(req, res, err))

// Maps requests to buffered raw bodies so that
// multiple calls to `json` work as expected
const rawBodyMap = new WeakMap()

const parseJSON = str => {
  try {
    return JSON.parse(str)
  } catch (err) {
    throw createError(400, 'Invalid JSON', err)
  }
}

exports.buffer = (req, { limit = '1mb', encoding } = {}) =>
  Promise.resolve().then(() => {
    const type = req.headers['content-type'] || 'text/plain'
    const length = req.headers['content-length']
    encoding = encoding === undefined
      ? typer.parse(type).parameters.charset
      : encoding

    const body = rawBodyMap.get(req)

    if (body) {
      return body
    }

    return getRawBody(req, { limit, length, encoding })
      .then(buf => {
        rawBodyMap.set(req, buf)
        return buf
      })
      .catch(err => {
        if (err.type === 'entity.too.large') {
          throw createError(413, `Body exceeded ${limit} limit`, err)
        } else {
          throw createError(400, 'Invalid body', err)
        }
      })
  })

exports.text = (req, { limit, encoding } = {}) =>
  exports.buffer(req, { limit, encoding }).then(body => body.toString(encoding))

exports.json = (req, opts) =>
  exports.text(req, opts).then(body => parseJSON(body))

function send(res, code, obj = null) {
  res.statusCode = code

  if (obj === null) {
    res.end()
    return
  }

  if (Buffer.isBuffer(obj)) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/octet-stream')
    }

    res.setHeader('Content-Length', obj.length)
    res.end(obj)
    return
  }

  if (isStream(obj)) {
    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/octet-stream')
    }

    obj.pipe(res)
    return
  }

  let str = obj

  if (typeof obj === 'object' || typeof obj === 'number') {
    // We stringify before setting the header
    // in case `JSON.stringify` throws and a
    // 500 has to be sent instead

    // the `JSON.stringify` call is split into
    // two cases as `JSON.stringify` is optimized
    // in V8 if called with only one argument
    if (DEV) {
      str = JSON.stringify(obj, null, 2)
    } else {
      str = JSON.stringify(obj)
    }

    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', 'application/json')
    }
  }

  res.setHeader('Content-Length', Buffer.byteLength(str))
  res.end(str)
}

function sendError(req, res, { statusCode, status, message, stack }) {
  statusCode = statusCode || status

  if (statusCode) {
    send(res, statusCode, DEV ? stack : message)
  } else {
    send(res, 500, DEV ? stack : 'Internal Server Error')
  }

  if (!TESTING) {
    console.error(stack)
  }
}

function createError(code, msg, orig) {
  const err = new Error(msg)
  err.statusCode = code
  err.originalError = orig
  return err
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
var logging = require("./logging");
var querystring = require("querystring");
var response = require("./response");
var config = require("./config");
var http = require("http");
var mime = require("mime");
var path = require("path");
var url = require("url");
var fs = require("fs");
var server = null;

var routes = {
  index: require("./routes/index"),
  avatars: require("./routes/avatars"),
  skins: require("./routes/skins"),
  renders: require("./routes/renders"),
  capes: require("./routes/capes")
};

// serves assets from lib/public
function asset_request(req, callback) {
  var filename = path.join(__dirname, "public", req.url.path_list.join("/"));
  fs.exists(filename, function(exists) {
    if (exists) {
      fs.readFile(filename, function(err, data) {
        callback({
          body: data,
          type: mime.lookup(filename),
          err: err
        });
      });
    } else {
      callback({});
    }
  });
}

// generates a 12 character random string
function request_id() {
  return Math.random().toString(36).substring(2, 14);
}

// splits a URL path into an Array
// the path is resolved and decoded
function path_list(pathname) {
  // remove trailing and double slashes + other junk

  pathname = path.resolve(pathname);
  var list = pathname.split("/");
  list.shift();
  for (var i = 0; i < list.length; i++) {
    // URL decode
    list[i] = querystring.unescape(list[i]);
  }
  return list;
}

function requestHandler(req, res) {
  req.url = url.parse(req.url, true);
  req.url.query = req.url.query || {};
  req.url.path_list = path_list(req.url.pathname);

  req.id = request_id();
  req.start = Date.now();

  var local_path = req.url.path_list[0];
  logging.log(req.id, req.method, req.url.href);
  if (req.method === "GET" || req.method === "HEAD") {
    try {
      switch (local_path) {
        case "":
        routes.index(req, function(result) {
          response(req, res, result);
        });
        break;
        case "avatars":
        routes.avatars(req, function(result) {
          response(req, res, result);
        });
        break;
        case "skins":
        routes.skins(req, function(result) {
          response(req, res, result);
        });
        break;
        case "renders":
        routes.renders(req, function(result) {
          response(req, res, result);
        });
        break;
        case "capes":
        routes.capes(req, function(result) {
          response(req, res, result);
        });
        break;
        default:
        asset_request(req, function(result) {
          response(req, res, result);
        });
      }
    } catch(e) {
      var error = JSON.stringify(req.headers) + "\n" + e.stack;
      logging.error(req.id + "Error:", error);
      res.writeHead(500, {
        "Content-Type": "text/plain"
      });
      res.end(config.debug_enabled ? error : "Internal Server Error");
    }
  } else {
    res.writeHead(405, {
      "Content-Type": "text/plain"
    });
    res.end("Method Not Allowed");
  }
}

var exp = {};

exp.boot = function(callback) {
  var port = process.env.PORT || 3000;
  var bind_ip = process.env.BIND || "0.0.0.0";
  logging.log("Server running on http://" + bind_ip + ":" + port + "/");
  server = http.createServer(requestHandler).listen(port, bind_ip, function() {
    if (callback) {
      callback();
    }
  });
};

exp.close = function(callback) {
  server.close(function() {
    callback();
  });
};

module.exports = exp;

if (require.main === module) {
  logging.error("Please use 'npm start' or 'www.js'");
  process.exit(1);
}<commit_msg>get rid of path.resolve<commit_after>#!/usr/bin/env node
var logging = require("./logging");
var querystring = require("querystring");
var response = require("./response");
var config = require("./config");
var http = require("http");
var mime = require("mime");
var path = require("path");
var url = require("url");
var fs = require("fs");
var server = null;

var routes = {
  index: require("./routes/index"),
  avatars: require("./routes/avatars"),
  skins: require("./routes/skins"),
  renders: require("./routes/renders"),
  capes: require("./routes/capes")
};

// serves assets from lib/public
function asset_request(req, callback) {
  var filename = path.join(__dirname, "public", req.url.path_list.join("/"));
  fs.exists(filename, function(exists) {
    if (exists) {
      fs.readFile(filename, function(err, data) {
        callback({
          body: data,
          type: mime.lookup(filename),
          err: err
        });
      });
    } else {
      callback({});
    }
  });
}

// generates a 12 character random string
function request_id() {
  return Math.random().toString(36).substring(2, 14);
}

// splits a URL path into an Array
// the path is resolved and decoded
function path_list(pathname) {
  // remove double and trailing slashes
  pathname = pathname.replace(/\/\/+/g, "/").replace(/\/$/, "");
  var list = pathname.split("/");
  list.shift();
  for (var i = 0; i < list.length; i++) {
    // URL decode
    list[i] = querystring.unescape(list[i]);
  }
  return list;
}

function requestHandler(req, res) {
  req.url = url.parse(req.url, true);
  req.url.query = req.url.query || {};
  req.url.path_list = path_list(req.url.pathname);

  req.id = request_id();
  req.start = Date.now();

  var local_path = req.url.path_list[0];
  logging.log(req.id, req.method, req.url.href);
  if (req.method === "GET" || req.method === "HEAD") {
    try {
      switch (local_path) {
        case "":
        routes.index(req, function(result) {
          response(req, res, result);
        });
        break;
        case "avatars":
        routes.avatars(req, function(result) {
          response(req, res, result);
        });
        break;
        case "skins":
        routes.skins(req, function(result) {
          response(req, res, result);
        });
        break;
        case "renders":
        routes.renders(req, function(result) {
          response(req, res, result);
        });
        break;
        case "capes":
        routes.capes(req, function(result) {
          response(req, res, result);
        });
        break;
        default:
        asset_request(req, function(result) {
          response(req, res, result);
        });
      }
    } catch(e) {
      var error = JSON.stringify(req.headers) + "\n" + e.stack;
      logging.error(req.id + "Error:", error);
      res.writeHead(500, {
        "Content-Type": "text/plain"
      });
      res.end(config.debug_enabled ? error : "Internal Server Error");
    }
  } else {
    res.writeHead(405, {
      "Content-Type": "text/plain"
    });
    res.end("Method Not Allowed");
  }
}

var exp = {};

exp.boot = function(callback) {
  var port = process.env.PORT || 3000;
  var bind_ip = process.env.BIND || "0.0.0.0";
  logging.log("Server running on http://" + bind_ip + ":" + port + "/");
  server = http.createServer(requestHandler).listen(port, bind_ip, function() {
    if (callback) {
      callback();
    }
  });
};

exp.close = function(callback) {
  server.close(function() {
    callback();
  });
};

module.exports = exp;

if (require.main === module) {
  logging.error("Please use 'npm start' or 'www.js'");
  process.exit(1);
}<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var qs = require('querystring')
  , parse = require('url').parse
  , readFileSync = require('fs').readFileSync
  , crypto = require('crypto')
  , base64id = require('base64id')
  , transports = require('./transports')
  , EventEmitter = require('events').EventEmitter
  , Socket = require('./socket')
  , WebSocketServer = require('ws').Server
  , debug = require('debug')('engine');

/**
 * Module exports.
 */

module.exports = Server;

/**
 * Server constructor.
 *
 * @param {Object} options
 * @api public
 */

function Server(opts){
  if (!(this instanceof Server)) {
    return new Server(opts);
  }

  this.clients = {};
  this.clientsCount = 0;

  opts = opts || {};
  this.pingTimeout = opts.pingTimeout || 60000;
  this.pingInterval = opts.pingInterval || 25000;
  this.upgradeTimeout = opts.upgradeTimeout || 10000;
  this.maxHttpBufferSize = opts.maxHttpBufferSize || 10E7;
  this.transports = opts.transports || Object.keys(transports);
  this.allowUpgrades = false !== opts.allowUpgrades;
  this.allowRequest = opts.allowRequest;
  this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false;
  this.cookiePath = false !== opts.cookiePath ? (opts.cookiePath || false) : false;
  this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false;
  this.httpCompression = false !== opts.httpCompression ? (opts.httpCompression || {}) : false;

  var self = this;

  // initialize compression options
  ['perMessageDeflate', 'httpCompression'].forEach(function(type) {
    var compression = self[type];
    if (true === compression) self[type] = compression = {};
    if (compression && null == compression.threshold) {
      compression.threshold = 1024;
    }
  });

  // initialize websocket server
  if (~this.transports.indexOf('websocket')) {
    this.ws = new WebSocketServer({
      noServer: true,
      clientTracking: false,
      perMessageDeflate: this.perMessageDeflate
    });
  }
}

/**
 * Protocol errors mappings.
 */

Server.errors = {
  UNKNOWN_TRANSPORT: 0,
  UNKNOWN_SID: 1,
  BAD_HANDSHAKE_METHOD: 2,
  BAD_REQUEST: 3
};

Server.errorMessages = {
  0: 'Transport unknown',
  1: 'Session ID unknown',
  2: 'Bad handshake method',
  3: 'Bad request'
};

/**
 * Inherits from EventEmitter.
 */

Server.prototype.__proto__ = EventEmitter.prototype;

/**
 * Hash of open clients.
 *
 * @api public
 */

Server.prototype.clients;

/**
 * Returns a list of available transports for upgrade given a certain transport.
 *
 * @return {Array}
 * @api public
 */

Server.prototype.upgrades = function(transport){
  if (!this.allowUpgrades) return [];
  return transports[transport].upgradesTo || [];
};

/**
 * Verifies a request.
 *
 * @param {http.ServerRequest}
 * @return {Boolean} whether the request is valid
 * @api private
 */

Server.prototype.verify = function(req, upgrade, fn){
  // transport check
  var transport = req._query.transport;
  if (!~this.transports.indexOf(transport)) {
    debug('unknown transport "%s"', transport);
    return fn(Server.errors.UNKNOWN_TRANSPORT, false);
  }

  // sid check
  var sid = req._query.sid;
  if (sid) {
    if (!this.clients.hasOwnProperty(sid))
      return fn(Server.errors.UNKNOWN_SID, false);
    if (!upgrade && this.clients[sid].transport.name !== transport) {
      debug('bad request: unexpected transport without upgrade');
      return fn(Server.errors.BAD_REQUEST, false);
    }
  } else {
    // handshake is GET only
    if ('GET' != req.method) return fn(Server.errors.BAD_HANDSHAKE_METHOD, false);
    if (!this.allowRequest) return fn(null, true);
    return this.allowRequest(req, fn);
  }

  fn(null, true);
};

/**
 * Prepares a request by processing the query string.
 *
 * @api private
 */

Server.prototype.prepare = function(req){
  // try to leverage pre-existing `req._query` (e.g: from connect)
  if (!req._query) {
    req._query = ~req.url.indexOf('?') ? qs.parse(parse(req.url).query) : {};
  }
};

/**
 * Closes all clients.
 *
 * @api public
 */

Server.prototype.close = function(){
  debug('closing all open clients');
  for (var i in this.clients) {
    if (this.clients.hasOwnProperty(i)) {
      this.clients[i].close();
    }
  }
  return this;
};

/**
 * Handles an Engine.IO HTTP request.
 *
 * @param {http.ServerRequest} request
 * @param {http.ServerResponse|http.OutgoingMessage} response
 * @api public
 */

Server.prototype.handleRequest = function(req, res){
  debug('handling "%s" http request "%s"', req.method, req.url);
  this.prepare(req);
  req.res = res;

  var self = this;
  this.verify(req, false, function(err, success) {
    if (!success) {
      sendErrorMessage(req, res, err);
      return;
    }

    if (req._query.sid) {
      debug('setting new request for existing client');
      self.clients[req._query.sid].transport.onRequest(req);
    } else {
      self.handshake(req._query.transport, req);
    }
  });
};

/**
 * Sends an Engine.IO Error Message
 *
 * @param {http.ServerResponse} response
 * @param {code} error code
 * @api private
 */

 function sendErrorMessage(req, res, code) {
    var headers = { 'Content-Type': 'application/json' };

    if (req.headers.origin) {
      headers['Access-Control-Allow-Credentials'] = 'true';
      headers['Access-Control-Allow-Origin'] = req.headers.origin;
    } else {
      headers['Access-Control-Allow-Origin'] = '*';
    }
    res.writeHead(400, headers);
    res.end(JSON.stringify({
      code: code,
      message: Server.errorMessages[code]
    }));
 }

/**
 * generate a socket id.
 * Overwrite this method to generate your custom socket id
 *
 * @param {Object} request object
 * @api public
 */

Server.prototype.generateId = function(req){
  return base64id.generateId();
};

/**
 * Handshakes a new client.
 *
 * @param {String} transport name
 * @param {Object} request object
 * @api private
 */

Server.prototype.handshake = function(transportName, req){
  var id = this.generateId(req);

  debug('handshaking client "%s"', id);

  try {
    var transport = new transports[transportName](req);
    if ('polling' == transportName) {
      transport.maxHttpBufferSize = this.maxHttpBufferSize;
      transport.httpCompression = this.httpCompression;
    } else if ('websocket' == transportName) {
      transport.perMessageDeflate = this.perMessageDeflate;
    }

    if (req._query && req._query.b64) {
      transport.supportsBinary = false;
    } else {
      transport.supportsBinary = true;
    }
  }
  catch (e) {
    sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST);
    return;
  }
  var socket = new Socket(id, this, transport, req);
  var self = this;

  if (false !== this.cookie) {
    transport.on('headers', function(headers){
      var cookie = self.cookie + '=' + id;
      if(false !== self.cookiePath) {
        cookie += '; path=' + self.cookiePath;
      }
      headers['Set-Cookie'] = cookie;
    });
  }

  transport.onRequest(req);

  this.clients[id] = socket;
  this.clientsCount++;

  socket.once('close', function(){
    delete self.clients[id];
    self.clientsCount--;
  });

  this.emit('connection', socket);
};

/**
 * Handles an Engine.IO HTTP Upgrade.
 *
 * @api public
 */

Server.prototype.handleUpgrade = function(req, socket, upgradeHead){
  this.prepare(req);

  var self = this;
  this.verify(req, true, function(err, success) {
    if (!success) {
      socket.end();
      return;
    }

    var head = new Buffer(upgradeHead.length);
    upgradeHead.copy(head);
    upgradeHead = null;

    // delegate to ws
    self.ws.handleUpgrade(req, socket, head, function(conn){
      self.onWebSocket(req, conn);
    });
  });
};

/**
 * Called upon a ws.io connection.
 *
 * @param {ws.Socket} websocket
 * @api private
 */

Server.prototype.onWebSocket = function(req, socket){
  if (!transports[req._query.transport].prototype.handlesUpgrades) {
    debug('transport doesnt handle upgraded requests');
    socket.close();
    return;
  }

  // get client id
  var id = req._query.sid;

  // keep a reference to the ws.Socket
  req.websocket = socket;

  if (id) {
    var client = this.clients[id];
    if (!client) {
      debug('upgrade attempt for closed client');
      socket.close();
    } else if (client.upgrading) {
      debug('transport has already been trying to upgrade');
      socket.close();
    } else if (client.upgraded) {
      debug('transport had already been upgraded');
      socket.close();
    } else {
      debug('upgrading existing transport');
      var transport = new transports[req._query.transport](req);
      if (req._query && req._query.b64) {
        transport.supportsBinary = false;
      } else {
        transport.supportsBinary = true;
      }
      transport.perMessageDeflate = this.perMessageDeflate;
      client.maybeUpgrade(transport);
    }
  } else {
    this.handshake(req._query.transport, req);
  }
};

/**
 * Captures upgrade requests for a http.Server.
 *
 * @param {http.Server} server
 * @param {Object} options
 * @api public
 */

Server.prototype.attach = function(server, options){
  var self = this;
  var options = options || {};
  var path = (options.path || '/engine.io').replace(/\/$/, '');

  var destroyUpgrade = (options.destroyUpgrade !== undefined) ? options.destroyUpgrade : true;
  var destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000;

  // normalize path
  path += '/';

  function check (req) {
    return path == req.url.substr(0, path.length);
  }

  // cache and clean up listeners
  var listeners = server.listeners('request').slice(0);
  server.removeAllListeners('request');
  server.on('close', self.close.bind(self));

  // add request handler
  server.on('request', function(req, res){
    if (check(req)) {
      debug('intercepting request for path "%s"', path);
      self.handleRequest(req, res);
    } else {
      for (var i = 0, l = listeners.length; i < l; i++) {
        listeners[i].call(server, req, res);
      }
    }
  });

  if(~self.transports.indexOf('websocket')) {
    server.on('upgrade', function (req, socket, head) {
      if (check(req)) {
        self.handleUpgrade(req, socket, head);
      } else if (false !== options.destroyUpgrade) {
        // default node behavior is to disconnect when no handlers
        // but by adding a handler, we prevent that
        // and if no eio thing handles the upgrade
        // then the socket needs to die!
        setTimeout(function() {
           if (socket.writable && socket.bytesWritten <= 0) {
             return socket.end();
           }
        }, options.destroyUpgradeTimeout);
      }
    });
  }
};
<commit_msg>fix tests on node 0.8<commit_after>
/**
 * Module dependencies.
 */

var qs = require('querystring')
  , parse = require('url').parse
  , readFileSync = require('fs').readFileSync
  , crypto = require('crypto')
  , base64id = require('base64id')
  , transports = require('./transports')
  , EventEmitter = require('events').EventEmitter
  , Socket = require('./socket')
  , WebSocketServer = require('ws').Server
  , debug = require('debug')('engine');

/**
 * Module exports.
 */

module.exports = Server;

/**
 * Server constructor.
 *
 * @param {Object} options
 * @api public
 */

function Server(opts){
  if (!(this instanceof Server)) {
    return new Server(opts);
  }

  this.clients = {};
  this.clientsCount = 0;

  opts = opts || {};
  this.pingTimeout = opts.pingTimeout || 60000;
  this.pingInterval = opts.pingInterval || 25000;
  this.upgradeTimeout = opts.upgradeTimeout || 10000;
  this.maxHttpBufferSize = opts.maxHttpBufferSize || 10E7;
  this.transports = opts.transports || Object.keys(transports);
  this.allowUpgrades = false !== opts.allowUpgrades;
  this.allowRequest = opts.allowRequest;
  this.cookie = false !== opts.cookie ? (opts.cookie || 'io') : false;
  this.cookiePath = false !== opts.cookiePath ? (opts.cookiePath || false) : false;
  this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || true) : false;
  this.httpCompression = false !== opts.httpCompression ? (opts.httpCompression || {}) : false;

  var self = this;

  // turn off per message deflate for node 0.8
  // due to it not supporting DeflateRaw#close
  // and thus not working with ws 0.8.x
  if (/^v0\.8\./.test(process.version)) {
    debug('perMessageDeflate not supported by node 0.8');
    this.perMessageDeflate = false;
  }

  // initialize compression options
  ['perMessageDeflate', 'httpCompression'].forEach(function(type) {
    var compression = self[type];
    if (true === compression) self[type] = compression = {};
    if (compression && null == compression.threshold) {
      compression.threshold = 1024;
    }
  });

  // initialize websocket server
  if (~this.transports.indexOf('websocket')) {
    this.ws = new WebSocketServer({
      noServer: true,
      clientTracking: false,
      perMessageDeflate: this.perMessageDeflate
    });
  }
}

/**
 * Protocol errors mappings.
 */

Server.errors = {
  UNKNOWN_TRANSPORT: 0,
  UNKNOWN_SID: 1,
  BAD_HANDSHAKE_METHOD: 2,
  BAD_REQUEST: 3
};

Server.errorMessages = {
  0: 'Transport unknown',
  1: 'Session ID unknown',
  2: 'Bad handshake method',
  3: 'Bad request'
};

/**
 * Inherits from EventEmitter.
 */

Server.prototype.__proto__ = EventEmitter.prototype;

/**
 * Hash of open clients.
 *
 * @api public
 */

Server.prototype.clients;

/**
 * Returns a list of available transports for upgrade given a certain transport.
 *
 * @return {Array}
 * @api public
 */

Server.prototype.upgrades = function(transport){
  if (!this.allowUpgrades) return [];
  return transports[transport].upgradesTo || [];
};

/**
 * Verifies a request.
 *
 * @param {http.ServerRequest}
 * @return {Boolean} whether the request is valid
 * @api private
 */

Server.prototype.verify = function(req, upgrade, fn){
  // transport check
  var transport = req._query.transport;
  if (!~this.transports.indexOf(transport)) {
    debug('unknown transport "%s"', transport);
    return fn(Server.errors.UNKNOWN_TRANSPORT, false);
  }

  // sid check
  var sid = req._query.sid;
  if (sid) {
    if (!this.clients.hasOwnProperty(sid))
      return fn(Server.errors.UNKNOWN_SID, false);
    if (!upgrade && this.clients[sid].transport.name !== transport) {
      debug('bad request: unexpected transport without upgrade');
      return fn(Server.errors.BAD_REQUEST, false);
    }
  } else {
    // handshake is GET only
    if ('GET' != req.method) return fn(Server.errors.BAD_HANDSHAKE_METHOD, false);
    if (!this.allowRequest) return fn(null, true);
    return this.allowRequest(req, fn);
  }

  fn(null, true);
};

/**
 * Prepares a request by processing the query string.
 *
 * @api private
 */

Server.prototype.prepare = function(req){
  // try to leverage pre-existing `req._query` (e.g: from connect)
  if (!req._query) {
    req._query = ~req.url.indexOf('?') ? qs.parse(parse(req.url).query) : {};
  }
};

/**
 * Closes all clients.
 *
 * @api public
 */

Server.prototype.close = function(){
  debug('closing all open clients');
  for (var i in this.clients) {
    if (this.clients.hasOwnProperty(i)) {
      this.clients[i].close();
    }
  }
  return this;
};

/**
 * Handles an Engine.IO HTTP request.
 *
 * @param {http.ServerRequest} request
 * @param {http.ServerResponse|http.OutgoingMessage} response
 * @api public
 */

Server.prototype.handleRequest = function(req, res){
  debug('handling "%s" http request "%s"', req.method, req.url);
  this.prepare(req);
  req.res = res;

  var self = this;
  this.verify(req, false, function(err, success) {
    if (!success) {
      sendErrorMessage(req, res, err);
      return;
    }

    if (req._query.sid) {
      debug('setting new request for existing client');
      self.clients[req._query.sid].transport.onRequest(req);
    } else {
      self.handshake(req._query.transport, req);
    }
  });
};

/**
 * Sends an Engine.IO Error Message
 *
 * @param {http.ServerResponse} response
 * @param {code} error code
 * @api private
 */

 function sendErrorMessage(req, res, code) {
    var headers = { 'Content-Type': 'application/json' };

    if (req.headers.origin) {
      headers['Access-Control-Allow-Credentials'] = 'true';
      headers['Access-Control-Allow-Origin'] = req.headers.origin;
    } else {
      headers['Access-Control-Allow-Origin'] = '*';
    }
    res.writeHead(400, headers);
    res.end(JSON.stringify({
      code: code,
      message: Server.errorMessages[code]
    }));
 }

/**
 * generate a socket id.
 * Overwrite this method to generate your custom socket id
 *
 * @param {Object} request object
 * @api public
 */

Server.prototype.generateId = function(req){
  return base64id.generateId();
};

/**
 * Handshakes a new client.
 *
 * @param {String} transport name
 * @param {Object} request object
 * @api private
 */

Server.prototype.handshake = function(transportName, req){
  var id = this.generateId(req);

  debug('handshaking client "%s"', id);

  try {
    var transport = new transports[transportName](req);
    if ('polling' == transportName) {
      transport.maxHttpBufferSize = this.maxHttpBufferSize;
      transport.httpCompression = this.httpCompression;
    } else if ('websocket' == transportName) {
      transport.perMessageDeflate = this.perMessageDeflate;
    }

    if (req._query && req._query.b64) {
      transport.supportsBinary = false;
    } else {
      transport.supportsBinary = true;
    }
  }
  catch (e) {
    sendErrorMessage(req, req.res, Server.errors.BAD_REQUEST);
    return;
  }
  var socket = new Socket(id, this, transport, req);
  var self = this;

  if (false !== this.cookie) {
    transport.on('headers', function(headers){
      var cookie = self.cookie + '=' + id;
      if(false !== self.cookiePath) {
        cookie += '; path=' + self.cookiePath;
      }
      headers['Set-Cookie'] = cookie;
    });
  }

  transport.onRequest(req);

  this.clients[id] = socket;
  this.clientsCount++;

  socket.once('close', function(){
    delete self.clients[id];
    self.clientsCount--;
  });

  this.emit('connection', socket);
};

/**
 * Handles an Engine.IO HTTP Upgrade.
 *
 * @api public
 */

Server.prototype.handleUpgrade = function(req, socket, upgradeHead){
  this.prepare(req);

  var self = this;
  this.verify(req, true, function(err, success) {
    if (!success) {
      socket.end();
      return;
    }

    var head = new Buffer(upgradeHead.length);
    upgradeHead.copy(head);
    upgradeHead = null;

    // delegate to ws
    self.ws.handleUpgrade(req, socket, head, function(conn){
      self.onWebSocket(req, conn);
    });
  });
};

/**
 * Called upon a ws.io connection.
 *
 * @param {ws.Socket} websocket
 * @api private
 */

Server.prototype.onWebSocket = function(req, socket){
  if (!transports[req._query.transport].prototype.handlesUpgrades) {
    debug('transport doesnt handle upgraded requests');
    socket.close();
    return;
  }

  // get client id
  var id = req._query.sid;

  // keep a reference to the ws.Socket
  req.websocket = socket;

  if (id) {
    var client = this.clients[id];
    if (!client) {
      debug('upgrade attempt for closed client');
      socket.close();
    } else if (client.upgrading) {
      debug('transport has already been trying to upgrade');
      socket.close();
    } else if (client.upgraded) {
      debug('transport had already been upgraded');
      socket.close();
    } else {
      debug('upgrading existing transport');
      var transport = new transports[req._query.transport](req);
      if (req._query && req._query.b64) {
        transport.supportsBinary = false;
      } else {
        transport.supportsBinary = true;
      }
      transport.perMessageDeflate = this.perMessageDeflate;
      client.maybeUpgrade(transport);
    }
  } else {
    this.handshake(req._query.transport, req);
  }
};

/**
 * Captures upgrade requests for a http.Server.
 *
 * @param {http.Server} server
 * @param {Object} options
 * @api public
 */

Server.prototype.attach = function(server, options){
  var self = this;
  var options = options || {};
  var path = (options.path || '/engine.io').replace(/\/$/, '');

  var destroyUpgrade = (options.destroyUpgrade !== undefined) ? options.destroyUpgrade : true;
  var destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000;

  // normalize path
  path += '/';

  function check (req) {
    return path == req.url.substr(0, path.length);
  }

  // cache and clean up listeners
  var listeners = server.listeners('request').slice(0);
  server.removeAllListeners('request');
  server.on('close', self.close.bind(self));

  // add request handler
  server.on('request', function(req, res){
    if (check(req)) {
      debug('intercepting request for path "%s"', path);
      self.handleRequest(req, res);
    } else {
      for (var i = 0, l = listeners.length; i < l; i++) {
        listeners[i].call(server, req, res);
      }
    }
  });

  if(~self.transports.indexOf('websocket')) {
    server.on('upgrade', function (req, socket, head) {
      if (check(req)) {
        self.handleUpgrade(req, socket, head);
      } else if (false !== options.destroyUpgrade) {
        // default node behavior is to disconnect when no handlers
        // but by adding a handler, we prevent that
        // and if no eio thing handles the upgrade
        // then the socket needs to die!
        setTimeout(function() {
           if (socket.writable && socket.bytesWritten <= 0) {
             return socket.end();
           }
        }, options.destroyUpgradeTimeout);
      }
    });
  }
};
<|endoftext|>


<commit_before>/*
 * built-in event emitter for own events.
 */
var EventEmitter = require("events").EventEmitter,
	emitter = new EventEmitter();

/*
 * Required built-in and custom modules
 */

var http = require("http");
var url = require("url");
var qs = require("querystring");
var router = require("./router");
var db = require("./db");
var eq = require("./evqueue");
var api = require("./api");
var util = require("./util");
var time = require("./time")
var async = require("async");
var srv = exports;

util.add_console_log_printing_format();

/*
 * Make api functions public to front module, so can call them from front module server.js:
 *
 * var jsa = require("jsa");
 * jsa.api.docs.create({uid:32342,doc:{test:1}},function(err,ret_val){ ... })
 */

srv.events = {};
srv.config = { 	system:{},
				events:{},
				db:{
					default_catalogs : ["docs", "events", "timers"]
				}
			};
srv.api = { docs:{}, init:{}, events:{}, eq:{} };
srv.eq = { events:{} };
srv.db = db;

for( key in api.remote ){

	(function(){

		var scopekey = key;
		exports.api.docs[scopekey] = function(params, ret_handler){

			api.remote[scopekey](params, ret_handler?ret_handler:function(err,val){

				if(err)
					throw err;
			});
		}
	})();
};

for( key in api.init ){

	(function(){
		var scopekey = key;
		exports.api.init[scopekey] = function(params, ret_handler){

			api.init[scopekey](params, ret_handler);
		}
	})();
};

exports.api.init.rcpts = function( rcpt_handler ){

	api.rcpts = rcpt_handler;
}

/*
 * This function is where all new operations will be added or init stuff will be coded.
 * When all init stuff is done must call ret_handler to signal the end of the init process.
 */
exports.api.init.execute = [

	function( end_handler ){
		/*
		srv.config.events.catalog = {};
		srv.config.events.catalog["docs"].mode = 1; //all event_types have the same event_mode for docs belonging to docs catalog.

		srv.config.events.catalog["docs"].operation = {};
		//catalog op
		srv.config.events.catalog["docs"].operation["create"].mode = 1;
		//doc ops
		srv.config.events.catalog["docs"].operation["join"].mode = 1;
		srv.config.events.catalog["docs"].operation["unjoin"].mode = 1;
		srv.config.events.catalog["docs"].operation["add"].mode = 0;
		srv.config.events.catalog["docs"].operation["remove"].mode = 0;

		//field ops
		srv.config.events.catalog["docs"].operation["set"].mode = 0;
		srv.config.events.catalog["docs"].operation["set"].field = {};
		srv.config.events.catalog["docs"].operation["set"].field["x"].mode = 1;
		srv.config.events.catalog["docs"].operation["set"].field["y"].mode = 1;
		srv.config.events.catalog["docs"].operation["set"].field["z"].mode = 1;

		srv.config.events.catalog["docs"].operation["add"].field["a"].mode = 1;
		srv.config.events.catalog["docs"].operation["add"].field["a.b"].mode = 1;
		srv.config.events.catalog["docs"].operation["add"].field["a.b.c"].mode = 1;

		srv.config.events.catalog["docs"].operation["push"].mode = 1;
		srv.config.events.catalog["docs"].operation["pop"].mode = 1;
		srv.config.events.catalog["docs"].operation["shift"].mode = 1;
		srv.config.events.catalog["docs"].operation["unshift"].mode = 1;

		srv.config.events["create"] = {docs:0};
		*/
		end_handler();
}];

var cancel_default_event = false;
exports.api.events.cancel_default_event = function(){

	cancel_default_event = true;
}

/*
 * Add external handler for api module events. evqueue.js module adds a default handler for
 * all primitive events related to primitive operations: ev_create, ev_join, ev_unjoin,
 * ev_add, ev_rem, ev_set, ev_incr, ev_decr, ev_push, ev_pop, ev_pull.
 */
exports.api.events.on = function( ev_type, ret_handler ){

	api.on(ev_type, ret_handler);
	return exports.api.events;
}

/*
 * Emit event as an api event.
 */
exports.api.events.emit = function( ev_type, params, rcpts ){

	api.emit( ev_type, params, rcpts );
}

/*
 * Register listeners for eventqueue events. Eventqueue event list:
 *
 * ev_eq_push
 */
exports.eq.events.on = function( ev_type, ret_handler ){

	eq.on(ev_type, ret_handler);
	return exports.api.events;
}

/*
 * Emit  event as a evqueue event.
 */
exports.eq.events.emit = function( ev_type, params, rcpts ){

	eq.emit( ev_type, params, rcpts );
}

/*
 * Register listeners to the server events using a delegation pattern for EventEmitter.
 *
 */
exports.events.on = function( ev_type, ret_handler ){

	emitter.on(ev_type, function( params, rcpts ){

		ret_handler( {
						ev_type:ev_type,
						ev_tstamp:new Date().getTime(),
						ev_data:params
					 }, rcpts );
	});
}

/*
 * emit a server-event. Server events list:
 *
 *   -ev_srv_start
 *   -ev_srv_stop
 *   -ev_srv_req
 *
 */
var emit = exports.events.emit =  function( ev_type, params, rcpts ){

	emitter.emit(ev_type, params, rcpts );
}

/*
 * add a new operation to the service
 */
exports.api.newop = function( fname, f_handler ){

	var ev_name = "ev_" + fname;

	api.remote[fname] = function( params, ret_handler){

		exports.api.events[ev_name] = {params:params, rcpts:undefined};

		if( params && params.catalog && params.wid ){ //load the document if parameter wid is supplied

			db.select(params.catalog, params.wid, function(err,doc){

				if(!err && doc){

					params.doc = doc;

					f_handler(params, function(err,val){

							db.save(params.catalog, doc, function(err, doc){

								if(!err){

									ret_handler(null,val);

									if( !cancel_default_event )
										api.emit( ev_name, exports.api.events[ev_name].params , exports.api.events[ev_name].rcpts  );

									cancel_default_event = false;
								}else{

									ret_handler(err,null);
								}
							});
					});
				}else if(!err && !doc){

					ret_handler({code:-7, message:"Document not found: @" + params.catalog + ":" + params.wid }, null);
				}else{

					ret_handler(err,null);
				}
			});
		}else{

			f_handler(params, function(err,val){

				ret_handler(null,val);

				if( !cancel_default_event )
					api.emit( ev_name, exports.api.events[ev_name].params , exports.api.events[ev_name].rcpts  );

				cancel_default_event = false;
			});
		}
	}

	exports.api.docs[fname] = function( params, ret_handler ){

		api.remote[fname](params, ret_handler);
	}

	eq.api.listen( ev_name );
}

/*
 *  termination handler.
 */
function termination_handler(sig) {

   if (typeof sig === "string") {
      console.log("Received %s - terminating Node process.", sig);
      process.exit(1);
   }
   console.log("Node process stopped.");
}

if( process.listeners("exit").length  == 1 ){

	//Process on exit and signals.
	process.on('exit', function() { termination_handler(); });

	//Uncaught exceptions handler to make the server stay running forever.
	process.on("uncaughtException", function(err){

		console.warn( err );

	});

	[
	 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS',
	 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGPIPE', 'SIGTERM'
	].forEach(function(element, index, array) {

	    process.on(element, function() { termination_handler(element); });
	});

}

/*
 * Plots active timers.
 */
exports.timers = function(){

	util.show_timers();
}

/*
 * If srv.config.app is undefined then settings are loaded from file settings.json, otherwise the function
 * prints and/or overwrites these default settings (srv.config.app) with program settings (econfig).
 *
 */
var settings = exports.settings = function( econfig ){

	if( srv.config.app == undefined ){

		try{

			srv.config.app = require("../settings.json");
			srv.config.app.version = require("../package.json").version;
		}catch(err){

			throw new Error("Cannot read config file 'settings.json'");
		}
	}else if( typeof econfig == "object" && econfig.reload == 1){

		try{
			console.log("**Reloading config file 'settings.json'");
			delete require.cache[ process.cwd() + "/settings.json" ];
			delete require.cache[ process.cwd() + "/package.json" ];
			srv.config.app = require("../settings.json");
			srv.config.app.version = require("../package.json").version;
			delete econfig.reload;
		}catch(err){
			throw new Error("Cannot read config file 'settings.json' or 'package.json'");
		}

	}

	if( typeof econfig == "object" ){
		for(key in econfig)
			srv.config.app[key] = econfig[key];
	}

}

/*
 * private function to process incoming request: parse url and route request  based on pathname.
 */
function request_handler(request, response){

	if( request.method == "POST" ){

	    if( request.headers["content-type"] == "application/json" ){

			var data = "";
			var pathname = url.parse(request.url).pathname;
			request.setEncoding("utf8");
			request.on("data", function( chunk ){

			    data += chunk;
			});
			request.on("end", function(){

			    emit("ev_srv_req", data);
			    router.route( pathname, response, data );
			});

	    }else{

			response.writeHead(400, {"Content-Type":"text/html"});
			response.write("400 Bad request.");
			response.end();
	    }
	}else{
	    response.writeHead(405,{"Content-Type":"text/html"});
	    response.write("405 Method not allowed");
	    response.end();
	}
}

srv.config.system = {pid:process.pid, uid:process.getuid(), cwd:process.cwd(), node_version:process.version };

/*
 * start http server and connect driver to db.
 *
 * srv.config.app properties:
 *
 * .port : http port to listen
 * .ipaddr : addr to bind the service
 * .dbhost: db host ip address
 * .dbport : db  port
 * .dbname: database name
 * .dbuser : db connection user.
 * .dbpass : db connection password
 */
var httpsrv = http.createServer(request_handler);
exports.start = function( econfig, ret_handler ){

	//set parameters conveniently if only one parameter is passed.
	if( typeof econfig == "function"){

		ret_handler = econfig;
		econfig = undefined;

	}

	if(httpsrv._handle)
		stop();

	console.log("**System settings:\n\t%s", JSON.stringify(srv.config.system) );
	console.log("**Reading config file 'settings.json'");
	settings(econfig);
	console.log("**App settings:\n\t%s" , JSON.stringify(srv.config.app) );

	db.connect( srv.config.app, function(err,val){

		if( !err ){

			httpsrv.listen( srv.config.app.httpport, srv.config.app.httpaddr, function(){

				console.log("**Httpd listening on %s:%d", srv.config.app.httpaddr, srv.config.app.httpport);

				async.series(
				[
					function(next){

						async.series( srv.api.init.execute, function(err, results){

							console.log("**Executed api.init.execute");
							console.log("**Remote operations: {%s}", Object.keys(srv.api.docs).join());
							next();
						});
					},
					function(next){

						db.prepare(srv.config.db,function(){

							console.log("**Prepared db catalogs: {%s}", srv.config.db.default_catalogs.join() );
							next();
						});
					},
					function(next){

						time.load_timers(function(n){

							console.log("**Loaded %d timers.",n);
							next();
						});
					}
				],function(){

					console.tlog("Server started");
					emit("ev_srv_start");

					if(ret_handler)
						ret_handler();
				});
			});

		}else
			console.tlog(err);
	});

}

/*
 * Stop http server.
 */
var stop = function(){

	if( httpsrv )
		httpsrv.close();
	console.log("Httpd stopped.");
	emit("ev_srv_stop");
}
exports.stop = stop;

<commit_msg>srv.config.runtime.ops.methodname = 1 gets assigned by method introspection at api.remote.<commit_after>/*
 * built-in event emitter for own events.
 */
var EventEmitter = require("events").EventEmitter,
	emitter = new EventEmitter();

/*
 * Required built-in and custom modules
 */

var http = require("http");
var url = require("url");
var qs = require("querystring");
var router = require("./router");
var db = require("./db");
var eq = require("./evqueue");
var api = require("./api");
var util = require("./util");
var time = require("./time")
var async = require("async");
var srv = exports;

util.add_console_log_printing_format();

/*
 * Make api functions public to front module, so can call them from front module server.js:
 *
 * var jsa = require("jsa");
 * jsa.api.docs.create({uid:32342,doc:{test:1}},function(err,ret_val){ ... })
 */

srv.events = {};
srv.config = { 	system:{},
				events:{},
				runtime:{ ops:{} },
				db:{
					default_catalogs : ["docs", "events", "timers"]
				}
			};
srv.api = { docs:{}, init:{}, events:{}, eq:{} };
srv.eq = { events:{} };
srv.db = db;

for( key in api.remote ){

	(function(){

		var scopekey = key;
		srv.config.runtime.ops[scopekey] = 1;
		srv.api.docs[scopekey] = function(params, ret_handler){

			api.remote[scopekey](params, ret_handler?ret_handler:function(err,val){

				if(err)
					throw err;
			});
		}
	})();
};

for( key in api.init ){

	(function(){
		var scopekey = key;
		exports.api.init[scopekey] = function(params, ret_handler){

			api.init[scopekey](params, ret_handler);
		}
	})();
};

exports.api.init.rcpts = function( rcpt_handler ){

	api.rcpts = rcpt_handler;
}

/*
 * This function is where all new operations will be added or init stuff will be coded.
 * When all init stuff is done must call ret_handler to signal the end of the init process.
 */
exports.api.init.execute = [

	function( end_handler ){
		/*
		srv.config.events.catalog = {};
		srv.config.events.catalog["docs"].mode = 1; //all event_types have the same event_mode for docs belonging to docs catalog.

		srv.config.events.catalog["docs"].operation = {};
		//catalog op
		srv.config.events.catalog["docs"].operation["create"].mode = 1;
		//doc ops
		srv.config.events.catalog["docs"].operation["join"].mode = 1;
		srv.config.events.catalog["docs"].operation["unjoin"].mode = 1;
		srv.config.events.catalog["docs"].operation["add"].mode = 0;
		srv.config.events.catalog["docs"].operation["remove"].mode = 0;

		//field ops
		srv.config.events.catalog["docs"].operation["set"].mode = 0;
		srv.config.events.catalog["docs"].operation["set"].field = {};
		srv.config.events.catalog["docs"].operation["set"].field["x"].mode = 1;
		srv.config.events.catalog["docs"].operation["set"].field["y"].mode = 1;
		srv.config.events.catalog["docs"].operation["set"].field["z"].mode = 1;

		srv.config.events.catalog["docs"].operation["add"].field["a"].mode = 1;
		srv.config.events.catalog["docs"].operation["add"].field["a.b"].mode = 1;
		srv.config.events.catalog["docs"].operation["add"].field["a.b.c"].mode = 1;

		srv.config.events.catalog["docs"].operation["push"].mode = 1;
		srv.config.events.catalog["docs"].operation["pop"].mode = 1;
		srv.config.events.catalog["docs"].operation["shift"].mode = 1;
		srv.config.events.catalog["docs"].operation["unshift"].mode = 1;

		srv.config.events["create"] = {docs:0};
		*/

		end_handler();
}];

var cancel_default_event = false;
exports.api.events.cancel_default_event = function(){

	cancel_default_event = true;
}

/*
 * Add external handler for api module events. evqueue.js module adds a default handler for
 * all primitive events related to primitive operations: ev_create, ev_join, ev_unjoin,
 * ev_add, ev_rem, ev_set, ev_incr, ev_decr, ev_push, ev_pop, ev_pull.
 */
exports.api.events.on = function( ev_type, ret_handler ){

	api.on(ev_type, ret_handler);
	return exports.api.events;
}

/*
 * Emit event as an api event.
 */
exports.api.events.emit = function( ev_type, params, rcpts ){

	api.emit( ev_type, params, rcpts );
}

/*
 * Register listeners for eventqueue events. Eventqueue event list:
 *
 * ev_eq_push
 */
exports.eq.events.on = function( ev_type, ret_handler ){

	eq.on(ev_type, ret_handler);
	return exports.api.events;
}

/*
 * Emit  event as a evqueue event.
 */
exports.eq.events.emit = function( ev_type, params, rcpts ){

	eq.emit( ev_type, params, rcpts );
}

/*
 * Register listeners to the server events using a delegation pattern for EventEmitter.
 *
 */
exports.events.on = function( ev_type, ret_handler ){

	emitter.on(ev_type, function( params, rcpts ){

		ret_handler( {
						ev_type:ev_type,
						ev_tstamp:new Date().getTime(),
						ev_data:params
					 }, rcpts );
	});
}

/*
 * emit a server-event. Server events list:
 *
 *   -ev_srv_start
 *   -ev_srv_stop
 *   -ev_srv_req
 *
 */
var emit = exports.events.emit =  function( ev_type, params, rcpts ){

	emitter.emit(ev_type, params, rcpts );
}

/*
 * add a new operation to the service
 */
exports.api.newop = function( fname, f_handler ){

	var ev_name = "ev_" + fname;

	api.remote[fname] = function( params, ret_handler){

		srv.api.events[ev_name] = {params:params, rcpts:undefined};

		if( params && params.catalog && params.wid ){ //load the document if parameter wid is supplied

			db.select(params.catalog, params.wid, function(err,doc){

				if(!err && doc){

					params.doc = doc;

					f_handler(params, function(err,val){

							db.save(params.catalog, doc, function(err, doc){

								if(!err){

									ret_handler(null,val);

									if( !cancel_default_event )
										api.emit( ev_name, exports.api.events[ev_name].params , exports.api.events[ev_name].rcpts  );

									cancel_default_event = false;
								}else{

									ret_handler(err,null);
								}
							});
					});
				}else if(!err && !doc){

					ret_handler({code:-7, message:"Document not found: @" + params.catalog + ":" + params.wid }, null);
				}else{

					ret_handler(err,null);
				}
			});
		}else{

			f_handler(params, function(err,val){

				ret_handler(null,val);

				if( !cancel_default_event )
					api.emit( ev_name, exports.api.events[ev_name].params , exports.api.events[ev_name].rcpts  );

				cancel_default_event = false;
			});
		}
	}

	srv.api.docs[fname] = function( params, ret_handler ){

		api.remote[fname](params, ret_handler);
	}

	eq.api.listen( ev_name );
	srv.config.runtime.ops[fname] = 1;
}

/*
 *  termination handler.
 */
function termination_handler(sig) {

   if (typeof sig === "string") {
      console.log("Received %s - terminating Node process.", sig);
      process.exit(1);
   }
   console.log("Node process stopped.");
}

if( process.listeners("exit").length  == 1 ){

	//Process on exit and signals.
	process.on('exit', function() { termination_handler(); });

	//Uncaught exceptions handler to make the server stay running forever.
	process.on("uncaughtException", function(err){

		console.warn( err );

	});

	[
	 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGBUS',
	 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2', 'SIGPIPE', 'SIGTERM'
	].forEach(function(element, index, array) {

	    process.on(element, function() { termination_handler(element); });
	});

}

/*
 * Plots active timers.
 */
exports.timers = function(){

	util.show_timers();
}

/*
 * If srv.config.app is undefined then settings are loaded from file settings.json, otherwise the function
 * prints and/or overwrites these default settings (srv.config.app) with program settings (econfig).
 *
 */
var settings = exports.settings = function( econfig ){

	if( srv.config.app == undefined ){

		try{

			srv.config.app = require("../settings.json");
			srv.config.app.version = require("../package.json").version;
		}catch(err){

			throw new Error("Cannot read config file 'settings.json'");
		}
	}else if( typeof econfig == "object" && econfig.reload == 1){

		try{
			console.log("**Reloading config file 'settings.json'");
			delete require.cache[ process.cwd() + "/settings.json" ];
			delete require.cache[ process.cwd() + "/package.json" ];
			srv.config.app = require("../settings.json");
			srv.config.app.version = require("../package.json").version;
			delete econfig.reload;
		}catch(err){
			throw new Error("Cannot read config file 'settings.json' or 'package.json'");
		}

	}

	if( typeof econfig == "object" ){
		for(key in econfig)
			if(econfig[key])
				srv.config.app[key] = econfig[key];
	}

}

/*
 * private function to process incoming request: parse url and route request  based on pathname.
 */
function request_handler(request, response){

	if( request.method == "POST" ){

	    if( request.headers["content-type"] == "application/json" ){

			var data = "";
			var pathname = url.parse(request.url).pathname;
			request.setEncoding("utf8");
			request.on("data", function( chunk ){

			    data += chunk;
			});
			request.on("end", function(){

			    emit("ev_srv_req", data);
			    router.route( pathname, response, data );
			});

	    }else{

			response.writeHead(400, {"Content-Type":"text/html"});
			response.write("400 Bad request.");
			response.end();
	    }
	}else{
	    response.writeHead(405,{"Content-Type":"text/html"});
	    response.write("405 Method not allowed");
	    response.end();
	}
}

srv.config.system = {pid:process.pid, uid:process.getuid(), cwd:process.cwd(), node_version:process.version };

/*
 * start http server and connect driver to db.
 *
 * srv.config.app properties:
 *
 * .port : http port to listen
 * .ipaddr : addr to bind the service
 * .dbhost: db host ip address
 * .dbport : db  port
 * .dbname: database name
 * .dbuser : db connection user.
 * .dbpass : db connection password
 */
var httpsrv = http.createServer(request_handler);
exports.start = function( econfig, ret_handler ){

	//set parameters conveniently if only one parameter is passed.
	if( typeof econfig == "function"){

		ret_handler = econfig;
		econfig = undefined;

	}

	if(httpsrv._handle)
		stop();

	console.log("\n\n");
	console.log("**Starting JSA server...");
	console.log("**System settings:\n\t%s", JSON.stringify(srv.config.system) );
	console.log("**Reading config file 'settings.json'");
	settings(econfig);
	console.log("**App settings:\n\t%s" , JSON.stringify(srv.config.app) );

	db.connect( srv.config.app, function(err,val){

		if( !err ){

			httpsrv.listen( srv.config.app.httpport, srv.config.app.httpaddr, function(){

				console.log("**Httpd listening on %s:%d", srv.config.app.httpaddr, srv.config.app.httpport);

				async.series(
				[
					function(next){

						async.series( srv.api.init.execute, function(err, results){

							console.log("**Executed jsa.api.init.execute");
							console.log("**Remote operations: {%s}", Object.keys(srv.api.docs).join());
							next();
						});
					},
					function(next){

						db.prepare(srv.config.db,function(){

							console.log("**Prepared db catalogs: {%s}", srv.config.db.default_catalogs.join() );
							next();
						});
					},
					function(next){

						time.load_remove_timers(function(n){

							console.log("**Loaded %d remove timers.",n);
							next();
						});
					}
				],function(){

					console.tlog("Server started");
					emit("ev_srv_start");

					if(ret_handler)
						ret_handler();
				});
			});

		}else
			console.tlog(err);
	});

}

/*
 * Stop http server.
 */
var stop = function(){

	if( httpsrv )
		httpsrv.close();
	console.log("Httpd stopped.");
	emit("ev_srv_stop");
}
exports.stop = stop;

<|endoftext|>


<commit_before>'use strict';

var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var express = require('express');
var morgan = require('morgan');
var path = require('path');
var normalizeHeaderCase = require("header-case-normalizer");

function fixHeaders(headers) {
	Object.keys(headers).map(key => {
		headers[normalizeHeaderCase(key)] = headers[key];
	});
}

function Server(contentDirectory, lambdaApi, logger) {
	this.logger = logger || console.log;
	this.App = express();
	//Logger
	this.App.use(morgan('dev'));
	this.App.use(cookieParser());

	// Log every request to the console.
	this.App.use((req, res, next) => {
		this.logger("********************************************************************************");
		fixHeaders(req.headers);
		this.logger("Request Headers: " + JSON.stringify(req.headers, null, 2));
		this.logger("----------------------------------");
		next();
	});

	/**********************/
	/*		 API		*/

	var api = express.Router();
	this.App.use('/api/', api);

	api.use(bodyParser.json({ type: '*/*' }));
	api.use((error, req, res, next) => {
		res.status(400).send({error: 'Invalid JSON'});
	});

	api.get('/', (req, res, next) => {
		try {
			Promise.resolve(null)
			.then((data) => {
				res.send({});
				res.end();
			})
			.catch((error) => {
				var message = `Error: ${error}`;
				console.error(message);
				res.status(400).send(message);
			});
		}
		catch (exception) {
			var message = `Error: ${exception}`;
			console.error(message);
			res.status(500).send(message);
		}
	});

	Object.keys(lambdaApi.Routes || []).map(method => {
		Object.keys(lambdaApi.Routes[method]).map(resource => {
			var expressResource = resource.replace(/{proxy\+}/, '*').replace(/{([^\{\}]+)}/g, ':$1');
			api.all(expressResource, (req, res) => {
				var event = {
					resource: resource,
					path: req.path,
					httpMethod: req.method,
					headers: req.headers || {},
					queryStringParameters: req.query,
					pathParameters: req.params || {},
					body: req.body,
					stageVariables: { lambdaVersion : 'local' }
				};
				var context = {
					functionName: 'lambdaApi',
					stage: 'local',
					local: true,
					identity: {
						cognitoIdentityId: 'local',
						cognitoIdentityPoolId: null
					}
				};

				var callback = (_, response) => {
					this.logger(`StatusCode: ${response.statusCode}`);
					this.logger(`Headers: ${JSON.stringify(response.headers, null, 2)}`);
					if (response.headers) { res.set(response.headers); }
					if (!response.statusCode) { response.statusCode = 200; }
					try {
						var json = JSON.parse(response.body);
						this.logger(`Body: ${JSON.stringify(json, null, 2)}`);
						return res.status(response.statusCode).json(json);
					}
					catch (exception) {
						this.logger(`Body: Binary}`);
						return res.status(response.statusCode).send(response.body);
					}
				};

				if (!lambdaApi.Authorizer || !lambdaApi.Authorizer.AuthorizerFunc) {
					lambdaApi.Authorizer.AuthorizerFunc = () => ({ principalId: '<no-authorizer-func-specified>' });
				}

				try {
					return Promise.resolve(lambdaApi.Authorizer.AuthorizerFunc(event))
					.then(promiseResult => {
						event.requestContext = {
							authorizer: {
								principalId: promiseResult.principalId
							}
						};
						lambdaApi.handler(event, context, callback);
					}, authorizerError => {
						console.error(authorizerError.stack || authorizerError);
						res.status(authorizerError === 'Unauthorized' ? 401 : 403).json({
							code: 'Unauthorized',
							title: 'Authorizer returned rejection',
							response: authorizerError.toString()
						});
					});
				} catch (exception) {
					res.status(500).json({
						code: 'AuthorizationRuntimeFailure',
						title: 'Synchronous failure in authorizer',
						error: exception.stack || exception.toString()
					});
				}
			});
		});
	});

	api.all(/.*/, (req, res, next) => {
		res.status(404).json({
			statusCode: 404,
			title: `Resource not found at ${req.originalUrl}`
		});
	});

	api.use((error, req, res, next) => {
		console.error(`Catch-all Error: ${error.stack || error} - ${JSON.stringify(error, null, 2)}`);
		res.status(500).json({
			statusCode: 500,
			title: 'Catch-all Error',
			detail: error.stack || error.toString()
		});
	});

	/**********************/

	var web = express.Router();
	this.App.use('/', web);
	web.use(bodyParser.urlencoded({ extended: false }));
	web.get(/^(.*)$/, (req, res) => {
		var options = {
			root: contentDirectory,
			dotfiles: 'deny',
			headers: {
				'X-Timestamp': Date.now(),
				'X-Sent': true
			}
		};

		var fileName = req.params[0] || 'index.html'
		res.sendFile(fileName, options, (err) => {
			if (err) {
			  console.error(`Error sending file: ${err}`);
			  console.error(err);
			  let statusCode = err.status;
			  if (!statusCode || statusCode.toString().match(/^[12345]\d{2}$/)) { statusCode = 500; }
			  res.status(statusCode).end();
			}
			else {
			  this.logger('Sent:', fileName);
			}
		});
	});
};

Server.prototype.Run = function(port) {
	var debug = require('debug')('my-application');
	var http = require('http');

	var httpServer = http.createServer(this.App);
	http.createServer(this.App);

	var server = httpServer.listen(port, function() {
		debug(`Express server listening on port ${server.address().port}`);
	});
};

module.exports = Server;<commit_msg>Return context as part of authorizer in local server.<commit_after>'use strict';

var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var express = require('express');
var morgan = require('morgan');
var path = require('path');
var normalizeHeaderCase = require("header-case-normalizer");

function fixHeaders(headers) {
	Object.keys(headers).map(key => {
		headers[normalizeHeaderCase(key)] = headers[key];
	});
}

function Server(contentDirectory, lambdaApi, logger) {
	this.logger = logger || console.log;
	this.App = express();
	//Logger
	this.App.use(morgan('dev'));
	this.App.use(cookieParser());

	// Log every request to the console.
	this.App.use((req, res, next) => {
		this.logger("********************************************************************************");
		fixHeaders(req.headers);
		this.logger("Request Headers: " + JSON.stringify(req.headers, null, 2));
		this.logger("----------------------------------");
		next();
	});

	/**********************/
	/*		 API		*/

	var api = express.Router();
	this.App.use('/api/', api);

	api.use(bodyParser.json({ type: '*/*' }));
	api.use((error, req, res, next) => {
		res.status(400).send({error: 'Invalid JSON'});
	});

	api.get('/', (req, res, next) => {
		try {
			Promise.resolve(null)
			.then((data) => {
				res.send({});
				res.end();
			})
			.catch((error) => {
				var message = `Error: ${error}`;
				console.error(message);
				res.status(400).send(message);
			});
		}
		catch (exception) {
			var message = `Error: ${exception}`;
			console.error(message);
			res.status(500).send(message);
		}
	});

	Object.keys(lambdaApi.Routes || []).map(method => {
		Object.keys(lambdaApi.Routes[method]).map(resource => {
			var expressResource = resource.replace(/{proxy\+}/, '*').replace(/{([^\{\}]+)}/g, ':$1');
			api.all(expressResource, (req, res) => {
				var event = {
					resource: resource,
					path: req.path,
					httpMethod: req.method,
					headers: req.headers || {},
					queryStringParameters: req.query,
					pathParameters: req.params || {},
					body: req.body,
					stageVariables: { lambdaVersion : 'local' }
				};
				var context = {
					functionName: 'lambdaApi',
					stage: 'local',
					local: true,
					identity: {
						cognitoIdentityId: 'local',
						cognitoIdentityPoolId: null
					}
				};

				var callback = (_, response) => {
					this.logger(`StatusCode: ${response.statusCode}`);
					this.logger(`Headers: ${JSON.stringify(response.headers, null, 2)}`);
					if (response.headers) { res.set(response.headers); }
					if (!response.statusCode) { response.statusCode = 200; }
					try {
						var json = JSON.parse(response.body);
						this.logger(`Body: ${JSON.stringify(json, null, 2)}`);
						return res.status(response.statusCode).json(json);
					}
					catch (exception) {
						this.logger(`Body: Binary}`);
						return res.status(response.statusCode).send(response.body);
					}
				};

				if (!lambdaApi.Authorizer || !lambdaApi.Authorizer.AuthorizerFunc) {
					lambdaApi.Authorizer.AuthorizerFunc = () => ({ principalId: '<no-authorizer-func-specified>' });
				}

				try {
					return Promise.resolve(lambdaApi.Authorizer.AuthorizerFunc(event))
					.then(promiseResult => {
						event.requestContext = {
							authorizer: promiseResult.context || {}
						};
						event.requestContext.authorizer.principalId = promiseResult.principalId;

						lambdaApi.handler(event, context, callback);
					}, authorizerError => {
						console.error(authorizerError.stack || authorizerError);
						res.status(authorizerError === 'Unauthorized' ? 401 : 403).json({
							code: 'Unauthorized',
							title: 'Authorizer returned rejection',
							response: authorizerError.toString()
						});
					});
				} catch (exception) {
					res.status(500).json({
						code: 'AuthorizationRuntimeFailure',
						title: 'Synchronous failure in authorizer',
						error: exception.stack || exception.toString()
					});
				}
			});
		});
	});

	api.all(/.*/, (req, res, next) => {
		res.status(404).json({
			statusCode: 404,
			title: `Resource not found at ${req.originalUrl}`
		});
	});

	api.use((error, req, res, next) => {
		console.error(`Catch-all Error: ${error.stack || error} - ${JSON.stringify(error, null, 2)}`);
		res.status(500).json({
			statusCode: 500,
			title: 'Catch-all Error',
			detail: error.stack || error.toString()
		});
	});

	/**********************/

	var web = express.Router();
	this.App.use('/', web);
	web.use(bodyParser.urlencoded({ extended: false }));
	web.get(/^(.*)$/, (req, res) => {
		var options = {
			root: contentDirectory,
			dotfiles: 'deny',
			headers: {
				'X-Timestamp': Date.now(),
				'X-Sent': true
			}
		};

		var fileName = req.params[0] || 'index.html'
		res.sendFile(fileName, options, (err) => {
			if (err) {
			  console.error(`Error sending file: ${err}`);
			  console.error(err);
			  let statusCode = err.status;
			  if (!statusCode || statusCode.toString().match(/^[12345]\d{2}$/)) { statusCode = 500; }
			  res.status(statusCode).end();
			}
			else {
			  this.logger('Sent:', fileName);
			}
		});
	});
};

Server.prototype.Run = function(port) {
	var debug = require('debug')('my-application');
	var http = require('http');

	var httpServer = http.createServer(this.App);
	http.createServer(this.App);

	var server = httpServer.listen(port, function() {
		debug(`Express server listening on port ${server.address().port}`);
	});
};

module.exports = Server;<|endoftext|>