4 changed files with 293 additions and 0 deletions
@ -0,0 +1,5 @@ |
|||||||
|
HTTP one-to-all test |
||||||
|
====== |
||||||
|
|
||||||
|
This code can be deployed across a large number of VMs or containers to test and benchmark HTTP traffic within a virtual network at scale. The agent acts as a server and can query other agents, while the server collects agent data and tells agents about each other. It's designed to use RFC4193-based ZeroTier IPv6 addresses within the cluster, which allows the easy provisioning of a large cluster without IP conflicts. |
||||||
|
|
||||||
@ -0,0 +1,224 @@ |
|||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Customizable parameters:
|
||||||
|
|
||||||
|
// How frequently in ms to run tests
|
||||||
|
//var RUN_TEST_EVERY = (60 * 5 * 1000);
|
||||||
|
var RUN_TEST_EVERY = 1000; |
||||||
|
|
||||||
|
// Maximum test duration in milliseconds (must be less than RUN_TEST_EVERY)
|
||||||
|
var TEST_DURATION = (60 * 1000); |
||||||
|
|
||||||
|
// Where should I contact to register and query a list of other nodes?
|
||||||
|
var SERVER_HOST = '127.0.0.1'; |
||||||
|
var SERVER_PORT = 18080; |
||||||
|
|
||||||
|
// Which port should agents use for their HTTP?
|
||||||
|
var AGENT_PORT = 18888; |
||||||
|
|
||||||
|
// Payload size in bytes
|
||||||
|
var PAYLOAD_SIZE = 4096; |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var ipaddr = require('ipaddr.js'); |
||||||
|
var os = require('os'); |
||||||
|
var http = require('http'); |
||||||
|
var async = require('async'); |
||||||
|
|
||||||
|
var express = require('express'); |
||||||
|
var app = express(); |
||||||
|
|
||||||
|
// Find our ZeroTier-assigned RFC4193 IPv6 address
|
||||||
|
var thisAgentId = null; |
||||||
|
var interfaces = os.networkInterfaces(); |
||||||
|
if (!interfaces) { |
||||||
|
console.error('FATAL: os.networkInterfaces() failed.'); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
for(var ifname in interfaces) { |
||||||
|
var ifaddrs = interfaces[ifname]; |
||||||
|
if (Array.isArray(ifaddrs)) { |
||||||
|
for(var i=0;i<ifaddrs.length;++i) { |
||||||
|
if (ifaddrs[i].family == 'IPv6') { |
||||||
|
try { |
||||||
|
var ipbytes = ipaddr.parse(ifaddrs[i].address).toByteArray(); |
||||||
|
if ((ipbytes.length === 16)&&(ipbytes[0] == 0xfd)&&(ipbytes[9] == 0x99)&&(ipbytes[10] == 0x93)) { |
||||||
|
thisAgentId = ''; |
||||||
|
for(var j=0;j<16;++j) { |
||||||
|
var tmp = ipbytes[j].toString(16); |
||||||
|
if (tmp.length === 1) |
||||||
|
thisAgentId += '0'; |
||||||
|
thisAgentId += tmp; |
||||||
|
} |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.error(e); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (thisAgentId === null) { |
||||||
|
console.error('FATAL: no ZeroTier-assigned RFC4193 IPv6 addresses found on any local interface!'); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
//console.log(thisAgentId);
|
||||||
|
|
||||||
|
// Create a random (and therefore not very compressable) payload
|
||||||
|
var payload = ''; |
||||||
|
while (payload.length < PAYLOAD_SIZE) { |
||||||
|
payload += String.fromCharCode(Math.round(Math.random() * 255.0)); |
||||||
|
} |
||||||
|
|
||||||
|
// Incremented for each test
|
||||||
|
var testCounter = 0; |
||||||
|
|
||||||
|
function registerAndGetPeers(callback) |
||||||
|
{ |
||||||
|
http.get({ |
||||||
|
host: SERVER_HOST, |
||||||
|
port: SERVER_PORT, |
||||||
|
path: '/'+thisAgentId |
||||||
|
},function(res) { |
||||||
|
var body = ''; |
||||||
|
res.on('data',function(chunk) { body += chunk.toString(); }); |
||||||
|
res.on('end',function() { |
||||||
|
try { |
||||||
|
var peers = JSON.parse(body); |
||||||
|
if (Array.isArray(peers)) |
||||||
|
return callback(null,peers); |
||||||
|
else return callback(new Error('invalid JSON response from server'),null); |
||||||
|
} catch (e) { |
||||||
|
return callback(new Error('invalid JSON response from server'),null); |
||||||
|
} |
||||||
|
}); |
||||||
|
}).on('error',function(e) { |
||||||
|
return callback(e,null); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
function performTestOnAllPeers(peers,callback) |
||||||
|
{ |
||||||
|
var allResults = {}; |
||||||
|
var timedOut = false; |
||||||
|
var endOfTestTimer = setTimeout(function() { |
||||||
|
timedOut = true; |
||||||
|
return callback(allResults); |
||||||
|
},TEST_DURATION); |
||||||
|
var testStartTime = Date.now(); |
||||||
|
|
||||||
|
async.each(peers,function(peer,next) { |
||||||
|
if (timedOut) |
||||||
|
return next(null); |
||||||
|
if (peer.length !== 32) |
||||||
|
return next(null); |
||||||
|
|
||||||
|
var connectionStartTime = Date.now(); |
||||||
|
allResults[peer] = { |
||||||
|
testStart: testStartTime, |
||||||
|
start: connectionStartTime, |
||||||
|
end: null, |
||||||
|
error: null, |
||||||
|
bytes: 0, |
||||||
|
test: testCounter |
||||||
|
}; |
||||||
|
|
||||||
|
var peerHost = ''; |
||||||
|
peerHost += peer.substr(0,4); |
||||||
|
peerHost += ':'; |
||||||
|
peerHost += peer.substr(4,4); |
||||||
|
peerHost += ':'; |
||||||
|
peerHost += peer.substr(8,4); |
||||||
|
peerHost += ':'; |
||||||
|
peerHost += peer.substr(12,4); |
||||||
|
peerHost += ':'; |
||||||
|
peerHost += peer.substr(16,4); |
||||||
|
peerHost += ':'; |
||||||
|
peerHost += peer.substr(20,4); |
||||||
|
peerHost += ':'; |
||||||
|
peerHost += peer.substr(24,4); |
||||||
|
peerHost += ':'; |
||||||
|
peerHost += peer.substr(28,4); |
||||||
|
|
||||||
|
http.get({ |
||||||
|
host: peerHost, |
||||||
|
port: AGENT_PORT, |
||||||
|
path: '/' |
||||||
|
},function(res) { |
||||||
|
var bytes = 0; |
||||||
|
res.on('data',function(chunk) { bytes += chunk.length; }); |
||||||
|
res.on('end',function() { |
||||||
|
if (timedOut) |
||||||
|
return next(null); |
||||||
|
allResults[peer] = { |
||||||
|
testStart: testStartTime, |
||||||
|
start: connectionStartTime, |
||||||
|
end: Date.now(), |
||||||
|
error: null, |
||||||
|
bytes: bytes, |
||||||
|
test: testCounter |
||||||
|
}; |
||||||
|
return next(null); |
||||||
|
}); |
||||||
|
}).on('error',function(e) { |
||||||
|
if (timedOut) |
||||||
|
return next(null); |
||||||
|
allResults[peer] = { |
||||||
|
testStart: testStartTime, |
||||||
|
start: connectionStartTime, |
||||||
|
end: Date.now(), |
||||||
|
error: e.toString(), |
||||||
|
bytes: 0, |
||||||
|
test: testCounter |
||||||
|
}; |
||||||
|
return next(null); |
||||||
|
}); |
||||||
|
},function(err) { |
||||||
|
if (!timedOut) { |
||||||
|
clearTimeout(endOfTestTimer); |
||||||
|
return callback(allResults); |
||||||
|
} |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
// Agents just serve up a test payload
|
||||||
|
app.get('/',function(req,res) { |
||||||
|
return res.status(200).send(payload); |
||||||
|
}); |
||||||
|
|
||||||
|
var expressServer = app.listen(AGENT_PORT,function () { |
||||||
|
registerAndGetPeers(function(err,peers) { |
||||||
|
if (err) { |
||||||
|
console.error('FATAL: unable to contact or query server: '+err.toString()); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
setInterval(function() { |
||||||
|
++testCounter; |
||||||
|
|
||||||
|
registerAndGetPeers(function(err,peers) { |
||||||
|
if (err) { |
||||||
|
console.error('WARNING: unable to contact or query server, test aborted: '+err.toString()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
performTestOnAllPeers(peers,function(results) { |
||||||
|
console.log(results); |
||||||
|
|
||||||
|
var submit = http.request({ |
||||||
|
host: SERVER_HOST, |
||||||
|
port: SERVER_PORT, |
||||||
|
path: '/'+thisAgentId, |
||||||
|
method: 'POST' |
||||||
|
},function(res) { |
||||||
|
}).on('error',function(e) { |
||||||
|
console.error('WARNING: unable to submit results to server: '+err.toString()); |
||||||
|
}); |
||||||
|
submit.write(JSON.stringify(results)); |
||||||
|
submit.end(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
},RUN_TEST_EVERY); |
||||||
|
}); |
||||||
|
}); |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"name": "zerotier-test-http", |
||||||
|
"version": "1.0.0", |
||||||
|
"description": "ZeroTier in-network HTTP test", |
||||||
|
"main": "agent.js", |
||||||
|
"scripts": { |
||||||
|
"test": "echo \"Error: no test specified\" && exit 1" |
||||||
|
}, |
||||||
|
"author": "ZeroTier, Inc.", |
||||||
|
"license": "GPL-3.0", |
||||||
|
"dependencies": { |
||||||
|
"async": "^1.5.0", |
||||||
|
"express": "^4.13.3", |
||||||
|
"ipaddr.js": "^1.0.3" |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Customizable parameters:
|
||||||
|
|
||||||
|
var SERVER_PORT = 18080; |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
var express = require('express'); |
||||||
|
var app = express(); |
||||||
|
|
||||||
|
app.use(function(req,res,next) { |
||||||
|
req.rawBody = ''; |
||||||
|
req.on('data', function(chunk) { req.rawBody += chunk.toString(); }); |
||||||
|
req.on('end', function() { return next(); }); |
||||||
|
}); |
||||||
|
|
||||||
|
var knownAgents = {}; |
||||||
|
|
||||||
|
app.get('/:agentId',function(req,res) { |
||||||
|
var agentId = req.params.agentId; |
||||||
|
if ((!agentId)||(agentId.length !== 32)) |
||||||
|
return res.status(404).send(''); |
||||||
|
knownAgents[agentId] = Date.now(); |
||||||
|
return res.status(200).send(JSON.stringify(Object.keys(knownAgents))); |
||||||
|
}); |
||||||
|
|
||||||
|
app.post('/:agentId',function(req,res) { |
||||||
|
var agentId = req.params.agentId; |
||||||
|
if ((!agentId)||(agentId.length !== 32)) |
||||||
|
return res.status(404).send(''); |
||||||
|
var resultData = null; |
||||||
|
try { |
||||||
|
resultData = JSON.parse(req.rawBody); |
||||||
|
} catch (e) { |
||||||
|
resultData = req.rawBody; |
||||||
|
} |
||||||
|
result = { |
||||||
|
agentId: agentId, |
||||||
|
result: resultData |
||||||
|
}; |
||||||
|
console.log(result); |
||||||
|
return res.status(200).send(''); |
||||||
|
}); |
||||||
|
|
||||||
|
var expressServer = app.listen(SERVER_PORT,function () { |
||||||
|
console.log('LISTENING ON '+SERVER_PORT); |
||||||
|
console.log(''); |
||||||
|
}); |
||||||
Loading…
Reference in new issue