diff options
author | Andreas Fankhauser hiddenalpha.ch | 2022-12-01 11:50:06 +0100 |
---|---|---|
committer | Andreas Fankhauser hiddenalpha.ch | 2022-12-01 11:50:06 +0100 |
commit | 26365aed4c321b91cf6ea737cd23eff37341d5f1 (patch) | |
tree | d5a663866e986b35575d803e7b7ea6c65dbed60f | |
parent | 6e7d341e6ba407e2f22ddbf3faec36f0876a8a67 (diff) | |
parent | 68e9acb83b6590a3298b3636d1a65d7725eed87f (diff) | |
download | UnspecifiedGarbage-26365aed4c321b91cf6ea737cd23eff37341d5f1.zip UnspecifiedGarbage-26365aed4c321b91cf6ea737cd23eff37341d5f1.tar.gz |
Merge 'Add HttpFlood js utils from obsolete project' to master
-rw-r--r-- | src/main/nodejs/HttpFlood/HttpFlood.js | 227 | ||||
-rw-r--r-- | src/main/nodejs/HttpFlood/HttpNullsink.js | 130 | ||||
-rw-r--r-- | src/main/nodejs/HttpFlood/SetGateleenHook.js | 127 | ||||
-rw-r--r-- | src/main/nodejs/HttpFlood/putshowcaseresources.js | 133 |
4 files changed, 617 insertions, 0 deletions
diff --git a/src/main/nodejs/HttpFlood/HttpFlood.js b/src/main/nodejs/HttpFlood/HttpFlood.js new file mode 100644 index 0000000..e6cd545 --- /dev/null +++ b/src/main/nodejs/HttpFlood/HttpFlood.js @@ -0,0 +1,227 @@ +;(function(){ "use strict"; + +const http = require("http"); +const util = require("util"); +const DevNull = { write:function(){} }; + +const hrtime = process.hrtime; +const stdin = process.stdin ; +const stdlog = process.stderr; +const stdout = process.stdout; +const noop = function(){}; + + +setTimeout(main); + + +function printHelp(){ + stdout.write("\n" + +" hiddenalphas HTTP pressure test utility.\n" + +"\n" + +"Options:\n" + +"\n" + +" --host <ip|hostname>\n" + +" Eg: 127.0.0.1\n" + +"\n" + +" --port <int>\n" + +" Eg: 7013\n" + +"\n" + +" --path <str>\n" + +" Eg: /houston/services/nullsink\n" + +"\n" + +" --max-parallel <int>\n" + +" Defaults to 42.\n" + +"\n" + +" --inter-request-gap <int>\n" + +" Milliseconds to wait before starting another request when the previous\n" + +" one has ended.\n" + +" Defaults to zero.\n" + +"\n"); +} + + +function parseArgs( cls_flood, argv ){ + // Some defaults + cls_flood.maxParallel = 42; + cls_flood.interRequestGapMs = 0; + // Parse args + for( var i=2 ; i<argv.length ; ++i ){ + var arg = argv[i]; + if( arg == "--help" ){ + printHelp(); + return -1; + }else if( arg == "--host" ){ + cls_flood.host = argv[++i]; + if( !cls_flood.host ){ stdlog.write("Arg --host: Value missing\n"); return -1; } + }else if( arg == "--port" ){ + cls_flood.port = parseInt(argv[++i]); + if( isNaN(cls_flood.port) ){ stdlog.write("Arg --port: Cannot parse "+ argv[i]+"\n"); return -1; } + }else if( arg == "--path" ){ + cls_flood.reqPath = argv[++i]; + if( !cls_flood.reqPath ){ stdlog.write("Arg --path: Value missing\n"); return -1; } + }else if( arg == "--max-parallel"){ + cls_flood.maxParallel = parseInt(argv[++i]); + if( isNaN(cls_flood.maxParallel) ){ stdlog.write("Arg --max-parallel: Cannot parse "+ argv[i]+"\n"); return -1; } + }else if( arg == "--inter-request-gap"){ + cls_flood.interRequestGapMs = parseInt(argv[++i]); + if( isNaN(cls_flood.interRequestGapMs) ){ stdlog.write("Arg --inter-request-gap: Cannot parse "+argv[i]); return -1; } + }else{ + stdlog.write("Unknown arg: "+ arg +"\n"); + return -1; + } + } + // A few validity checks. + if( cls_flood.host === null ){ stdlog.write("Arg --host missing\n"); return -1; } + if( cls_flood.port === null ){ stdlog.write("Arg --port missing\n"); return -1; } + if( cls_flood.reqPath === null ){ stdlog.write("Arg --path missing\n"); return -1; } + if( ! cls_flood.reqPath.startsWith("/") ){ cls_flood.reqPath = "/"+ cls_flood.reqPath; } + return 0; +} + + +function main() { + const cls_flood = Object.seal({ + host: null, port: null, reqPath: null, + // + interRequestGapMs: null, + isNullsink: null, + maxParallel: null, + totalReqCount: 0, + statsIntervalMs: 3000, + httpAgent: null, + method: "PUT", + printLowMs: 50, + printHigMs: Number.MAX_SAFE_INTEGER, + }); + + if( parseArgs(cls_flood, process.argv) ) return; + + cls_flood.httpAgent = new http.Agent({ + keepAlive:true, maxSockets:cls_flood.maxParallel, keepAliveMsecs: 42000 + }); + + flood_sendParallelRequests(cls_flood); + stdin.on("data", function(){}); // <- Allows entering newlines in console :) +} + + +function flood_sendParallelRequests( cls_flood ){ + stdlog.write("Flood '"+ cls_flood.method +" " + + cls_flood.host +":"+ cls_flood.port + cls_flood.reqPath +"'\n '- on " + + cls_flood.maxParallel +" connections. Print if t > " + + cls_flood.printLowMs + +"\n" ); + let floodBegin = hrtime(); + let prevStatsPrint = floodBegin; + let numRequestsTotl = 0; + let numRequests = 0; + for( let iSt=0 ; iSt < cls_flood.maxParallel ; ++iSt ){ + fireOne(); + } + function fireOne(){ flood_performHttpRequest(cls_flood, onOneDone); } + function onOneDone(){ + numRequests += 1; + let now = hrtime(); + let msSinceStatsPrint = hrtimeDiffMs(now, prevStatsPrint); + if( msSinceStatsPrint > cls_flood.statsIntervalMs ){ + numRequestsTotl += numRequests; + printStats(now, numRequestsTotl, numRequests, msSinceStatsPrint); + prevStatsPrint = now; + numRequests = 0; + } + // Use the free slot to fire another request. + if( cls_flood.interRequestGapMs > 0 ){ + setTimeout(fireOne, cls_flood.interRequestGapMs); + }else{ + fireOne(); + } + } + function printStats( now, numRequestsTotl, numRequests, msSinceStatsPrint ){ + const reqPerSecStr = (" "+ Math.floor(numRequests / msSinceStatsPrint * 1000)).substr(-6); + const numReqTotalStr = (" "+ numRequestsTotl).substr(-9); + const runningSinceSecStr = (" "+ Math.floor(hrtimeDiffMs(now, floodBegin)/1000)).substr(-9); + stdlog.write("Stats: " + + reqPerSecStr +"/sec, " + + numReqTotalStr +" req total, " + + runningSinceSecStr +"s running" + +"\n"); + } +} + + +function flood_performHttpRequest( cls_flood, onResponseEndCb ) { + const cls_req = Object.seal({ + cls_flood: cls_flood, + onResponseEndCb: onResponseEndCb || noop, + req: null, rsp: null, + tsReqBegin: hrtime(), tsRspBegin: null, tsRspEnd: null, + }); + let path = cls_flood.reqPath; + let headers = undefined; + if( path.indexOf("/{vehicleId}/") != -1 ){ + let vehicleId = "vehiku00"+ Math.floor(Math.random()*4000); + path = path.replace( /\/{vehicleId}\//, "/"+ vehicleId +"/" ); + headers = { "x-vehicleid": vehicleId }; + } + const req = cls_req.req = http.request({ + hostname: cls_flood.host, port: cls_flood.port, + method: cls_flood.method, path: path, + headers: headers, + agent: cls_flood.httpAgent, + }); + req.on("error", function( err ){ console.error(err); }); + req.on("response", function( rsp ){ + cls_req.rsp = rsp; + cls_req.tsRspBegin = hrtime(); + rsp.on("data", onResponseData.bind(0,cls_req)); + rsp.on("end", onResponseEnd.bind(0,cls_req)); + let s = rsp.statusCode; + if( s == 200 || s == 404 ){ + // Fine + }else{ + stdlog.write( "Received a: HTTP "+ rsp.statusCode +" "+ rsp.statusMessage +"\n" ); + } + }); + if( cls_flood.method != "GET" ){ + req.write( '{ "info":"Nume es guguseli tscheison zum testle" }' ); + } + req.end(); +} + + +function onResponseData( cls_req, rspBodyChunk ) { + const rsp = cls_req.rsp; + if( ! rsp.isContinuedBodyChunk ){ + rsp.isContinuedBodyChunk = true; +// stdout.write("\n"); + } +// stdout.write(rspBodyChunk); +// stdout.write("\n"); +} + + +function onResponseEnd( cls_req, rsp ){ + const cls_flood = cls_req.cls_flood; + cls_req.tsRspEnd = hrtime(); + let reqTime = Math.round(hrtimeDiffMs( cls_req.tsRspBegin, cls_req.tsReqBegin )); + let rspTime = Math.round(hrtimeDiffMs( cls_req.tsRspEnd, cls_req.tsRspBegin )); + let totTime = Math.round(hrtimeDiffMs( cls_req.tsRspEnd, cls_req.tsReqBegin )); + if( totTime < cls_flood.printLowMs ){ + // Do NOT print + }else if( totTime > cls_flood.printHigMs ){ + // Do NOT print + }else{ +// stdlog.write(util.format( "%s%d%s%d%s%d\n", +// "HttpCycle: ", totTime, "ms, TTFB: ", reqTime, ", DownloadMs: ", rspTime )); + } + cls_req.onResponseEndCb(); +} + + +function hrtimeDiffMs( subtrahend, minuend ){ + return 1000 * (subtrahend[0] - minuend[0]) + + 0.000001 * (subtrahend[1] - minuend[1]) ; +} + + +}()); /*endOfModuleScope*/ diff --git a/src/main/nodejs/HttpFlood/HttpNullsink.js b/src/main/nodejs/HttpFlood/HttpNullsink.js new file mode 100644 index 0000000..97e1653 --- /dev/null +++ b/src/main/nodejs/HttpFlood/HttpNullsink.js @@ -0,0 +1,130 @@ +;(function(){ "use strict"; + +const http = require("http"); +const util = require("util"); +const DevNull = { write:function(){} }; + +const hrtime = process.hrtime; +const stdin = process.stdin ; +const stdout = process.stdout; +const stdlog = process.stderr; + + +setTimeout(main); + + +function printHelp(){ + stdout.write("\n" + +" hiddenalphas simple HTTP server just responding '200 OK' for every\n" + +" incoming request.\n" + +"\n" + +" Options:\n" + +"\n" + +" --host <ip|hostname> (default 127.0.0.1)\n" + +" Listen address.\n" + +"\n" + +" --port <int>\n" + +" Listen port.\n" + +"\n" + +" --statsIntervalMs <int> (default 5000)\n" + +" Interval when to print statistics.\n" + +"\n"); +} + + +function parseArgs( cls_nullsink, args ){ + cls_nullsink.host = "127.0.0.1"; + cls_nullsink.port = null; + cls_nullsink.statsIntervalMs = 5000; + for( let i=2 ; i<args.length ; ++i ){ + let arg = args[i]; + if( arg=="--help" ){ + printHelp(); return -1; + }else if( arg=="--host" ){ + cls_nullsink.host = args[++i]; + if( !args[i] ){ stdlog.write("Arg --host expects a value\n"); return -1; } + }else if( arg=="--port" ){ + cls_nullsink.port = parseInt(args[++i]); + if( isNaN(cls_nullsink.port) ){ stdlog.write("Arg --port: Cannot parse "+ argv[i]+"\n"); return -1; } + }else if( arg=="--statsIntervalMs" ){ + cls_nullsink.statsIntervalMs = parseInt(args[++i]); + if( isNaN(cls_nullsink.statsIntervalMs) ){ stdlog.write("Arg --statsIntervalMs: Cannot parse "+ argv[i]+"\n"); return -1; } + }else{ + stdlog.write("Unknown arg: "+ arg +"\n"); + } + } + if( cls_nullsink.host === null ){ stdlog.write("Arg --host missing\n"); return -1; } + if( cls_nullsink.port === null ){ stdlog.write("Arg --port missing\n"); return -1; } + return 0; +} + + +function main() { + const cls_nullsink = Object.seal({ + server: null, + host: null, + port: null, + backlog: undefined, + statsIntervalMs: null, + srvStart: hrtime(), + reqTotl: 0, + reqCnt: 0, + lastStats: hrtime(), + }); + if( parseArgs(cls_nullsink, process.argv) ) return; + launchServer(cls_nullsink); + logStatsPeriodically(cls_nullsink); + // Attaching a listener is enough to allow pressing enter in console. + stdin.on("data", function(){}); +} + + +function launchServer( cls_nullsink ){ + const server = cls_nullsink.server = http.createServer(onRequest.bind(0,cls_nullsink)); + server.listen(cls_nullsink.port, cls_nullsink.host, cls_nullsink.backlog); + stdlog.write("Server listening on " + + cls_nullsink.host +":"+ cls_nullsink.port + +" (backlog "+ cls_nullsink.backlog +")\n"); +} + + +function onRequest( cls_nullsink, req, rsp ){ + // Just respond "200 OK" for all requests. + cls_nullsink.reqCnt += 1; + rsp.writeHead(200); + rsp.end(); +} + + +function logStatsPeriodically( cls_nullsink ){ + scheduleOne(); + function scheduleOne(){ + setTimeout(log, cls_nullsink.statsIntervalMs-1); + } + function log(){ + const now = hrtime(); + const durationMs = Math.floor(hrtimeDiffMs(now, cls_nullsink.lastStats)); + const totlMs = Math.floor(hrtimeDiffMs(now, cls_nullsink.srvStart)) + cls_nullsink.reqTotl += cls_nullsink.reqCnt; + stdlog.write("Stats: Consumed " + + cls_nullsink.reqCnt +" req in " + + durationMs +" ms. So avg " + + Math.floor(cls_nullsink.reqCnt / durationMs * 1000) +" req/sec of overall " + + cls_nullsink.reqTotl +" req in " + + Math.floor(totlMs/1000) +" sec. Avg " + + Math.floor(cls_nullsink.reqTotl / totlMs) + +"\n"); + cls_nullsink.lastStats = now; + cls_nullsink.reqCnt = 0; + scheduleOne(); + } +} + + +function hrtimeDiffMs( subtrahend, minuend ){ + return 1000 * (subtrahend[0] - minuend[0]) + + 0.000001 * (subtrahend[1] - minuend[1]) ; +} + + +}()); /*endOfModule*/ diff --git a/src/main/nodejs/HttpFlood/SetGateleenHook.js b/src/main/nodejs/HttpFlood/SetGateleenHook.js new file mode 100644 index 0000000..2e3c27a --- /dev/null +++ b/src/main/nodejs/HttpFlood/SetGateleenHook.js @@ -0,0 +1,127 @@ +;(function(){ "use strict"; + +const http = require("http"); +const util = require("util"); +const DevNull = { write:function(){} }; + +const stdin = process.stdin ; +const stdout = process.stdout; +const stdlog = process.stderr; + + +setTimeout(main); + + +function printHelp(){ + stdout.write("\n" + +" hiddenalphas simple gateleen hook utilty.\n" + +"\n" + +" Options:\n" + +"\n" + +" --host <ip|hostname> (default 127.0.0.1)\n" + +" Gateleen to use.\n" + +"\n" + +" --port <int> (default 7012)\n" + +" Target port.\n" + +"\n" + +" --path <string>\n" + +" Target path where to set the hook for.\n" + +"\n" + +" --destination <url>\n" + +" Destination of the hook. Aka where gateleen will forward the\n" + +" requests to.\n" + +"\n" + +" --hook-timeout-sec <int> (default 300)\n" + +" Lifetime of the hook in seconds.\n" + +"\n" + +" --route\n" + +" Set a route hook.\n" + +"\n" + +" --listener\n" + +" Set a listener hook.\n" + +"\n"); +} + + +function parseArgs( cls_hook, args ){ + cls_hook.host = "127.0.0.1"; + cls_hook.port = 7012; + cls_hook.path = null; + cls_hook.destination = null; + cls_hook.hookTimeoutSec = 300; + cls_hook.isRoute = false; + cls_hook.isListener = false; + for( let i=2 ; i<args.length ; ++i ){ + let arg = args[i]; + if( arg == "--help" ){ + printHelp(); return -1; + }else if( arg == "--host" ){ + cls_hook.host = args[++i]; + if( !args[i] ){ stdlog.write("Arg --host expects a value\n"); return -1; } + }else if( arg == "--port" ){ + cls_hook.port = parseInt(args[++i]); + if( isNaN(cls_hook.port) ){ stdlog.write("Arg --port: Cannot parse "+ argv[i]+"\n"); return -1; } + }else if( arg == "--path" ){ + cls_hook.path = args[++i]; + if( !args[i] ){ stdlog.write("Arg --path expects a value\n"); return -1; } + }else if( arg == "--destination"){ + cls_hook.destination = args[++i]; + if( !args[i] ){ stdlog.write("Arg --destination expects a value\n"); return -1; } + }else if( arg == "--hook-timeout-sec"){ + cls_hook.hookTimeoutSec = parseInt(args[++i]); + if( isNaN(cls_hook.hookTimeoutSec) ){ stdlog.write("Parse --hook-timeout-sec failed: "+args[i]+"\n"); return -1; } + }else if( arg == "--route"){ + cls_hook.isRoute = true; + }else if( arg == "--isListener"){ + cls_hook.isListener = true; + }else{ + stdlog.write("Unknown arg: "+ arg +"\n"); + } + } + if( cls_hook.host === null ){ stdlog.write("Arg --host missing\n"); return -1; } + if( cls_hook.port === null ){ stdlog.write("Arg --port missing\n"); return -1; } + if( cls_hook.path === null ){ stdlog.write("Arg --path missing\n"); return -1; } + if( cls_hook.destination === null ){ stdlog.write("Arg --destination missing\n"); return -1; } + if( !cls_hook.isRoute && !cls_hook.isListener ){ stdlog.write("Need one of --route or --listener\n"); return -1; } + if( cls_hook.isRoute && cls_hook.isListener ){ stdlog.write("Cannot be --route and --listener simultaneously\n"); return -1; } + return 0; +} + + +function main() { + const cls_hook = Object.seal({ + host: null, + port: null, + path: null, + destination: null, + hookTimeoutSec: null, + isRoute: false, + isListener: false, + }); + if( parseArgs(cls_hook, process.argv) ) return; + setHook(cls_hook); +} + + +function setHook( cls_hook ){ + const req = http.request({ + hostname: cls_hook.host, port: cls_hook.port, + method: "PUT", + path: cls_hook.path + "/_hooks/"+ (cls_hook.isRoute ? "route" : "listener"), + headers: { + "Content-Type": "application/json", + }, + }); + req.on("error", function( err ){ console.error(err); }); + req.on("response", function( rsp ){ + stdout.write( "HTTP "+ rsp.statusCode +" "+ rsp.statusMessage +"\n" ); + }); + req.end(JSON.stringify({ + destination: cls_hook.destination, + methods: [], + })); +} + + +}()); /*endOfModule*/ + diff --git a/src/main/nodejs/HttpFlood/putshowcaseresources.js b/src/main/nodejs/HttpFlood/putshowcaseresources.js new file mode 100644 index 0000000..9845d61 --- /dev/null +++ b/src/main/nodejs/HttpFlood/putshowcaseresources.js @@ -0,0 +1,133 @@ +;(function(){ "use strict"; + + +const http = require("http"); +const stdout = process.stdout; +const stdlog = process.stderr; +const noop = function(){}; + + +setTimeout(main); + + +function printHelp(){ + stdout.write("\n" + +" Put showcase data into gateleen-playground via HTTP\n" + +"\n" + +" Options:\n" + +"\n" + +" --host <ip|hostname>\n" + +" Gateleen host to use.\n" + +"\n" + +" --port <int> (default 7012)\n" + +" TCP port of gateleen to use.\n" + +"\n" + +" --path <string>\n" + +" Root where to PUT trash data. Example: '/playground/tmp/my-trash'.\n" + +"\n"); +} + + +function parseArgs( cls_putit, args ){ + // Defaults + cls_putit.host = null; + cls_putit.port = 7012; + cls_putit.path = null; + for( let i=2 ; i<args.length ; ++i ){ + let arg = args[i]; + if( arg == "--help" ){ + printHelp(); return -1; + }else if( arg == "--host" ){ + cls_putit.host = args[++i]; + if( !args[i] ){ stdlog.write("Arg --host needs value\n"); return -1; } + }else if( arg == "--port" ){ + cls_putit.port = parseInt(args[++i]); + if( isNaN(cls_putit.port) ){ stdlog.write("Failed to parse --port: "+args[i]+"\n"); return -1; } + }else if( arg == "--path" ){ + cls_putit.path = args[++i]; + if( !args[i] ){ stdlog.write("Arg --path expects a value\n"); return -1; } + }else{ + stdlog.write("Unknown arg: "+ arg +"\n"); + } + } + if( !cls_putit.host ){ stdlog.write("Arg --host missing\n"); return -1; } + if( !cls_putit.path ){ stdlog.write("Arg --path missing\n"); return -1; } + return 0; +} + + +function main(){ + const cls_putit = Object.seal({ + host: null, + port: null, + path: null, + pendingRequests: 0, + httpAgent: null, + }); + if( parseArgs(cls_putit, process.argv) ) return; + cls_putit.httpAgent = new http.Agent({ + keepAlive:true, maxSockets:16, keepAliveMsecs: 42000, + }); + putShowcase(cls_putit, onPutShowcaseComplete.bind(0,cls_putit)); +} + + +function onPutShowcaseComplete( cls_putit ){ + stdout.write("Done :) Take a look at your gateleen. There's some trash for experimenting now.\n"); +} + + +function putShowcase( cls_putit, onComplete ){ + const level1 = 10; + const level2 = 3000; + let body = {}; + for( let i=0 ; i<42 ; ++i ){ + body["prop-"+ i] = "Hi There :)"; + } + body = JSON.stringify( body ); + stdlog.write("PUTting trash to http://"+ cls_putit.host +":"+ cls_putit.port + cls_putit.path +"\n"); + stdlog.write("Might take a moment. You can consult your CPU monitor meanwhile ;)\n"); + for( let iOne=0 ; iOne < level1 ; ++iOne ){ + for( let iTwo=0 ; iTwo < level2 ; ++iTwo ){ + const cls_req = Object.seal({ + cls_putit: cls_putit, + onComplete: onComplete, + req: null, + rsp: null, + }); + const req = cls_req.req = http.request({ + agent: cls_putit.httpAgent, + host: cls_putit.host, + port: cls_putit.port, + path: cls_putit.path +"/levlA-"+iOne+"/levlB-"+(iTwo), + method: "PUT", + }); + req.on("error", function( err ){ console.error(err); }); + req.on("response", function( rsp ){ + cls_req.rsp = rsp; + if( rsp.statusCode!=200 ){ + stdlog.write( "ERROR: HTTP "+ rsp.statusCode +" "+ rsp.statusMessage +"\n" ); + } + rsp.on("data", noop); + rsp.on("end", onResponseEnd.bind(0,cls_req)); + }); + cls_putit.pendingRequests += 1; + cls_req.req.end(body); + } + } +} + + +function onResponseEnd( cls_req ){ + const cls_putit = cls_req.cls_putit; + cls_putit.pendingRequests -= 1; + if( cls_putit.pendingRequests > 0 ){ + return; // Await more. + } + if( cls_req.onComplete ){ + cls_req.onComplete(); + } +} + + +}()); |