/**
* @file node-web-server.js
* @copyright Monohm 2014
*/
/**
* Web server for node.
* Implements support for POST on top of the regular incoming-request class.
*
* @class
* @constructor
* @param {function} inRequestListener - function to call with request when complete
*/
sensible.node.WebServer = function (inRequestListener)
{
this.requestListener = inRequestListener;
if (gSensibleApplication.config.ssl)
{
var sslConfig = gSensibleApplication.config.ssl;
console.log ("reading certificate from " + sslConfig.certificate_path);
var certificate = fs.readFileSync (sslConfig.certificate_path);
console.log ("reading private key from " + sslConfig.private_key_path);
var privateKey = fs.readFileSync (sslConfig.private_key_path);
var caCertificates = new Array ();
for (var i = 0; i < sslConfig.ca_certificate_paths.length; i++)
{
console.log ("reading CA certificate from " + sslConfig.ca_certificate_paths [i]);
caCertificates [i] = fs.readFileSync (sslConfig.ca_certificate_paths [i]);
}
// HACK won't work on tessel
var sslOptions =
{
cert: certificate,
key: privateKey,
ca: caCertificates
};
this.server = https.createServer (sslOptions, this.onRequest.bind (this));
}
else
{
this.server = http.createServer (this.onRequest.bind (this));
}
}
/**
* Stop serving requests.
*/
sensible.node.WebServer.prototype.stop =
function NodeWebServer_stop ()
{
this.server.close ();
}
/**
* Listen for requests.
*
* @param {integer} inPort - port on which to listen, eg 80 for HTTP
*/
sensible.node.WebServer.prototype.listen =
function NodeWebServer_listen (inPort)
{
this.server.listen (inPort);
console.log ("HTTP server listening on port " + inPort);
}
/**
* Dispatch event handler assignments.
* This class stands in for the regular server class, so we have to proxy
* the setting of various event handlers.
* Check for setting the request event handler, as we will call that
* when WE think the request is complete, ie once we've checked for POST, etc.
* Forward on any other event handler assignments to our server instance.
*
* @param {string} inEventName - name of event, eg request
* @param {function} inEventHandler - event handler function
*/
sensible.node.WebServer.prototype.on =
function NodeWebServer_on (inEventName, inEventHandler)
{
var handled = false;
if (inEventName == "request")
{
this.requestListener = inEventHandler;
handled = true;
}
if (!handled)
{
this.server.on (inEventName, inEventHandler);
}
}
// event handlers
/**
* Handle incoming HTTP requests.
* This implementation extends the regular Node http package
* by supporting POST.
*
* @param {object} inRequest - HTTP request
* @param {object} outResponse - HTTP response
*/
sensible.node.WebServer.prototype.onRequest =
function NodeWebServer_onRequest (inRequest, outResponse)
{
var requestURL = url.parse (inRequest.url, true, true);
if (inRequest.method.toLowerCase () == "post")
{
var parameters = new Object ();
var files = new Object ();
var self = this;
// we use busboy which gives us access to the NON file params too!
var busboy = new Busboy ({headers: inRequest.headers });
busboy.on
(
"file",
function (inFieldName, inFileStream, inFileName, inEncoding, inMimeType)
{
console.log ("got file " + inFieldName + " = " + inFileName);
// write the file out somewhere and add it to the list
// note, path.parse() is too new for Raspberry Pi
// so simulate using other bits of the path package
var extension = path.extname (inFileName);
var leaf = path.basename (inFileName, extension);
var outputFileName = leaf + "-" + Date.now () + extension;
console.log ("writing to " + outputFileName);
var uploadDirectoryPath = path.join (__dirname, "uploads");
if (! fs.existsSync (uploadDirectoryPath))
{
fs.mkdirSync (uploadDirectoryPath);
}
var fos = fs.createWriteStream (path.join (__dirname, "uploads", outputFileName));
inFileStream.on
(
"data",
function (inData)
{
fos.write (inData);
}
);
inFileStream.on
(
"end",
function ()
{
fos.end ();
var file = new Object ();
file.file = outputFileName;
file.mimeType = inMimeType;
files [inFieldName] = file;
}
);
}
);
busboy.on
(
"field",
function (inFieldName, inValue, inFieldNameTruncated, inValueTruncated)
{
console.log ("got field " + inFieldName + " = " + inValue);
parameters [inFieldName] = inValue;
}
);
busboy.on
(
"finish",
function ()
{
// vector off to our request listener
self.requestListener (inRequest, outResponse, requestURL, parameters, files);
}
);
inRequest.pipe (busboy);
}
else
{
this.requestListener (inRequest, outResponse, requestURL, requestURL.query, null);
}
}