From 34a0d8b1ae9d47492f7fd4b628dbf15ddfd760ac Mon Sep 17 00:00:00 2001 From: Herve Eichwald Date: Mon, 2 Dec 2019 12:31:29 -0500 Subject: [PATCH] Add support for multiple commands and interactive mode - Can send multiple frames - Can send frames with `-x` and still use the interactive mode - Configure delays between sending frames with `-x` - Always display closure codes --- bin/wscat | 105 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/bin/wscat b/bin/wscat index 59a9d88..37e8e9b 100755 --- a/bin/wscat +++ b/bin/wscat @@ -114,8 +114,15 @@ program .option('-c, --connect ', 'connect to a WebSocket server') .option('-p, --protocol ', 'optional protocol version') .option('-o, --origin ', 'optional origin') - .option('-x, --execute ', 'execute command after connecting') - .option('-w, --wait ', 'wait given seconds after executing command') + .option('-x, --execute ', + 'execute command after connecting. Repeat to set multiple', + collect, [] + ) + .option( + '--execute-interval ', + 'Optional interval in millis between sending subsequent commands. Initial delay for first command is 0 millis.' + ) + .option('-w, --wait ', 'optional: wait given seconds after executing command before closing') .option( '-P, --show-ping-pong', 'print a notification when a ping or pong is received' @@ -126,8 +133,7 @@ program .option( '-H, --header ', 'set an HTTP header. Repeat to set multiple (--connect only)', - collect, - [] + collect, [] ) .option( '--auth ', @@ -142,13 +148,17 @@ program .option( '--passphrase [passphrase]', "specify a Client SSL Certificate Key's passphrase (--connect only). " + - "If you don't provide a value, it will be prompted for" + "If you don't provide a value, it will be prompted for" + ) + .option( + '--non-interactive', + 'do not open an interactive console after executing commands' ) .option('--no-color', 'run without color') .option( '--slash', 'enable slash commands for control frames (/ping, /pong, /close ' + - '[code [, reason]])' + '[code [, reason]])' ) .option( '--proxy <[protocol://]host[:port]>', @@ -166,7 +176,9 @@ if (program.listen) { wsConsole.pause(); let ws = null; - const wss = new WebSocket.Server({ port: program.listen }, () => { + const wss = new WebSocket.Server({ + port: program.listen + }, () => { wsConsole.print( Console.Types.Control, `Listening on port ${program.listen} (press CTRL+C to quit)`, @@ -240,6 +252,8 @@ if (program.listen) { headers.Authorization = 'Basic ' + Buffer.from(program.auth).toString('base64'); } + if (program.execute) options.commands = program.execute; + options.interactive = !program.nonInteractive; if (program.host) headers.Host = program.host; if (program.protocol) options.protocolVersion = +program.protocol; if (program.origin) options.origin = program.origin; @@ -261,16 +275,29 @@ if (program.listen) { options.headers = headers; const ws = new WebSocket(connectUrl, program.subprotocol, options); + var scheduleSendCommand = (command, position) => { + setTimeout( + () => { + ws.send(command) + }, + program.executeInterval ? program.executeInterval * position : 0 + ); + }; + ws.on('open', () => { - if (program.execute) { - ws.send(program.execute); - setTimeout( - () => { - ws.close(); - }, - program.wait ? program.wait * 1000 : 2000 - ); - } else { + if (options.commands) { + options.commands.forEach(scheduleSendCommand); + if (program.wait) { + setTimeout( + () => { + ws.close(); + }, + program.wait * 1000 + ); + } + } + + if (options.interactive) { wsConsole.print( Console.Types.Control, 'Connected (press CTRL+C to quit)', @@ -287,22 +314,23 @@ if (program.listen) { case 'pong': ws.pong(noop); break; - case 'close': { - let closeStatusCode = 1000; - let closeReason = ''; - if (toks.length >= 2) { - closeStatusCode = parseInt(toks[1]); - } - if (toks.length >= 3) { - closeReason = toks.slice(2).join(' '); + case 'close': + { + let closeStatusCode = 1000; + let closeReason = ''; + if (toks.length >= 2) { + closeStatusCode = parseInt(toks[1]); + } + if (toks.length >= 3) { + closeReason = toks.slice(2).join(' '); + } + if (closeReason.length > 0) { + ws.close(closeStatusCode, closeReason); + } else { + ws.close(closeStatusCode); + } + break; } - if (closeReason.length > 0) { - ws.close(closeStatusCode, closeReason); - } else { - ws.close(closeStatusCode); - } - break; - } default: wsConsole.print( Console.Types.Error, @@ -319,13 +347,11 @@ if (program.listen) { }); ws.on('close', (code, reason) => { - if (!program.execute) { - wsConsole.print( - Console.Types.Control, - `Disconnected (code: ${code}, reason: "${reason}")`, - Console.Colors.Green - ); - } + wsConsole.print( + Console.Types.Control, + `Disconnected (code: ${code}, reason: "${reason}")`, + Console.Colors.Green + ); wsConsole.clear(); process.exit(); }); @@ -366,8 +392,7 @@ if (program.listen) { }; if (program.passphrase === true) { - read( - { + read({ prompt: 'Passphrase: ', silent: true, replace: '*'