|
|
|
var l = document.getElementById("log");
|
|
|
|
var c = document.getElementById("main");
|
|
|
|
var nodes = {
|
|
|
|
length: 0,
|
|
|
|
};
|
|
|
|
var in_traffic = 0;
|
|
|
|
var out_traffic = 0;
|
|
|
|
var tick = 0;
|
|
|
|
|
|
|
|
function nodeConnected(ident) {
|
|
|
|
if (nodes[ident]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nodes[ident] = {
|
|
|
|
send: 0,
|
|
|
|
recv: 0
|
|
|
|
};
|
|
|
|
nodes.length ++;
|
|
|
|
}
|
|
|
|
|
|
|
|
function nodeDisconnected(ident) {
|
|
|
|
if (nodes[ident]) {
|
|
|
|
delete nodes[ident];
|
|
|
|
nodes.length --;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function nodeSend(ident, n) {
|
|
|
|
if(!nodes[ident]) {
|
|
|
|
nodeConnected(ident);
|
|
|
|
}
|
|
|
|
nodes[ident].send += parseInt(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
function nodeRecv(ident, n) {
|
|
|
|
if(!nodes[ident]) {
|
|
|
|
nodeConnected(ident);
|
|
|
|
}
|
|
|
|
nodes[ident].recv += parseInt(n);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var tpeers = {};
|
|
|
|
|
|
|
|
var tunnels = {
|
|
|
|
length : 0
|
|
|
|
};
|
|
|
|
|
|
|
|
function ensureTunnel(tid) {
|
|
|
|
if(!tunnels[tid]) {
|
|
|
|
tunnels[tid] = {
|
|
|
|
latency : -1,
|
|
|
|
color: "#ddd",
|
|
|
|
};
|
|
|
|
tunnels.length ++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelBuild(peers, tid, inbound) {
|
|
|
|
logit("Tunnel "+tid+" build started");
|
|
|
|
ensureTunnel(tid);
|
|
|
|
tunnels[tid].peers = peers.split(":");
|
|
|
|
tunnels[tid].inbound = inbound;
|
|
|
|
tunnels[tid].state = "building";
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelLatency(tid, latency) {
|
|
|
|
// logit("tunnel "+tid+" latency "+latency+"ms");
|
|
|
|
if (!tunnels[tid]) return;
|
|
|
|
tunnels[tid].latency = latency;
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelEstablished(tid) {
|
|
|
|
if(!tunnels[tid]) return;
|
|
|
|
logit("Tunnel " + tid + " is healthy");
|
|
|
|
tunnels[tid].state = "healthy";
|
|
|
|
tunnels[tid].color = "#1d1";
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelBuildFailed(tid) {
|
|
|
|
if(!tunnels[tid]) return;
|
|
|
|
logit("Tunnel " + tid + " failed to build");
|
|
|
|
delete tunnels[tid];
|
|
|
|
tunnels.length --;
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelTestFailed(tid) {
|
|
|
|
if(!tunnels[tid]) return;
|
|
|
|
logit("Tunnel " + tid + " has failed tunnel test");
|
|
|
|
tunnels[tid].state = "stuck";
|
|
|
|
tunnels[tid].color = "orange";
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelFailed(tid) {
|
|
|
|
if(!tunnels[tid]) return;
|
|
|
|
logit("Tunnel " + tid + " has failed");
|
|
|
|
delete tunnels[tid];
|
|
|
|
tunnels.length--;
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelExpiring(tid) {
|
|
|
|
if(!tunnels[tid]) return;
|
|
|
|
logit("Tunnel " + tid + " is expired");
|
|
|
|
delete tunnels[tid];
|
|
|
|
tunnels.length--;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function tunnelState(tid, state) {
|
|
|
|
if (state == "3") {
|
|
|
|
tunnelEstablished(tid);
|
|
|
|
} else if (state == "2" ) {
|
|
|
|
tunnelBuildFailed(tid);
|
|
|
|
} else if (state == "4" ) {
|
|
|
|
tunnelTestFailed(tid);
|
|
|
|
} else if (state == "5" ) {
|
|
|
|
tunnelFailed(tid);
|
|
|
|
} else if (state == "6" ) {
|
|
|
|
tunnelExpiring(tid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function tunnelCreated(tid) {
|
|
|
|
logit("Tunnel "+tid+" was created");
|
|
|
|
}
|
|
|
|
|
|
|
|
function logit(msg) {
|
|
|
|
console.log(msg);
|
|
|
|
var t = document.createTextNode(msg);
|
|
|
|
var e = document.createElement("div");
|
|
|
|
e.appendChild(t);
|
|
|
|
l.appendChild(e);
|
|
|
|
while(l.children.length > 50)
|
|
|
|
l.removeChild(l.children[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
function socketClosed() {
|
|
|
|
var b = document.getElementById("connect-button");
|
|
|
|
b.onclick = startui
|
|
|
|
b.innerHTML = "connect";
|
|
|
|
}
|
|
|
|
|
|
|
|
function startui() {
|
|
|
|
var el = document.getElementById("ws-url");
|
|
|
|
var url;
|
|
|
|
if(el)
|
|
|
|
url = el.value;
|
|
|
|
else
|
|
|
|
url = "ws://127.0.0.1:7666";
|
|
|
|
|
|
|
|
var ws = new WebSocket(url);
|
|
|
|
ws.onclose = function (ev) {
|
|
|
|
if (ev.code == 1000)
|
|
|
|
logit("connection closed");
|
|
|
|
else
|
|
|
|
logit("failed to connect to "+url);
|
|
|
|
nodes = { length: 0 };
|
|
|
|
tpeers = { length: 0 };
|
|
|
|
socketClosed();
|
|
|
|
}
|
|
|
|
ws.onopen = function(ev) {
|
|
|
|
logit("connected to "+url);
|
|
|
|
var b = document.getElementById("connect-button");
|
|
|
|
b.onclick = function() {
|
|
|
|
ws.close();
|
|
|
|
}
|
|
|
|
b.innerHTML = "disconnect";
|
|
|
|
}
|
|
|
|
ws.onmessage = function(ev) {
|
|
|
|
var j = JSON.parse(ev.data);
|
|
|
|
if (j) {
|
|
|
|
console.log(j);
|
|
|
|
if(j.type == "transport.connected") {
|
|
|
|
nodeConnected(j.ident);
|
|
|
|
} else if (j.type == "transport.disconnected") {
|
|
|
|
nodeDisconnected(j.ident);
|
|
|
|
} else if (j.type == "transport.sendmsg") {
|
|
|
|
nodeSend(j.ident, j.number);
|
|
|
|
} else if (j.type == "transport.recvmsg") {
|
|
|
|
nodeRecv(j.ident, j.number);
|
|
|
|
} else if (j.type == "tunnel.build") {
|
|
|
|
tunnelBuild(j.value, j.tid, j.inbound);
|
|
|
|
} else if (j.type == "tunnel.latency") {
|
|
|
|
tunnelLatency(j.tid, j.value);
|
|
|
|
} else if (j.type == "tunnel.state") {
|
|
|
|
tunnelState(j.tid, j.value);
|
|
|
|
} else if (j.type == "tunnels.created") {
|
|
|
|
tunnelCreated(j.tid);
|
|
|
|
} else if (j.type == "tunnels.expired") {
|
|
|
|
tunnelExpiring(j.tid);
|
|
|
|
} else {
|
|
|
|
logit("message: "+j.type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPeer(h, us) {
|
|
|
|
if (tpeers[h]) {
|
|
|
|
return tpeers[h];
|
|
|
|
}
|
|
|
|
console.log("make peer "+h);
|
|
|
|
|
|
|
|
var p = {
|
|
|
|
x: Math.random(),
|
|
|
|
y: Math.random(),
|
|
|
|
name: h,
|
|
|
|
};
|
|
|
|
if (us) {
|
|
|
|
p.x = 0.5;
|
|
|
|
p.y = 0.5;
|
|
|
|
} else {
|
|
|
|
while (Math.abs(p.x - 0.5) <= 0.1 ) {
|
|
|
|
p.x = Math.random();
|
|
|
|
}
|
|
|
|
while (Math.abs(p.y - 0.5) <= 0.1 ) {
|
|
|
|
p.y = Math.random();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tpeers[h] = p;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawPeer(p) {
|
|
|
|
draw.beginPath();
|
|
|
|
draw.lineWidth = 1;
|
|
|
|
draw.fillStyle = "white";
|
|
|
|
draw.strokeStyle = "white";
|
|
|
|
draw.arc(p.x * c.width, p.y * c.height, 20, 0, 2* Math.PI);
|
|
|
|
draw.stroke();
|
|
|
|
draw.fillText(p.name.substr(0,6), p.x * c.width, p.y * c.height);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getTraffic(node) {
|
|
|
|
if (!node) return 1;
|
|
|
|
return 1.0 + ( (node.send + node.recv + 1.0) / 1.5) ;
|
|
|
|
}
|
|
|
|
var draw = c.getContext("2d");
|
|
|
|
var ticks = 0;
|
|
|
|
// draw
|
|
|
|
setInterval(function() {
|
|
|
|
draw.canvas.width = window.innerWidth - 100;
|
|
|
|
draw.canvas.height = window.innerHeight - 100;
|
|
|
|
draw.font = "10px monospace";
|
|
|
|
|
|
|
|
draw.clearRect(0, 0, c.width, c.height);
|
|
|
|
|
|
|
|
// draw tunnels
|
|
|
|
|
|
|
|
var e = [];
|
|
|
|
|
|
|
|
for (var tid in tunnels) {
|
|
|
|
if(tid == "length") continue;
|
|
|
|
var t = tunnels[tid];
|
|
|
|
var us = getPeer(t.peers[0], true);
|
|
|
|
var traff = getTraffic(nodes[t.peers[1]]);
|
|
|
|
e.push([us, getPeer(t.peers[1]), t.color, traff]);
|
|
|
|
for (var idx = 1 ; idx + 1 < t.peers.length; idx ++ ) {
|
|
|
|
var cur = getPeer(t.peers[idx]);
|
|
|
|
var next = getPeer(t.peers[idx+1]);
|
|
|
|
if(cur && next)
|
|
|
|
e.push([cur, next, t.color, traff]);
|
|
|
|
else
|
|
|
|
console.log(cur, next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( var h in tpeers ) {
|
|
|
|
if( h == "length") continue;
|
|
|
|
drawPeer(getPeer(h));
|
|
|
|
}
|
|
|
|
|
|
|
|
var newPeers = {};
|
|
|
|
var counter = 0;
|
|
|
|
for ( var ed in e ) {
|
|
|
|
var edge = e[ed];
|
|
|
|
draw.beginPath();
|
|
|
|
draw.strokeStyle = edge[2];
|
|
|
|
draw.lineWidth = edge[3];
|
|
|
|
draw.moveTo(edge[0].x * c.width, edge[0].y * c.height);
|
|
|
|
draw.lineTo(edge[1].x * c.width, edge[1].y * c.height);
|
|
|
|
if(!newPeers[edge[0].name]) {
|
|
|
|
newPeers[edge[0].name] = edge[0];
|
|
|
|
counter ++;
|
|
|
|
}
|
|
|
|
if(!newPeers[edge[1].name]) {
|
|
|
|
newPeers[edge[1].name] = edge[1];
|
|
|
|
counter ++;
|
|
|
|
}
|
|
|
|
draw.stroke();
|
|
|
|
}
|
|
|
|
newPeers.length = counter;
|
|
|
|
tpeers = newPeers;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// draw nodes
|
|
|
|
|
|
|
|
var n = nodes.length;
|
|
|
|
|
|
|
|
var centerx = c.width / 2;
|
|
|
|
var centery = c.height / 2;
|
|
|
|
|
|
|
|
var mult = Math.log(10 + nodes.length) + 0.5;
|
|
|
|
|
|
|
|
var outer_r = ((10 + n) * mult);
|
|
|
|
if(outer_r > c.width || outer_r > c.height) {
|
|
|
|
var smaller = c.height;
|
|
|
|
if(c.width < smaller) smaller = c.width;
|
|
|
|
outer_r = smaller - 20;
|
|
|
|
}
|
|
|
|
|
|
|
|
var inner_r = outer_r / 2;
|
|
|
|
draw.beginPath();
|
|
|
|
draw.lineWidth = 1;
|
|
|
|
draw.strokeStyle = "white";
|
|
|
|
draw.arc(centerx, centery, inner_r, 0, 2* Math.PI);
|
|
|
|
draw.stroke();
|
|
|
|
|
|
|
|
|
|
|
|
var idents = [];
|
|
|
|
var rad = 0;
|
|
|
|
for( var ident in nodes ) {
|
|
|
|
if (ident == "length" ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
idents.push( ident );
|
|
|
|
}
|
|
|
|
|
|
|
|
idents = idents.sort();
|
|
|
|
for (var i = 0; i < idents.length; i++) {
|
|
|
|
var ident = idents[i];
|
|
|
|
rad += ( Math.PI * 2 ) / nodes.length;
|
|
|
|
|
|
|
|
in_traffic += nodes[ident].recv / 10.0 ;
|
|
|
|
out_traffic += nodes[ident].send / 10.0;
|
|
|
|
|
|
|
|
var send = (nodes[ident].send ) * 5;
|
|
|
|
var recv = (nodes[ident].recv ) * 5;
|
|
|
|
|
|
|
|
var traff = (getTraffic(nodes[ident]) - 1) * 5;
|
|
|
|
|
|
|
|
var x0 = (Math.cos(rad) * inner_r) + centerx;
|
|
|
|
var y0 = (Math.sin(rad) * inner_r) + centery;
|
|
|
|
var x1 = (Math.cos(rad) * (inner_r + traff )) + centerx;
|
|
|
|
var y1 = (Math.sin(rad) * (inner_r + traff )) + centery;
|
|
|
|
|
|
|
|
draw.fillStyle = "white";
|
|
|
|
draw.beginPath();
|
|
|
|
var txt = ident.substr(0, 6);
|
|
|
|
draw.fillText(txt, x1-5, y1-5);
|
|
|
|
/**
|
|
|
|
if(i * 10 < c.height) {
|
|
|
|
txt += "| "+leftpad(nodes[ident].recv+" msg/s in", 15)+ " | "+leftpad(nodes[ident].send+" msg/s out", 15)+" |";
|
|
|
|
draw.fillText(txt, 100, 20 + (i*10));
|
|
|
|
} */
|
|
|
|
nodes[ident].recv = 0;
|
|
|
|
nodes[ident].send = 0;
|
|
|
|
draw.moveTo(x0, y0);
|
|
|
|
|
|
|
|
draw.strokeStyle = "#dfa";
|
|
|
|
|
|
|
|
draw.moveTo(x0, y0);
|
|
|
|
draw.lineTo(x1, y1);
|
|
|
|
draw.lineWidth = 8;
|
|
|
|
draw.stroke();
|
|
|
|
/*
|
|
|
|
if(( 40 + idx ) < c.height) {
|
|
|
|
var send = nodes[ident].send * 10;
|
|
|
|
var recv = nodes[ident].recv * 10;
|
|
|
|
var t = send + recv;
|
|
|
|
if (!t) continue;
|
|
|
|
draw.fillStyle = "black";
|
|
|
|
draw.fillText(ident.substr(0, 6), 10, idx);
|
|
|
|
draw.beginPath();
|
|
|
|
draw.rect(100, idx-20, send, 10);
|
|
|
|
if(send >= c.width) draw.fillStyle = "red";
|
|
|
|
else draw.fillStyle = "green";
|
|
|
|
draw.fill();
|
|
|
|
draw.beginPath();
|
|
|
|
draw.rect(100, idx-10, recv, 10);
|
|
|
|
if(recv >= c.width) draw.fillStyle = "red";
|
|
|
|
else draw.fillStyle = "blue";
|
|
|
|
draw.fill();
|
|
|
|
|
|
|
|
idx += 40;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
}
|
|
|
|
draw.fillStyle = "white";
|
|
|
|
draw.fillText("Tracked Peers | " +leftpad(" "+ nodes.length, 10), 500, 25);
|
|
|
|
draw.fillText("Tracked Tunnels | " +leftpad(" "+ tunnels.length, 10), 500, 35);
|
|
|
|
draw.fillText("In Traffic | "+leftpad(" "+Math.ceil(in_traffic)+" msg/s", 10), 500, 45);
|
|
|
|
draw.fillText("Out Traffic | "+leftpad(" "+Math.ceil(out_traffic)+" msg/s", 10), 500, 55);
|
|
|
|
|
|
|
|
if (tick % 10 == 0) {
|
|
|
|
in_traffic = 0;
|
|
|
|
out_traffic = 0;
|
|
|
|
}
|
|
|
|
tick ++;
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
logit("loaded");
|