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");