Source: node-web-server.js

/**
 * @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);
	}
}