diff --git a/lib/Connection.js b/lib/Connection.js index 490d329..336f4d1 100644 --- a/lib/Connection.js +++ b/lib/Connection.js @@ -58,6 +58,8 @@ const availableTransports = { * @property {string} [password] - The user's password. * @property {string} [privateKey] - The user's privateKey. * @property {string} [passphrase] - The user's passphrase to decrypt the private key. + * @property {object} [sshClient] - An ssh2 Client instance that is already connected and + * ready to exec a command. */ /** diff --git a/lib/transports/sshTransport.js b/lib/transports/sshTransport.js index 450feaf..edcd13e 100644 --- a/lib/transports/sshTransport.js +++ b/lib/transports/sshTransport.js @@ -17,23 +17,110 @@ const { Client } = require('ssh2'); -function sshCall(config, xmlIn, done) { +/** + * Function to call xmlservice regardless if an existing connection was passed + * @param {Object} parameters - Arguments to call xmlservice-cli + * @private + */ +function callXmlService(parameters) { const { - verbose = false, - } = config; + client, xmlIn, done, verbose, shouldEndClient, + } = parameters; let xmlOut = ''; const xmlBuffer = Buffer.from(xmlIn); - const client = new Client(); + client.exec('/QOpenSys/pkgs/bin/xmlservice-cli', (error, stream) => { + if (error) { + if (verbose) { + console.log('Exec error: ', error); + } + client.emit('error', error); + return; + } + stream.on('exit', (code, signal, didCoreDump, description) => { + if (verbose) { + console.log(`Stream exit code: ${code}`); + if (signal) { + console.log(`Signal: ${signal}, Description: ${description}`); + } + } + + if (signal) { + client.emit('error', new Error(`xmlservice-cli was signaled with: ${signal}`)); + return; + } - // subscribe for events + if (code !== 0) { + client.emit('error', new Error(`xmlservice-cli exited abnormally with code: ${code}`)); + return; + } + if (shouldEndClient) { + client.end(); + client.destroy(); + } + done(null, xmlOut); + }); + + stream.stdin.on('end', () => { + if (verbose) { + console.log('stdin has ended'); + } + }); + + stream.stdout.on('end', () => { + if (verbose) { + console.log('stdout has ended'); + } + }); + + stream.stdout.on('data', (data) => { + xmlOut += data.toString(); + if (verbose) { + console.log(`STDOUT:\n${data}`); + } + }); + + stream.stderr.on('data', (data) => { + if (verbose) { + console.log(`STDERR:\n${data}`); + } + }); + + stream.stdin.write(xmlBuffer); + stream.stdin.end(); + }); +} + +/** + * @private + * @param {object} config - Configuration options for ssh transport + * @param {string} xmlIn - The xml input to run with xml service + * @param {function} done - User defined callback to invoke when completed + */ +function sshCall(config, xmlIn, done) { + const { + sshClient, verbose = false, + } = config; + + const client = sshClient || new Client(); + const shouldEndClient = !sshClient; // Should not end and destroy passed in client + + // Ensure passed in client really is an instance of ssh2.Client + if (!(client instanceof Client)) { + done('The existing connection is not an ssh2 Client instance', null); + return; + } + + // setup event listeners client.on('error', (error) => { if (verbose) { console.log('SSH CLIENT ERROR: ', error); } - client.end(); - client.destroy(); + if (shouldEndClient) { + client.end(); + client.destroy(); + } done(error, null); }); @@ -49,71 +136,26 @@ function sshCall(config, xmlIn, done) { } }); - client.on('ready', () => { - if (verbose) { - console.log('SSH Client is ready'); - } - client.exec('/QOpenSys/pkgs/bin/xmlservice-cli', (error, stream) => { - if (error) { - if (verbose) { - console.log('Exec error: ', error); - } - client.emit('error', error); - return; + const parameters = { + client, xmlIn, done, verbose, shouldEndClient, + }; + + if (!sshClient) { + // ssh2 client is event driven + // the ready event emits once authtenticatation was succefully + // we can only call xmlservice after the ready event has fired + client.on('ready', () => { + if (verbose) { + console.log('SSH Client is ready'); } - stream.on('exit', (code, signal, didCoreDump, description) => { - if (verbose) { - console.log(`Stream exit code: ${code}`); - if (signal) { - console.log(`Signal: ${signal}, Description: ${description}`); - } - } - - if (signal) { - client.emit('error', new Error(`xmlservice-cli was signaled with: ${signal}`)); - return; - } - - if (code !== 0) { - client.emit('error', new Error(`xmlservice-cli exited abnormally with code: ${code}`)); - return; - } - client.end(); - client.destroy(); - done(null, xmlOut); - }); - - stream.stdin.on('end', () => { - if (verbose) { - console.log('stdin has ended'); - } - }); - - stream.stdout.on('end', () => { - if (verbose) { - console.log('stdout has ended'); - } - }); - - stream.stdout.on('data', (data) => { - xmlOut += data.toString(); - if (verbose) { - console.log(`STDOUT:\n${data}`); - } - }); - - stream.stderr.on('data', (data) => { - if (verbose) { - console.log(`STDERR:\n${data}`); - } - }); - - stream.stdin.write(xmlBuffer); - stream.stdin.end(); + callXmlService(parameters); }); - }); - client.connect(config); + client.connect(config); + } else { + // client should already be connected and ready to call xml service + callXmlService(parameters); + } } exports.sshCall = sshCall;