create-servers
Advanced tools
+102
-94
@@ -10,3 +10,4 @@ 'use strict'; | ||
| var fs = require('fs'), | ||
| const | ||
| fs = require('fs').promises, | ||
| tls = require('tls'), | ||
@@ -19,5 +20,5 @@ path = require('path'), | ||
| var pemFormat = /-----BEGIN/; | ||
| const pemFormat = /-----BEGIN/; | ||
| var CIPHERS = [ | ||
| const CIPHERS = [ | ||
| 'ECDHE-RSA-AES256-SHA384', | ||
@@ -41,3 +42,3 @@ 'DHE-RSA-AES256-SHA384', | ||
| var secureOptions = constants.SSL_OP_NO_SSLv3; | ||
| const secureOptions = constants.SSL_OP_NO_SSLv3; | ||
@@ -55,18 +56,15 @@ /** | ||
| const [ | ||
| [httpErr, http], | ||
| [httpsErr, https], | ||
| [http2Err, http2]] = await Promise.all([ | ||
| const [httpResult, httpsResult, http2Result] = await Promise.allSettled([ | ||
| createHttp(options.http, options.log), | ||
| createHttps(options.https, options.log), | ||
| createHttps(options.http2, options.log, true) | ||
| ]); | ||
| ]) | ||
| const servers = {}; | ||
| if (http) servers.http = http; | ||
| if (https) servers.https = https; | ||
| if (http2) servers.http2 = http2; | ||
| if (httpResult.value) servers.http = httpResult.value; | ||
| if (httpsResult.value) servers.https = httpsResult.value; | ||
| if (http2Result.value) servers.http2 = http2Result.value; | ||
| if (httpErr || httpsErr || http2Err) { | ||
| let errorSource = http2Err || httpsErr || httpErr; | ||
| const errorSource = httpResult.reason || httpsResult.reason || http2Result.reason; | ||
| if (errorSource) { | ||
| if (Array.isArray(errorSource)) { | ||
@@ -78,5 +76,5 @@ errorSource = errorSource[0]; | ||
| message: errorSource && errorSource.message, | ||
| http2: http2Err, | ||
| https: httpsErr, | ||
| http: httpErr | ||
| http2: http2Result.reason, | ||
| https: httpsResult.reason, | ||
| http: httpResult.reason | ||
| }), | ||
@@ -180,13 +178,13 @@ servers | ||
| return Array.isArray(data) | ||
| ? data.map(function(item) { | ||
| ? Promise.all(data.map(function(item) { | ||
| return normalizeCertChain(root, item); | ||
| }) | ||
| })) | ||
| : normalizePEMContent(root, data); | ||
| } | ||
| function normalizeCertChain(root, data) { | ||
| async function normalizeCertChain(root, data) { | ||
| // A chain can be an array, which we concatenate together into one PEM, | ||
| // an already-concatenated chain, or a single PEM | ||
| const content = normalizePEMContent(root, data); | ||
| const content = await normalizePEMContent(root, data); | ||
| return Array.isArray(content) ? content.join('\n') : content; | ||
@@ -199,3 +197,3 @@ } | ||
| } | ||
| return ca && ca.map(normalizePEMContent.bind(null, root)); | ||
| return ca && Promise.all(ca.map(normalizePEMContent.bind(null, root))); | ||
| } | ||
@@ -211,5 +209,5 @@ | ||
| if (Array.isArray(file)) | ||
| return file.map(function map(item) { | ||
| return Promise.all(file.map(function map(item) { | ||
| return normalizePEMContent(root, item); | ||
| }); | ||
| })); | ||
@@ -223,3 +221,3 @@ // | ||
| return fs.readFileSync(path.resolve(root, file)); | ||
| return fs.readFile(path.resolve(root, file)); | ||
| } | ||
@@ -238,7 +236,7 @@ | ||
| function getSNIHandler(sslOpts) { | ||
| var sniHosts = Object.keys(sslOpts.sni); | ||
| async function getSNIHandler(sslOpts) { | ||
| const sniHosts = Object.keys(sslOpts.sni); | ||
| // Pre-compile regexps for the hostname | ||
| var hostRegexps = sniHosts.map(function(host) { | ||
| const hostRegexps = sniHosts.map(function(host) { | ||
| return host === '*' ? /.*/ : new RegExp( | ||
@@ -255,3 +253,3 @@ '^' + | ||
| // Prepare secure contexts ahead-of-time | ||
| var hostSecureContexts = sniHosts.map(function(host) { | ||
| const hostSecureContexts = await Promise.all(sniHosts.map(async function(host) { | ||
| var hostOpts = sslOpts.sni[host]; | ||
@@ -261,7 +259,13 @@ | ||
| const [key, cert, ca] = await Promise.all([ | ||
| normalizePEMContent(root, hostOpts.key), | ||
| normalizeCertContent(root, hostOpts.cert), | ||
| normalizeCA(root, hostOpts.ca || sslOpts.ca) | ||
| ]) | ||
| return tls.createSecureContext( | ||
| assign({}, sslOpts, hostOpts, { | ||
| key: normalizePEMContent(root, hostOpts.key), | ||
| cert: normalizeCertContent(root, hostOpts.cert), | ||
| ca: normalizeCA(root, hostOpts.ca || sslOpts.ca), | ||
| key, | ||
| cert, | ||
| ca, | ||
| ciphers: normalizeCiphers(hostOpts.ciphers || sslOpts.ciphers), | ||
@@ -275,3 +279,3 @@ honorCipherOrder: !!( | ||
| ); | ||
| }); | ||
| })); | ||
@@ -298,3 +302,3 @@ return function(hostname, cb) { | ||
| log('http | no options.http; no server'); | ||
| return [null, null]; | ||
| return null; | ||
| } | ||
@@ -306,18 +310,19 @@ | ||
| return await new Promise(resolve => { | ||
| var server = require('http').createServer(httpConfig.handler), | ||
| timeout = httpConfig.timeout, | ||
| port = httpConfig.port, | ||
| args; | ||
| const | ||
| server = require('http').createServer(httpConfig.handler), | ||
| timeout = httpConfig.timeout, | ||
| port = httpConfig.port; | ||
| if (typeof timeout === 'number') server.setTimeout(timeout); | ||
| if (typeof timeout === 'number') server.setTimeout(timeout); | ||
| args = [server, port]; | ||
| if (httpConfig.host) { | ||
| args.push(httpConfig.host); | ||
| } | ||
| const args = [server, port]; | ||
| if (httpConfig.host) { | ||
| args.push(httpConfig.host); | ||
| } | ||
| log('http | try listen ' + port); | ||
| log('http | try listen ' + port); | ||
| return new Promise((resolve, reject) => { | ||
| args.push(function listener(err) { | ||
| resolve([err, server]); | ||
| err ? reject(err) : resolve(server); | ||
| }); | ||
@@ -335,3 +340,3 @@ connected.apply(null, args); | ||
| log('https | no options.https; no server'); | ||
| return [null, null]; | ||
| return null; | ||
| } | ||
@@ -343,48 +348,46 @@ | ||
| return await new Promise(resolve => { | ||
| var port = ssl.port, | ||
| timeout = ssl.timeout, | ||
| server, | ||
| args; | ||
| const [key, cert, ca] = await Promise.all([ | ||
| normalizePEMContent(ssl.root, ssl.key), | ||
| normalizeCertContent(ssl.root, ssl.cert, ssl.key), | ||
| normalizeCA(ssl.root, ssl.ca) | ||
| ]); | ||
| var finalHttpsOptions = assign({}, ssl, { | ||
| // | ||
| // Load default SSL key, cert and ca(s). | ||
| // | ||
| key: normalizePEMContent(ssl.root, ssl.key), | ||
| cert: normalizeCertContent(ssl.root, ssl.cert, ssl.key), | ||
| ca: normalizeCA(ssl.root, ssl.ca), | ||
| // | ||
| // Properly expose ciphers for an A+ SSL rating: | ||
| // https://certsimple.com/blog/a-plus-node-js-ssl | ||
| // | ||
| ciphers: normalizeCiphers(ssl.ciphers), | ||
| honorCipherOrder: !!ssl.honorCipherOrder, | ||
| // | ||
| // Protect against the POODLE attack by disabling SSLv3 | ||
| // @see http://googleonlinesecurity.blogspot.nl/2014/10/this-poodle-bites-exploiting-ssl-30.html | ||
| // | ||
| secureProtocol: 'SSLv23_method', | ||
| secureOptions: secureOptions | ||
| }); | ||
| const finalHttpsOptions = assign({}, ssl, { | ||
| key, | ||
| cert, | ||
| ca, | ||
| // | ||
| // Properly expose ciphers for an A+ SSL rating: | ||
| // https://certsimple.com/blog/a-plus-node-js-ssl | ||
| // | ||
| ciphers: normalizeCiphers(ssl.ciphers), | ||
| honorCipherOrder: !!ssl.honorCipherOrder, | ||
| // | ||
| // Protect against the POODLE attack by disabling SSLv3 | ||
| // @see http://googleonlinesecurity.blogspot.nl/2014/10/this-poodle-bites-exploiting-ssl-30.html | ||
| // | ||
| secureProtocol: 'SSLv23_method', | ||
| secureOptions: secureOptions | ||
| }); | ||
| if (ssl.sni && !finalHttpsOptions.SNICallback) { | ||
| finalHttpsOptions.SNICallback = getSNIHandler(ssl); | ||
| } | ||
| if (ssl.sni && !finalHttpsOptions.SNICallback) { | ||
| finalHttpsOptions.SNICallback = await getSNIHandler(ssl); | ||
| } | ||
| log('https | listening on %d', port); | ||
| if(h2) { | ||
| server = require('http2').createSecureServer(finalHttpsOptions, ssl.handler) | ||
| } else { | ||
| server = require('https').createServer(finalHttpsOptions, ssl.handler); | ||
| } | ||
| const port = ssl.port; | ||
| log('https | listening on %d', port); | ||
| const server = h2 | ||
| ? require('http2').createSecureServer(finalHttpsOptions, ssl.handler) | ||
| : require('https').createServer(finalHttpsOptions, ssl.handler); | ||
| if (typeof timeout === 'number') server.setTimeout(timeout); | ||
| args = [server, port]; | ||
| if (ssl.host) { | ||
| args.push(ssl.host); | ||
| } | ||
| const timeout = ssl.timeout; | ||
| if (typeof timeout === 'number') server.setTimeout(timeout); | ||
| const args = [server, port]; | ||
| if (ssl.host) { | ||
| args.push(ssl.host); | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| args.push(function listener(err) { | ||
| resolve([err, server]); | ||
| err ? reject(err) : resolve(server); | ||
| }); | ||
@@ -396,12 +399,17 @@ connected.apply(null, args); | ||
| async function createMultiple(createFn, configArray, log) { | ||
| const errorsOrServers = await Promise.all( | ||
| const errorsOrServers = await Promise.allSettled( | ||
| configArray.map(cfg => createFn(cfg, log)) | ||
| ); | ||
| const errors = [], | ||
| servers = []; | ||
| for (const [error, server] of errorsOrServers) { | ||
| error && errors.push(error); | ||
| server && servers.push(server); | ||
| const errors = [], servers = []; | ||
| for (const result of errorsOrServers) { | ||
| result.reason && errors.push(result.reason); | ||
| result.value && servers.push(result.value); | ||
| } | ||
| return [errors.length ? errors : null, servers]; | ||
| if (errors.length) { | ||
| throw errors; | ||
| } else { | ||
| return servers; | ||
| } | ||
| } |
+4
-1
| { | ||
| "name": "create-servers", | ||
| "version": "3.2.0", | ||
| "version": "3.2.1", | ||
| "description": "Create an http AND/OR an https server and call the same request handler.", | ||
| "main": "index.js", | ||
| "files": [ | ||
| "./index.js" | ||
| ], | ||
| "scripts": { | ||
@@ -7,0 +10,0 @@ "test": "node test/create-servers-test.js" |
| package-lock.json binary |
| language: node_js | ||
| node_js: | ||
| - "0.12" | ||
| - "0.10" | ||
| - "0.8" | ||
| - "iojs" | ||
| before_install: | ||
| - "npm install -g npm@1.4.x" |
-18
| # CHANGELOG | ||
| ## 3.2.0 | ||
| - [#37] Support HTTP2 | ||
| ## 3.1.0 | ||
| - [#23], @DullReferenceException Support "*" for SNI host pattern | ||
| ## 3.0.1 | ||
| - [#22] Return value compatibility. Do no return `null` valued keys. | ||
| ## 3.0.0 | ||
| - [#21], @DullReferenceException Support creating multiple HTTP or HTTPS servers. | ||
| - [BREAKING] Introduces `async` functions. |
| { | ||
| "platform": "github", | ||
| "autodiscover": false, | ||
| "requireConfig": true, | ||
| "ignoreNpmrcFile": true, | ||
| "rangeStrategy": "replace", | ||
| "packageRules": [ | ||
| { | ||
| "packagePatterns": [ | ||
| "*" | ||
| ], | ||
| "minor": { | ||
| "groupName": "all non-major dependencies", | ||
| "groupSlug": "all-minor-patch" | ||
| } | ||
| } | ||
| ], | ||
| "commitMessagePrefix": "[dist]" | ||
| } |
| /* | ||
| * create-servers-test.js: Make sure creating both works | ||
| * | ||
| * (C) 2013, Charlie Robbins. | ||
| * | ||
| */ | ||
| var path = require('path'), | ||
| fs = require('fs'), | ||
| url = require('url'), | ||
| http = require('http'), | ||
| https = require('https'), | ||
| http2 = require('http2'), | ||
| { promisify } = require('util'), | ||
| test = require('tape'), | ||
| sinon = require('sinon'), | ||
| evilDNS = require('evil-dns'), | ||
| createServers = require('../'); | ||
| const createServersAsync = promisify(createServers); | ||
| const { HTTP2_HEADER_PATH } = http2.constants; | ||
| const ca = fs.readFileSync(path.join(__dirname, './fixtures/example-ca-cert.pem')); | ||
| // | ||
| // Immediately end a response. | ||
| // | ||
| function fend(req, res) { | ||
| res.end(); | ||
| } | ||
| // | ||
| // Request and download response from a URL | ||
| // | ||
| async function download(httpsURL) { | ||
| return new Promise((resolve, reject) => { | ||
| const req = https.get({ | ||
| ...url.parse(httpsURL), | ||
| ca | ||
| }, res => { | ||
| const chunks = []; | ||
| res | ||
| .on('data', chunk => chunks.push(chunk)) | ||
| .once('end', () => { | ||
| resolve(chunks.map(chunk => chunk.toString('utf8')).join('')); | ||
| }) | ||
| .once('aborted', reject) | ||
| .once('close', reject) | ||
| .once('error', reject); | ||
| }); | ||
| req.once('error', reject); | ||
| }); | ||
| } | ||
| // | ||
| // Request and download response from a URL using HTTP/2 | ||
| // | ||
| async function download2(httpsURL) { | ||
| return new Promise((resolve, reject) => { | ||
| const clientSession = http2.connect(httpsURL); | ||
| const fail = results => { | ||
| clientSession.close(); | ||
| reject(results); | ||
| }; | ||
| const req = clientSession.request({ [HTTP2_HEADER_PATH]: '/' }); | ||
| req.on('response', () => { | ||
| const chunks = []; | ||
| req | ||
| .on('data', chunk => chunks.push(chunk)) | ||
| .once('end', () => { | ||
| resolve(chunks.map(chunk => chunk.toString('utf8')).join('')); | ||
| clientSession.close(); | ||
| }); | ||
| }) | ||
| .once('aborted', fail) | ||
| .once('close', fail) | ||
| .once('error', fail); | ||
| }); | ||
| } | ||
| /** | ||
| * Helper to start a server with HTTP/2 support. | ||
| * Returns stop function to handle server and dns cleanup after tests. | ||
| * | ||
| * Tests requests can be made to foo.example.org:3456 | ||
| * | ||
| * @param {object} options - Additional http2 server options. | ||
| * @returns {Promise<(function(): void)|*>} callback | ||
| */ | ||
| async function startHttp2Server(options = {}) { | ||
| // disabling to avoid UNABLE_TO_VERIFY_LEAF_SIGNATURE for tests | ||
| process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; | ||
| const servers = await createServersAsync({ | ||
| http2: { | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem', | ||
| ...options | ||
| }, | ||
| handler: (req, res) => { | ||
| const { httpVersion } = req; | ||
| const { socket: { alpnProtocol } } = httpVersion === '2.0' ? req.stream.session : req; | ||
| res.writeHead(200, { 'content-type': 'application/json' }); | ||
| res.end(JSON.stringify({ | ||
| alpnProtocol, | ||
| httpVersion | ||
| })); | ||
| } | ||
| }); | ||
| evilDNS.add('foo.example.org', '0.0.0.0'); | ||
| return function stop() { | ||
| servers && servers.http2 && servers.http2.close(); | ||
| evilDNS.clear(); | ||
| delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; | ||
| }; | ||
| } | ||
| test('only http', function (t) { | ||
| t.plan(5); | ||
| createServers({ | ||
| log: console.log, | ||
| http: 9876, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| console.dir(err); | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http, 'object'); | ||
| t.equals(servers.http instanceof Array, false); | ||
| t.equals(servers.https, undefined); | ||
| servers.http.close(); | ||
| }); | ||
| }); | ||
| test('only http, port 0', function (t) { | ||
| t.plan(4); | ||
| createServers({ | ||
| log: console.log, | ||
| http: 0, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| console.dir(err); | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http, 'object'); | ||
| t.equals(typeof servers.http.address().port, 'number'); | ||
| servers.http.close(); | ||
| }); | ||
| }); | ||
| test('only http, timeout', function (t) { | ||
| t.plan(5); | ||
| var time = 3000000; | ||
| createServers({ | ||
| log: console.log, | ||
| timeout: time, | ||
| http: 0, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| console.dir(err); | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http, 'object'); | ||
| t.equals(typeof servers.http.address().port, 'number'); | ||
| t.equals(servers.http.timeout, time); | ||
| servers.http.close(); | ||
| }); | ||
| }); | ||
| test('only https', function (t) { | ||
| t.plan(5); | ||
| createServers({ | ||
| log: console.log, | ||
| https: { | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.https, 'object'); | ||
| t.equals(servers.https instanceof Array, false); | ||
| t.equals(servers.http, undefined); | ||
| servers.https.close(); | ||
| }); | ||
| }); | ||
| test('only https', function (t) { | ||
| t.plan(4); | ||
| var time = 4000000; | ||
| createServers({ | ||
| log: console.log, | ||
| https: { | ||
| timeout: time, | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.https, 'object'); | ||
| t.equals(servers.https.timeout, time); | ||
| servers.https.close(); | ||
| }); | ||
| }); | ||
| test('only http2', function (t) { | ||
| t.plan(4); | ||
| var time = 4000000; | ||
| createServers({ | ||
| log: console.log, | ||
| http2: { | ||
| timeout: time, | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http2, 'object'); | ||
| t.equals(servers.http2.timeout, time); | ||
| servers.http2.close(); | ||
| }); | ||
| }); | ||
| test('absolute cert path resolution', function (t) { | ||
| t.plan(3); | ||
| createServers({ | ||
| log: console.log, | ||
| https: { | ||
| port: 3456, | ||
| root: '/', | ||
| cert: path.resolve(__dirname, 'fixtures', 'example-org-cert.pem'), | ||
| key: path.resolve(__dirname, 'fixtures', 'example-org-key.pem') | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.https, 'object'); | ||
| servers.https.close(); | ||
| }); | ||
| }); | ||
| test('http && https', function (t) { | ||
| t.plan(4); | ||
| createServers({ | ||
| log: console.log, | ||
| http: 8765, | ||
| https: { | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http, 'object'); | ||
| t.equals(typeof servers.https, 'object'); | ||
| servers.http.close(); | ||
| servers.https.close(); | ||
| }); | ||
| }); | ||
| test('provides useful debug information', async function (t) { | ||
| t.plan(4); | ||
| const config = { | ||
| log: console.log, | ||
| https: { | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| }, | ||
| handler: fend | ||
| }; | ||
| // Simulate a "port in use" error | ||
| const { https: server1 } = await createServersAsync(config); | ||
| try { | ||
| await createServersAsync(config); | ||
| } catch (err) { | ||
| t.equals(typeof err, 'object'); | ||
| t.equals(typeof err.https, 'object'); | ||
| t.equals(typeof err.message, 'string'); | ||
| t.notEqual(err.message, 'Unspecified error'); | ||
| } finally { | ||
| server1.close(); | ||
| } | ||
| }); | ||
| test('http && https with different handlers', function (t) { | ||
| t.plan(4); | ||
| createServers({ | ||
| log: console.log, | ||
| http: { | ||
| handler: function (req, res) { | ||
| res.end('http'); | ||
| }, | ||
| port: 8765 | ||
| }, | ||
| https: { | ||
| handler: function (req, res) { | ||
| res.end('https'); | ||
| }, | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| } | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http, 'object'); | ||
| t.equals(typeof servers.https, 'object'); | ||
| servers.http.close(); | ||
| servers.https.close(); | ||
| }); | ||
| test('only http with string type input for http port', function (t) { | ||
| t.plan(3); | ||
| createServers({ | ||
| log: console.log, | ||
| http: '9876', | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http, 'object'); | ||
| servers.http.close(); | ||
| }); | ||
| }); | ||
| test('host can be provided to the server', function (t) { | ||
| t.plan(4); | ||
| createServers({ | ||
| log: console.log, | ||
| http: { | ||
| port: 9877, | ||
| host: '127.0.0.1' | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.http, 'object'); | ||
| t.equals(servers.http.address().address, '127.0.0.1'); | ||
| servers.http.close(); | ||
| }); | ||
| }); | ||
| }); | ||
| test('supports cert contents instead of cert paths', function (t) { | ||
| t.plan(3); | ||
| var root = path.join(__dirname, 'fixtures'); | ||
| createServers({ | ||
| log: console.log, | ||
| https: { | ||
| port: 3456, | ||
| root: root, | ||
| cert: fs.readFileSync(path.resolve(root, 'example-org-cert.pem')), | ||
| key: fs.readFileSync(path.resolve(root, 'example-org-key.pem')) | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.https, 'object'); | ||
| servers.https.close(); | ||
| }); | ||
| }); | ||
| test('supports cert array instead of strings', function (t) { | ||
| t.plan(3); | ||
| var root = path.join(__dirname, 'fixtures'); | ||
| createServers({ | ||
| log: console.log, | ||
| https: { | ||
| port: 3456, | ||
| root: root, | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals(typeof servers, 'object'); | ||
| t.equals(typeof servers.https, 'object'); | ||
| servers.https.close(); | ||
| }); | ||
| }); | ||
| test('supports creating certificate chains', function (t) { | ||
| t.plan(2); | ||
| var root = path.join(__dirname, 'fixtures'); | ||
| var agent3Cert = fs.readFileSync(path.resolve(root, 'agent3-cert.pem')); | ||
| var intermediate = fs.readFileSync( | ||
| path.resolve(root, 'intermediate-cert.pem') | ||
| ); | ||
| var spy = sinon.spy(https, 'createServer'); | ||
| createServers({ | ||
| log: console.log, | ||
| https: { | ||
| port: 3456, | ||
| root: root, | ||
| cert: ['agent3-cert.pem', 'intermediate-cert.pem'], | ||
| key: 'agent3-key.pem' | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| const expectedBundle = [agent3Cert, intermediate].join('\n'); | ||
| const cert = spy.lastCall.args[0].cert; | ||
| t.equals(cert, expectedBundle, 'should create a cert chain'); | ||
| servers.https.close(); | ||
| spy.restore(); | ||
| }); | ||
| }); | ||
| test('supports requestCert https option', function (t) { | ||
| t.plan(2); | ||
| var spy = sinon.spy(https, 'createServer'); | ||
| createServers({ | ||
| log: console.log, | ||
| https: { | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem', | ||
| requestCert: true | ||
| }, | ||
| handler: fend | ||
| }, function (err, servers) { | ||
| t.error(err); | ||
| t.equals( | ||
| spy.lastCall.args[0].requestCert, | ||
| true, | ||
| 'should preserve the requestCert option' | ||
| ); | ||
| servers.https.close(); | ||
| spy.restore(); | ||
| }); | ||
| }); | ||
| test('supports SNI', async t => { | ||
| await testSni(t, { | ||
| 'example.com': { | ||
| key: 'example-com-key.pem', | ||
| cert: 'example-com-cert.pem' | ||
| }, | ||
| 'example.net': { | ||
| key: 'example-net-key.pem', | ||
| cert: 'example-net-cert.pem' | ||
| }, | ||
| '*.example.org': { | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| } | ||
| }, ['example.com', 'example.net', 'foo.example.org']); | ||
| }); | ||
| test('supports catch-all * for SNI', async t => { | ||
| await testSni(t, { | ||
| 'example.com': { | ||
| key: 'example-com-key.pem', | ||
| cert: 'example-com-cert.pem' | ||
| }, | ||
| '*': { | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| } | ||
| }, ['example.com', 'foo.example.org']); | ||
| }); | ||
| test('multiple https servers', async function (t) { | ||
| t.plan(2); | ||
| evilDNS.add('foo.example.org', '0.0.0.0'); | ||
| const servers = await createServersAsync({ | ||
| log: console.log, | ||
| https: [ | ||
| { | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| }, | ||
| { | ||
| port: 6543, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| key: 'example-org-key.pem', | ||
| cert: 'example-org-cert.pem' | ||
| } | ||
| ], | ||
| handler: (req, res) => { | ||
| res.end('Hello'); | ||
| } | ||
| }); | ||
| try { | ||
| t.equals(servers.https.length, 2, 'two servers were created'); | ||
| const responses = await Promise.all([ | ||
| download('https://foo.example.org:3456/'), | ||
| download('https://foo.example.org:6543/') | ||
| ]); | ||
| t.equals( | ||
| responses.every(str => str === 'Hello'), | ||
| true, | ||
| 'responses are as expected' | ||
| ); | ||
| } finally { | ||
| let toClose = | ||
| servers.https instanceof Array ? servers.https : [servers.https]; | ||
| toClose.forEach(server => server.close()); | ||
| evilDNS.clear(); | ||
| } | ||
| }); | ||
| test('supports http2-only requests', async function (t) { | ||
| t.plan(2); | ||
| const url = 'https://foo.example.org:3456/'; | ||
| let stopServer; | ||
| try { | ||
| stopServer = await startHttp2Server({}); | ||
| const httpsResponse = await download(url); | ||
| t.ok(httpsResponse.includes('Unknown ALPN Protocol')); | ||
| const response = JSON.parse(await download2(url)); | ||
| t.equals(response.httpVersion, '2.0'); | ||
| } catch (err) { | ||
| t.error(err); | ||
| } finally { | ||
| stopServer && stopServer(); | ||
| } | ||
| }); | ||
| test('supports http2 and https requests', async function (t) { | ||
| t.plan(2); | ||
| const url = 'https://foo.example.org:3456/'; | ||
| let stopServer; | ||
| try { | ||
| stopServer = await startHttp2Server({ | ||
| allowHTTP1: true | ||
| }); | ||
| const httpsResponse = JSON.parse(await download(url)); | ||
| t.equals(httpsResponse.httpVersion, '1.1'); | ||
| const response = JSON.parse(await download2(url)); | ||
| t.equals(response.httpVersion, '2.0'); | ||
| } catch (err) { | ||
| t.error(err); | ||
| } finally { | ||
| stopServer && stopServer(); | ||
| } | ||
| }); | ||
| async function testSni(t, sniConfig, hostNames) { | ||
| t.plan(1); | ||
| let httpsServer; | ||
| try { | ||
| const servers = await createServersAsync({ | ||
| https: { | ||
| port: 3456, | ||
| root: path.join(__dirname, 'fixtures'), | ||
| sni: sniConfig | ||
| }, | ||
| handler: (req, res) => { | ||
| res.write('Hello'); | ||
| res.end(); | ||
| } | ||
| }); | ||
| httpsServer = servers.https; | ||
| hostNames.forEach(host => evilDNS.add(host, '0.0.0.0')); | ||
| const responses = await Promise.all( | ||
| hostNames.map(hostname => download(`https://${hostname}:3456/`)) | ||
| ); | ||
| t.equals( | ||
| responses.every(str => str === 'Hello'), | ||
| true, | ||
| 'responses are as expected' | ||
| ); | ||
| } catch (err) { | ||
| return void t.error(err); | ||
| } finally { | ||
| httpsServer && httpsServer.close(); | ||
| evilDNS.clear(); | ||
| } | ||
| } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
2
-60%5
-28.57%26989
-56.46%4
-84%336
-63.36%