httpe.js

import generateCertificate from './generateCertificate';
import getPathStats from './getPathStats';
import https from 'https';
import IncomingMessage from './IncomingMessage';
import isPortAvailable from './isPortAvailable';
import mimeTypes from 'mime-types';
import os from 'os';
import Server from './Server';
import ServerResponse from './ServerResponse';

/**
* @module httpe
* @desc The HTTP interface designed to support http & https protocols & multiple ports simultaneously.
*/

const httpe = {
	...https,

	/**
	* @name createServer
	* @function
	* @desc Creates a new HTTP & HTTPS Server that supports multiple ports.
	* @param {Object} [options] - The options for the server or `connectionListener`.
	* @param {Boolean} [options.useAvailablePort] - Whether to use the first available port from the desired port or ports.
	* @param {Boolean} [options.listen] - The overriding desired port or ports to use if they are available, otherwising using the first available.
	* @param {Array|Number} [options.port] - The desired port or ports to use.
	* @param {Buffer|String} [options.cert] - The certificate; when missing along with `options.key` will cause a new certificate to be generated.
	* @param {Buffer|String} [options.key] - The key; when missing along with `options.cert` will cause a new certificate to be generated.
	* @param {Function} [connectionListener] - The listener bound to all connections.
	* @returns {Server}
	* @example <caption>Create a server on port 8080 that is automatically listening</caption>
	* server = new httpe.createServer({ port: 8080, listen: true })
	*/

	createServer () {
		return new Server(...arguments);
	},

	/**
	* @name generateCertificate
	* @function
	* @desc Generates a new SSL certificate.
	* @param {Array|Object} props - A map of OID subject items.
	* @param {Object} opts - Additional certificate configuration.
	* @returns {Object} The certificate (`cert`) and private key (`key`).
	* @example
	* cert = new httpe.generateCertificate({
	*   commonName: 'example.org',
	*   countryName, 'US',
	*   localityName: 'Blacksburg',
	*   organizationName: 'Test'
	* })
	*/

	generateCertificate,

	/**
	* @name getCharset
	* @function
	* @desc Returns the character set of a given path.
	* @param {String} path - The path used to determine the charset.
	* @returns {String|Null} The determined charset set or null.
	* @example
	* httpe.getCharset('/script.js') // 'UTF-8'
	*/

	getCharset (path) {
		return mimeTypes.charset(mimeTypes.lookup(path)) || null;
	},

	/**
	* @name getContentType
	* @function
	* @desc Returns the content type of a given path.
	* @param {String} path - The path used to determine the content-type.
	* @returns {String|Null} The determined content-type or null.
	* @example
	* httpe.contentType('/script.js') // 'application/javascript; charset=utf-8'
	*/

	getContentType (path) {
		return mimeTypes.contentType(mimeTypes.lookup(path)) || null;
	},

	/**
	* @name getPathStats
	* @function
	* @desc Returns the file stats for a given path.
	* @param {String} path - The path used to get stats.
	* @param {Object} [opts] - Additional configuration resolving file stats.
	* @param {Object} [opts.from = '.'] - The directory used to resolve the path.
	* @param {Object} [opts.index = 'index.html'] - The index basename used to resolve directories.
	* @async
	* @returns {Stats} A promise resolving with the matching path and file stats, or an error if it failed.
	* @example
	* httpe.getPathStats('/path/to/dir').then(stats => {
	*   // get the resolved path
	*   const isPathAFile = stats.path
	*
	*   // get whether the path is a file
	*   const isPathAFile = stats.isFile()
	*
	*   // destructure additional custom stats
	*   const { charset, contentLength, contentType, lastModified, mimeType, path } = stats
	* })
	*/

	getPathStats (path, opts) {
		return getPathStats(path, opts);
	},

	/**
	* @name getMimeType
	* @function
	* @desc Returns the mime type of a given path.
	* @param {String} path - The path used to determine the mimetype.
	* @returns {String|Null} The determined mimetype or null.
	* @example
	* httpe.getCharset('/script.js') // 'application/javascript'
	*/

	getMimeType (path) {
		return mimeTypes.lookup(path) || null;
	},

	/**
	* @name IncomingMessage
	* @desc Creates the `request` object used to access client status, headers and data. See {@link IncomingMessage}.
	* @implements {IncomingMessage}
	*/

	IncomingMessage,

	/**
	* @name isPortAvailable
	* @async
	* @function
	* @desc Returns a promise for whether a port is available for a connection.
	* @param {Number} port - The port tested for an available connection.
	* @param {Boolean} useAvailablePort - Whether to use the first available port.
	* @async
	* @returns {Number|Error} A promise resolving with the available port or rejecting with the error.
	* @example
	* httpe.isPortAvailable(80).then(availablePort => {}, error => {});
	* @example <caption>Get any available port from 80 onward</caption>
	* httpe.isPortAvailable(80, true).then(availablePort => {}, error => {});
	*/

	isPortAvailable,

	/**
	* @name Server
	* @desc Creates a new HTTP & HTTPS Server that supports multiple ports. See {@link Server}.
	* @implements {Server}
	* @example <caption>Create a server on port 8080 that is automatically listening</caption>
	* server = new httpe.Server({ port: 8080, listen: true })
	*/

	Server,

	/**
	* @name ServerResponse
	* @desc Creates the `response` object used to define server status, headers and data. See {@link ServerResponse}.
	* @implements {ServerResponse}
	*/

	ServerResponse,

	/**
	* @type {String}
	* @desc Gets the first external IP address for the server.
	* @example <caption>Get the current IP address of the server.</caption>
	* httpe.ip // '127.0.0.1'
	*/

	get ip () {
		const interfaces = os.networkInterfaces();

		for (const name in interfaces) {
			for (const address of interfaces[name]) {
				if (address.family === 'IPv4' && !address.internal) {
					return address.address;
				}
			}
		}

		return '127.0.0.1';
	}
};

export default httpe;

/**
* The file stats for a given path. Extends {@link https://nodejs.org/api/fs.html#fs_class_fs_stats fs.Stats}.
* @typedef {Object} Stats
* @property {number} charset - The X Coordinate
* @property {number} contentLength - The Y Coordinate
* @property {number} contentType - The X Coordinate
* @property {number} lastModified - The Y Coordinate
* @property {number} mimeType - The X Coordinate
* @property {number} path - The Y Coordinate
*/