Skip to content

Commit

Permalink
Add a silly pinger thing
Browse files Browse the repository at this point in the history
  • Loading branch information
virtulis committed Nov 2, 2024
1 parent 71848ba commit beb2324
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 4 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/dist
/conf
/node_modules

/.idea
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"name": "@virtulis/this-computer",
"private": true,
"type": "module"
"type": "module",
"dependencies": {
"ws": "^8.18.0"
}
}
31 changes: 31 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 68 additions & 0 deletions pong/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { WebSocketServer } from 'ws';

const verbose = !!process.env.WS_VERBOSE;
const port = process.env.WS_PORT ? Number(process.env.WS_PORT) : 1234;
const wss = new WebSocketServer({ port });

const timeoutMs = 60_000;
let connId = 1;

const interval = 200;

wss.on('connection', function connection(ws, req) {

const id = connId++;
const ip = req.headers['x-forwarded-for'] ?? req.socket.remoteAddress;

if (verbose) console.log('Connected:', id, ip);

const send = msg => ws.send(JSON.stringify(msg));

let lastRecv = Date.now();
let timer;
const sender = setInterval(() => {
send({ ping: Date.now() });
}, interval);

const bump = () => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
if (verbose) console.log('Dead:', id, ip);
ws.close();
}, timeoutMs);
}
bump();

if (verbose) ws.on('error', e => console.log('Error:', id, ip, e?.message ?? e));

ws.on('close', () => {
if (verbose) console.log('Closed:', id, ip);
clearTimeout(timer);
clearInterval(sender);
});

ws.on('message', function message(data) {
try {
if (verbose) console.log('Received:', id, ip, data);
const msg = JSON.parse(data);
const time = Date.now();
const diff = time - lastRecv;
lastRecv = time;
send({
pong: msg.ping,
time,
diff,
});
}
catch (e) {
if (verbose) console.log('Error:', id, ip, e);
ws.close();
}
});

send({
ping: Date.now(),
interval,
});

});
198 changes: 195 additions & 3 deletions template/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
body {
font-family: sans-serif;
padding: 16px;
--canvas-axis: #000;
--canvas-bars: #606;
--canvas-line: #090;
}
a {
color: #339;
}
footer {
margin: 30px 0 10px;
Expand All @@ -17,6 +23,42 @@
font-size: 20px;
padding: 10px 15px;
}
#graphs {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px 0;
max-width: 500px;
}
.legend {
display: flex;
gap: 16px;
align-items: center;
flex-wrap: wrap;
}
.legend span {
min-width: 80px;
}
canvas {
width: 100%;
max-width: 500px;
}
@media (prefers-color-scheme: dark) {
body {
background: black;
color: white;
--canvas-axis: #fff;
--canvas-bars: #969;
--canvas-line: #6e6;
}
a {
color: #66a;
}
button {
background: transparent;
color: white;
}
}
</style>
</head>

Expand All @@ -37,6 +79,12 @@ <h2>This Computer:</h2>
<button id="copy">Copy</button>
<button id="copy4" disabled>IPv4</button>
<button id="copy6" disabled>IPv6</button>
<div id="graphs">

</div>
<div id="debug">

</div>
</main>

<footer>
Expand All @@ -47,9 +95,9 @@ <h2>This Computer:</h2>
</footer>

<script type="application/javascript">

const timeout = new Promise(resolve => setTimeout(() => resolve(null), 30_000));

const ips = {};
const promises = {
4: Promise.race([timeout, fetch('https://IP4_HOST/').then(res => res.text()).catch(() => null)]),
Expand All @@ -60,7 +108,7 @@ <h2>This Computer:</h2>
document.getElementById(`public${v}`).textContent = str ?? '—';
document.getElementById(`copy${v}`).disabled = !str;
}));

document.getElementById('copy').addEventListener('click', async e => {
const lines = Object.values(ips).filter(s => !!s);
if (!lines.length) return;
Expand All @@ -72,6 +120,150 @@ <h2>This Computer:</h2>
e.target.textContent = 'Copied';
}));

const pingServers = [{
label: '???',
host: 'BASE_HOST'
}];

const graphsCtr = document.getElementById('graphs');
const debug = document.getElementById('debug');
const bufLen = 30 * 5;

for (const [i, { label, host }] of pingServers.entries()) {

let ws;
let backoff = 1;
let pinger;
let interval = 200;
let worst = 500;
let changed = false;

const pings = [];
const recv = [];

const legend = document.createElement('div');
legend.className = 'legend';
const lblSpan = document.createElement('strong');
lblSpan.textContent = `${label}:`;
legend.append(lblSpan);
const [avgSpan, minSpan, maxSpan, jitSpan] = [1, 2, 3, 4].map(_ => {
const s = document.createElement('span');
legend.append(s);
return s;
});
graphsCtr.append(legend);

const canvas = document.createElement('canvas');
let dpr, cw, ch, style;
const resizeCanvas = () => {
const ctrW = graphsCtr.getBoundingClientRect().width;
dpr = devicePixelRatio;
canvas.width = cw = Math.round(ctrW * dpr);
canvas.height = ch = Math.round(200 * dpr);
style = getComputedStyle(graphsCtr);
};
graphsCtr.append(canvas);
resizeCanvas();
window.addEventListener('resize', resizeCanvas);

const connect = () => {
ws = new WebSocket(`wss://${host}/ws`);
ws.onclose = closed;
ws.onmessage = e => {
const msg = JSON.parse(e.data);
if (msg.interval) {
interval = msg.interval;
clearInterval(pinger);
pinger = setInterval(() => {
const at = Date.now();
const prev = pings[pings.length - 1];
const sentDiff = prev ? at - prev.at : interval;
ws.send(JSON.stringify({ ping: at }))
pings.push({ at, sentDiff, roundtrip: null, diff: null });
if (pings.length > bufLen) pings.shift();
}, interval);
}
if (msg.ping) {
const at = Date.now();
const last = recv[recv.length - 1];
const diff = !last ? 0 : (at - last.at) - interval;
recv.push({ at: Date.now(), ping: msg.ping, diff });
if (recv.length > bufLen) recv.shift();
}
if (msg.pong) {
const ping = pings.find(p => p.at == msg.pong);
if (ping) {
ping.roundtrip = Date.now() - ping.at;
ping.diff = msg.diff - ping.sentDiff;
}
}
changed = true;
// debug.textContent = JSON.stringify({ pings, recv });
}
};
const closed = () => {
clearInterval(pinger);
setTimeout(connect, 1_000 * backoff++);
}

connect();

const ctx = canvas.getContext('2d');
const paint = () => {

const mid = Math.round(ch / 2);
const tWin = bufLen * interval;
const t0 = Date.now() - tWin;
const bar = Math.ceil(cw / bufLen);
ctx.clearRect(0, 0, cw, ch);
ctx.fillStyle = style.getPropertyValue('--canvas-axis');
ctx.fillRect(0, mid, cw, 1);
ctx.fillStyle = style.getPropertyValue('--canvas-bars');
for (const ping of pings) {
const x = (ping.at - t0) / tWin * cw;
const h = ping.roundtrip ? ping.roundtrip / worst * ch : 1;
const y = mid - h / 2 + (ping.diff || 0) / worst;
ctx.fillRect(x, y, bar, h);
}
ctx.strokeStyle = style.getPropertyValue('--canvas-line');
ctx.lineWidth = dpr * 2;
ctx.beginPath();
for (const [i, ping] of recv.entries()) {
const x = (ping.at - t0) / tWin * cw;
const y = mid + (ping.diff || 0) / worst;
if (i) ctx.lineTo(x, y);
else ctx.moveTo(x, y);
}
ctx.stroke();

if (changed) {
let min = Infinity, max = 0, sum = 0, num = 0, jit = 0;
for (const [i, ping] of pings.entries()) {
const rtt = ping.roundtrip;
if (typeof rtt != 'number') continue;
num++;
sum += rtt;
min = Math.min(min, rtt);
max = Math.max(max, rtt);
if (i) jit = Math.max(jit, Math.abs(ping.diff));
}
if (num) {
const avg = sum / num;
avgSpan.textContent = `Avg; ${avg.toFixed(1)} ms`;
minSpan.textContent = `Min: ${Math.round(min)} ms`;
maxSpan.textContent = `Max: ${Math.round(max)} ms`;
jitSpan.textContent = `Jitter: ${Math.round(jit)} ms`;
}
changed = false;
}

requestAnimationFrame(paint);

};
requestAnimationFrame(paint);

}

</script>

</body>
Expand Down
8 changes: 8 additions & 0 deletions template/this-computer.conf
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ server {
index index.html;
try_files $uri $uri.html =404;
}
location /ws {
proxy_pass http://127.0.0.1:1234;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_read_timeout 60m;
}

}

Expand Down

0 comments on commit beb2324

Please sign in to comment.