UNPKG

xserver

Version:
198 lines (178 loc) 5.26 kB
var fs = require("fs"); var http = require("http"); var https = require("https"); var path = require("path"); var Directive = require("./directive"); function XServer ( config ) { this.directories = config.directories || []; this.port = config.port || 80; this.certs = config.certificates; this.routes = {}; this.maxRequestSize = config.maxRequestSize || 2000000; this.temporaryDirectory = fs.realpathSync(config.temporaryDirectory || "."); } XServer.prototype.start = function () { this.stop(); var listener = this.onTransaction.bind(this); this.server = this.certs ? https.createServer(this.certs, listener) : this.server = http.createServer(listener); this.server.listen(this.port); }; XServer.prototype.stop = function () { if ( this.server ) { this.server.close(); delete this.server; } }; // method, regex, [size], [download], calls... XServer.prototype.route = function () { var args = Array.prototype.slice.call(arguments); var method = String(args[0]).toUpperCase(); var index = 2; var opts = { regex: args[1], size: this.maxRequestSize, download: false }; if ( args.length > 3 && typeof args[2] == "number" ) { index++; opts.size = args[2]; } if ( args.length > 4 && typeof args[3] == "boolean" ) { index++; opts.download = args[3]; } if ( !this.routes[method] ) { this.routes[method] = {}; } opts.methods = args.slice(index, args.length); this.routes[method][opts.regex] = opts; }; XServer.prototype.getRoute = function ( directive ) { var routes = this.routes[directive.request.method] for ( key in routes ) { var rx = routes[key].regex; if ( typeof rx === "string" ) { rx = new RegExp(rx); } uri = directive.url.value.substr(1, directive.url.value.length); if ( rx.test && rx.test(uri) ) { var params = rx.exec(uri); params.splice(0, 1); return { chain: routes[key].methods, params: params, download: routes[key].download, size: routes[key].size }; } } }; XServer.prototype.runUntilSent = function ( directive, arr, execute ) { if ( directive.sent ) return; if ( !arr.length && !directive.sent ) return directive.send(); execute(arr.shift(), (function () { this.runUntilSent(directive, arr, execute); }).bind(this)); }; XServer.prototype.finishRouteDirective = function ( route, directive ) { this.runUntilSent(directive, route.chain.slice(), function ( process, next ) { directive.next = next; process.apply(process, [directive].concat(route.params)); }); }; XServer.prototype.limitRequestSize = function ( directive, limit, abort ) { var size = 0; var call = function ( chunk ) { size += chunk.length; if ( size > limit ) { directive.request.removeListener("data", call); abort(); } }; directive.request.on("data", call); }; XServer.prototype.respondToUpload = function ( route, directive ) { var name = String((new Date()).getTime()) var uri = path.join(this.temporaryDirectory, name); var stream = fs.createWriteStream(uri); var onEnd = (function () { this.finishRouteDirective(route, directive) }).bind(this); directive.fileUri = uri; directive.request.pipe(stream); directive.request.once("end", onEnd); this.limitRequestSize(directive, route.size, function () { directive.request.removeListener("end", onEnd); directive.sendStatus(413); }); }; XServer.prototype.respondToRoute = function ( route, directive ) { var clean = function () { directive.request.removeListener("end", onEnd); directive.request.removeListener("data", onData); } var onData = function ( chunk ) { if ( directive.body ) { directive.body += chunk; } else { directive.body = chunk; } }; var onEnd = (function () { clean(); this.finishRouteDirective(route, directive); }).bind(this); directive.request.on("data", onData); directive.request.on("end", onEnd); this.limitRequestSize(directive, route.size, function () { clean(); directive.sendStatus(413); }); }; XServer.prototype.respondWithFile = function ( directive ) { directive.setStatus(404); this.runUntilSent(directive, this.directories.slice(), function ( directory, next ) { var path = directory + directive.url.value; directive.prepareFile(path, function ( err ) { if ( err ) return next(); directive.send(); }); }); }; XServer.prototype.onTransaction = function( request, response ) { var directive = new Directive(request, response); var route; if ( route = this.getRoute(directive) ) { if ( route.download ) { this.respondToUpload(route, directive); } else { this.respondToRoute(route, directive); } } else { this.respondWithFile(directive); } }; var methods = [ "connect", "delete", "get", "head", "options", "patch", "post", "put", "trace" ]; for ( var i = 0; i < methods.length; i++ ) { var method = methods[i]; XServer.prototype[method] = (function ( method ) { return function () { var args = Array.prototype.slice.call(arguments); this.route.apply(this, [method].concat(args)); } })(method); } module.exports = XServer;