agent.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // ZeroTier distributed HTTP test agent
  2. // ---------------------------------------------------------------------------
  3. // Customizable parameters:
  4. // Maximum interval between test attempts
  5. //var TEST_INTERVAL_MAX = (60 * 1 * 1000);
  6. var TEST_INTERVAL_MAX = 1000;
  7. // Test timeout in ms
  8. var TEST_TIMEOUT = 30000;
  9. // Where should I contact to register and query a list of other test agents?
  10. var SERVER_HOST = '127.0.0.1';
  11. //var SERVER_HOST = '104.238.141.145';
  12. var SERVER_PORT = 18080;
  13. // Which port should agents use for their HTTP?
  14. var AGENT_PORT = 18888;
  15. // Payload size in bytes
  16. var PAYLOAD_SIZE = 10000;
  17. // ---------------------------------------------------------------------------
  18. var ipaddr = require('ipaddr.js');
  19. var os = require('os');
  20. var http = require('http');
  21. var async = require('async');
  22. var express = require('express');
  23. var app = express();
  24. // Find our ZeroTier-assigned RFC4193 IPv6 address
  25. var thisAgentId = null;
  26. var interfaces = os.networkInterfaces();
  27. if (!interfaces) {
  28. console.error('FATAL: os.networkInterfaces() failed.');
  29. process.exit(1);
  30. }
  31. for(var ifname in interfaces) {
  32. var ifaddrs = interfaces[ifname];
  33. if (Array.isArray(ifaddrs)) {
  34. for(var i=0;i<ifaddrs.length;++i) {
  35. if (ifaddrs[i].family == 'IPv6') {
  36. try {
  37. var ipbytes = ipaddr.parse(ifaddrs[i].address).toByteArray();
  38. if ((ipbytes.length === 16)&&(ipbytes[0] == 0xfd)&&(ipbytes[9] == 0x99)&&(ipbytes[10] == 0x93)) {
  39. thisAgentId = '';
  40. for(var j=0;j<16;++j) {
  41. var tmp = ipbytes[j].toString(16);
  42. if (tmp.length === 1)
  43. thisAgentId += '0';
  44. thisAgentId += tmp;
  45. }
  46. }
  47. } catch (e) {
  48. console.error(e);
  49. }
  50. }
  51. }
  52. }
  53. }
  54. if (thisAgentId === null) {
  55. console.error('FATAL: no ZeroTier-assigned RFC4193 IPv6 addresses found on any local interface!');
  56. process.exit(1);
  57. }
  58. //console.log(thisAgentId);
  59. // Create a random (and therefore not very compressable) payload
  60. var payload = new Buffer(PAYLOAD_SIZE);
  61. for(var xx=0;xx<PAYLOAD_SIZE;++xx) {
  62. payload.writeUInt8(Math.round(Math.random() * 255.0),xx);
  63. }
  64. function agentIdToIp(agentId)
  65. {
  66. var ip = '';
  67. ip += agentId.substr(0,4);
  68. ip += ':';
  69. ip += agentId.substr(4,4);
  70. ip += ':';
  71. ip += agentId.substr(8,4);
  72. ip += ':';
  73. ip += agentId.substr(12,4);
  74. ip += ':';
  75. ip += agentId.substr(16,4);
  76. ip += ':';
  77. ip += agentId.substr(20,4);
  78. ip += ':';
  79. ip += agentId.substr(24,4);
  80. ip += ':';
  81. ip += agentId.substr(28,4);
  82. return ip;
  83. };
  84. var lastTestResult = null;
  85. var allOtherAgents = [];
  86. function doTest()
  87. {
  88. var submit = http.request({
  89. host: SERVER_HOST,
  90. port: SERVER_PORT,
  91. path: '/'+thisAgentId,
  92. method: 'POST'
  93. },function(res) {
  94. var body = '';
  95. res.on('data',function(chunk) { body += chunk.toString(); });
  96. res.on('end',function() {
  97. if (body) {
  98. try {
  99. var peers = JSON.parse(body);
  100. if (Array.isArray(peers))
  101. allOtherAgents = peers;
  102. } catch (e) {}
  103. }
  104. if (allOtherAgents.length > 0) {
  105. var target = allOtherAgents[Math.floor(Math.random() * allOtherAgents.length)];
  106. var testRequest = null;
  107. var timeoutId = null;
  108. timeoutId = setTimeout(function() {
  109. if (testRequest !== null)
  110. testRequest.abort();
  111. timeoutId = null;
  112. });
  113. var startTime = Date.now();
  114. testRequest = http.get({
  115. host: agentIdToIp(target),
  116. port: AGENT_PORT,
  117. path: '/'
  118. },function(res) {
  119. var bytes = 0;
  120. res.on('data',function(chunk) { bytes += chunk.length; });
  121. res.on('end',function() {
  122. lastTestResult = {
  123. source: thisAgentId,
  124. target: target,
  125. time: (Date.now() - startTime),
  126. bytes: bytes,
  127. timedOut: (timeoutId === null),
  128. error: null
  129. };
  130. if (timeoutId !== null)
  131. clearTimeout(timeoutId);
  132. return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1);
  133. });
  134. }).on('error',function(e) {
  135. lastTestResult = {
  136. source: thisAgentId,
  137. target: target,
  138. time: (Date.now() - startTime),
  139. bytes: 0,
  140. timedOut: (timeoutId === null),
  141. error: e.toString()
  142. };
  143. if (timeoutId !== null)
  144. clearTimeout(timeoutId);
  145. return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1);
  146. });
  147. } else {
  148. return setTimeout(doTest,Math.round(Math.random() * TEST_INTERVAL_MAX) + 1);
  149. }
  150. });
  151. }).on('error',function(e) {
  152. console.log('POST failed: '+e.toString());
  153. return setTimeout(doTest,1000);
  154. });
  155. if (lastTestResult !== null) {
  156. submit.write(JSON.stringify(lastTestResult));
  157. lastTestResult = null;
  158. }
  159. submit.end();
  160. };
  161. /*
  162. function performTestOnAllPeers(peers,callback)
  163. {
  164. var allResults = {};
  165. var allRequests = [];
  166. var timedOut = false;
  167. var endOfTestTimer = setTimeout(function() {
  168. timedOut = true;
  169. for(var x=0;x<allRequests.length;++x)
  170. allRequests[x].abort();
  171. },TEST_DURATION);
  172. async.each(peers,function(peer,next) {
  173. if (timedOut)
  174. return next(null);
  175. if (peer.length !== 32)
  176. return next(null);
  177. var connectionStartTime = Date.now();
  178. allResults[peer] = {
  179. start: connectionStartTime,
  180. end: 0,
  181. error: null,
  182. timedOut: false,
  183. bytes: 0
  184. };
  185. allRequests.push(http.get({
  186. host: agentIdToIp(peer),
  187. port: AGENT_PORT,
  188. path: '/'
  189. },function(res) {
  190. var bytes = 0;
  191. res.on('data',function(chunk) {
  192. bytes += chunk.length;
  193. });
  194. res.on('end',function() {
  195. allResults[peer] = {
  196. start: connectionStartTime,
  197. end: Date.now(),
  198. error: null,
  199. timedOut: timedOut,
  200. bytes: bytes
  201. };
  202. return next(null);
  203. });
  204. }).on('error',function(e) {
  205. allResults[peer] = {
  206. start: connectionStartTime,
  207. end: Date.now(),
  208. error: e.toString(),
  209. timedOut: timedOut,
  210. bytes: 0
  211. };
  212. return next(null);
  213. }));
  214. },function(err) {
  215. if (!timedOut)
  216. clearTimeout(endOfTestTimer);
  217. return callback(allResults);
  218. });
  219. };
  220. function doTestsAndReport()
  221. {
  222. registerAndGetPeers(function(err,peers) {
  223. if (err) {
  224. console.error('WARNING: skipping test: unable to contact or query server: '+err.toString());
  225. } else {
  226. performTestOnAllPeers(peers,function(results) {
  227. var submit = http.request({
  228. host: SERVER_HOST,
  229. port: SERVER_PORT,
  230. path: '/'+thisAgentId,
  231. method: 'POST'
  232. },function(res) {
  233. }).on('error',function(e) {
  234. console.error('WARNING: unable to submit results to server: '+err.toString());
  235. });
  236. submit.write(JSON.stringify(results));
  237. submit.end();
  238. });
  239. }
  240. });
  241. };
  242. */
  243. // Agents just serve up a test payload
  244. app.get('/',function(req,res) { return res.status(200).send(payload); });
  245. var expressServer = app.listen(AGENT_PORT,function () {
  246. // Start timeout-based loop
  247. doTest();
  248. });