From bd99b9daa0ecc29f28a162c32a7035412d7441e6 Mon Sep 17 00:00:00 2001 From: Aral Balkan Date: Sun, 5 Apr 2020 16:35:56 +0100 Subject: [PATCH] Change to async API that handles waiting for Pebble to boot --- CHANGELOG.md | 6 ++++++ README.md | 29 +++++----------------------- example/index.js | 25 +++++------------------- index.js | 20 +++++++++++++++++--- package.json | 2 +- test/index.js | 49 +++++++----------------------------------------- 6 files changed, 41 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a042a4..7f18c9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), Nothing yet. +## [2.0.0] - 2020-04-05 + +### Changes + + - Breaking change: new asynchronous API + ## [1.0.0] - 2020-04-05 Initial release. diff --git a/README.md b/README.md index cf0faa9..915bee3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ npm i @small-tech/node-pebble ## API ```js -const pebbleProcess = Pebble.spawn([args], [env]) +const pebbleProcess = await Pebble.spawn([args], [env]) ``` ### Parameters @@ -36,7 +36,7 @@ const pebbleProcess = Pebble.spawn([args], [env]) ### Return value -`ChildProcess` instance of the spawned Pebble server instance. +`Promise` a promise that is resolved to a reference of the spawned Pebble server instance once the Pebble server instance has finished booting. When this promise resolves, Pebble is ready to use. ## Default configuration @@ -65,32 +65,13 @@ Pebble.spawn('-config customConfig.json') ## Basic example -The following listing launches the Pebble server with its default settings, displays output and errors, and shuts the server down after 5 seconds have elapsed. +The following listing launches the Pebble server with its default settings and then shuts it down. ```js const Pebble = require('node-pebble') -const pebbleProcess = Pebble.spawn() - -pebbleProcess.on('error', (error) => { - console.log('[Pebble] Process error', error) -}) - -pebbleProcess.stdout.on('data', (data) => { - console.log(`[Pebble] ${data}`) -}) - -pebbleProcess.stderr.on('data', (data) => { - console.log(`[Pebble] Error ${data}`) -}) - -pebbleProcess.on('close', (code) => { - console.log('Pebble server process exited with code', code) -}) - -setTimeout(() => { - pebbleProcess.kill() -}, 3000) +const pebbleProcess = await Pebble.spawn() +pebbleProcess.kill() ``` ## Install development dependencies (for tests and coverage) diff --git a/example/index.js b/example/index.js index 0a5d13f..bdf9879 100644 --- a/example/index.js +++ b/example/index.js @@ -3,24 +3,9 @@ */ const Pebble = require('..') -const pebbleProcess = Pebble.spawn() - -pebbleProcess.on('error', (error) => { - console.log('[Pebble] Process error', error) -}) - -pebbleProcess.stdout.on('data', (data) => { - console.log(`[Pebble] ${data}`) -}) - -pebbleProcess.stderr.on('data', (data) => { - console.log(`[Pebble] Error ${data}`) -}) - -pebbleProcess.on('close', (code) => { - console.log('Pebble server process exited with code', code) -}) - -setTimeout(() => { +async function main() { + const pebbleProcess = await Pebble.spawn() pebbleProcess.kill() -}, 5000) +} + +main() diff --git a/index.js b/index.js index 673c21c..c723bcf 100644 --- a/index.js +++ b/index.js @@ -19,12 +19,12 @@ const spawn = childProcess.spawn */ class Pebble { /** - * Spawns Pebble process and returns a reference to it. + * Promises to spawn a Pebble process and resolve the promise when the server is ready for use. * * @static * @args {[String[]]|String} Optional space-delimited list or array of arguments to pass to Pebble process. * @env {Object={ PEBBLE_VA_NOSLEEP: 1, PEBBLE_WFE_NONCEREJECT: 0 }} Optional environment variables to set for Pebble process. - * @returns {ChildProcess} Reference to spawned child process. You’re responsible for managing its life-cycle. + * @returns {Promise} Promise to return spawned child process. You’re responsible for managing its life-cycle. */ static spawn (args = [], env = { PEBBLE_VA_NOSLEEP: 1, PEBBLE_WFE_NONCEREJECT: 0 }) { // Spawn expects argument to be an array. Automatically convert a space-delimited arguments string to one. @@ -43,7 +43,21 @@ class Pebble { const pebbleProcess = spawn(pebbleBinaryPath, args, options) - return pebbleProcess + let output = '' + + return new Promise((resolve, reject) => { + const timeoutInterval = setTimeout(() => { + reject('Timed out while attempting to spawn Pebble server (waited 5 seconds).') + }, 5000) + + pebbleProcess.stdout.on('data', data => { + output = `${output}${data}` + if (output.includes('ACME directory available at: https://0.0.0.0:14000/dir')) { + clearInterval(timeoutInterval) + resolve(pebbleProcess) + } + }) + }) } } diff --git a/package.json b/package.json index 3f7bf31..f76c4b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@small-tech/node-pebble", - "version": "1.0.0", + "version": "2.0.0", "description": "A Node.js wrapper for Let’s Encrypt’s Pebble (“a small RFC 8555 ACME test server not suited for a production certificate authority”).", "main": "index.js", "os": ["linux", "win32"], diff --git a/test/index.js b/test/index.js index d8cdeb5..ba39c4c 100644 --- a/test/index.js +++ b/test/index.js @@ -1,51 +1,16 @@ const test = require('tape') const Pebble = require('..') -test ('Node Pebble', t => { - const expectedOutputContents = [ - 'Starting Pebble ACME server', - 'Generated new root issuer', - 'Generated new intermediate issuer', - 'Using system DNS resolver for ACME challenges', - 'Disabling random VA sleeps', - 'Configured to reject 0% of good nonces', - 'Configured to attempt authz reuse for each identifier 50% of the time', - 'Configured to show 3 orders per page', - 'Management interface listening on: 0.0.0.0:15000', - 'Root CA certificate available at: https://0.0.0.0:15000/roots/0', - 'Listening on: 0.0.0.0:14000', - 'ACME directory available at: https://0.0.0.0:14000/dir' - ] +test ('Node Pebble', async t => { + const pebbleProcess = await Pebble.spawn() - t.plan(expectedOutputContents.length) - - const pebbleProcess = Pebble.spawn() - - let outputBuffer = Buffer.from('') - - pebbleProcess.on('error', (error) => { - t.fail('Spawn should not error') - }) - - pebbleProcess.stdout.on('data', (data) => { - outputBuffer = Buffer.concat([outputBuffer, data]) - }) - - pebbleProcess.stderr.on('data', (data) => { - t.fail('Pebble server should not error') - }) + t.pass('pebble process launches as expected') pebbleProcess.on('close', (code) => { - output = outputBuffer.toString('utf-8') - - expectedOutputContents.forEach(expectedOutputContent => { - t.ok(output.includes(expectedOutputContent), `output should contain ${expectedOutputContent}`) - }) - + t.pass('pebble process killed as expected') t.end() }) - setTimeout(() => { - pebbleProcess.kill() - }, 2000) -}) \ No newline at end of file + pebbleProcess.kill() +}) +