Expose peer and piece state to web UI

This commit is contained in:
ookami125 2026-06-17 01:29:44 -04:00
parent f4f4e86be4
commit 6ae72907b0
6 changed files with 213 additions and 11 deletions

View file

@ -26,6 +26,7 @@
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -51,12 +52,16 @@ typedef struct {
typedef struct {
int fd;
naut_peer_addr addr;
char name[40];
uint8_t peer_id[20];
bool have_peer_id;
uint8_t *rbuf; size_t rcap, rlen;
bool hs_done, peer_choking;
naut_bitfield have;
req_t *inflight; size_t nflight, cflight;
uint64_t blocks_received;
uint64_t bytes_received;
naut_ext_handshake extensions;
uint64_t pex_received;
naut_pipeline pipeline;
@ -121,11 +126,77 @@ static void on_piece_complete(void *opaque, uint32_t index) {
emit_event(config, NAUT_EVENT_PIECE_COMPLETE, index, NULL, NULL);
}
static void peer_client_label(const peer_t *peer, char out[64]) {
if (!peer || !peer->have_peer_id) {
snprintf(out, 64, "Unknown");
return;
}
if (peer->peer_id[0] == '-' && peer->peer_id[7] == '-') {
char code[3] = {
(char)peer->peer_id[1],
(char)peer->peer_id[2],
0,
};
char version[5];
memcpy(version, peer->peer_id + 3, 4);
version[4] = 0;
for (size_t i = 0; i < sizeof version - 1; i++)
if (!isprint((unsigned char)version[i])) version[i] = '?';
snprintf(out, 64, "%s %s", code, version);
return;
}
char id[21];
memcpy(id, peer->peer_id, sizeof peer->peer_id);
id[20] = 0;
for (size_t i = 0; i < sizeof id - 1; i++)
if (!isprint((unsigned char)id[i])) id[i] = '.';
snprintf(out, 64, "%s", id);
}
static void peer_flags(const peer_t *peer, char out[16]) {
size_t n = 0;
if (peer && !peer->peer_choking && n + 1 < 16) out[n++] = 'D';
if (peer && (peer->extensions.ut_pex || peer->pex_received) && n + 1 < 16)
out[n++] = 'X';
out[n] = 0;
}
static void snapshot_peer_stats(naut_swarm_stats *stats,
const peer_t *peers, int npeers,
uint32_t total_pieces) {
if (!stats || !peers || npeers <= 0) return;
for (int i = 0; i < npeers &&
stats->peer_count < NAUT_SWARM_MAX_PEER_STATS; i++) {
const peer_t *peer = &peers[i];
if (peer->dead || !peer->hs_done) continue;
naut_swarm_peer_stats *out =
&stats->peer_stats[stats->peer_count++];
snprintf(out->ip, sizeof out->ip, "%u.%u.%u.%u",
peer->addr.ip[0], peer->addr.ip[1],
peer->addr.ip[2], peer->addr.ip[3]);
out->port = peer->addr.port;
peer_client_label(peer, out->client);
snprintf(out->connection, sizeof out->connection, "TCP");
peer_flags(peer, out->flags);
size_t have = peer->have.words ? naut_bitfield_count(&peer->have) : 0;
double ratio = total_pieces
? (double)have / (double)total_pieces : 0.0;
if (ratio > 1.0) ratio = 1.0;
out->progress = ratio;
out->relevance = ratio;
out->dlspeed = peer->pipeline.bytes_per_second;
out->upspeed = 0.0;
out->downloaded = peer->bytes_received;
out->uploaded = 0;
}
}
static void report_progress(const naut_swarm_config *config,
const naut_download *download,
const naut_metainfo *metainfo,
uint32_t peers_total, uint32_t peers_connecting,
uint32_t peers_active, uint32_t peers_failed,
const peer_t *peers, int npeers,
double started_at) {
if (!config->on_progress) return;
naut_swarm_stats stats = {
@ -139,6 +210,11 @@ static void report_progress(const naut_swarm_config *config,
.peers_failed = peers_failed,
.elapsed_seconds = now() - started_at,
};
snapshot_peer_stats(&stats, peers, npeers, metainfo->num_pieces);
if (download) {
stats.piece_state_count = (uint32_t)naut_download_piece_states(
download, stats.piece_states, NAUT_SWARM_MAX_PIECE_STATS);
}
config->on_progress(config->context, &stats);
}
@ -476,6 +552,7 @@ static bool peer_start(naut_download *download, const naut_metainfo *metainfo,
const uint8_t peerid[20], const endpoint_t *endpoint,
peer_t *peer, int fd) {
peer->fd = fd;
peer->addr = endpoint->addr;
snprintf(peer->name, sizeof peer->name, "%s", endpoint->name);
peer->peer_choking = true;
naut_pipeline_init(&peer->pipeline, NAUT_BLOCK, 4, 1024, 32);
@ -580,6 +657,8 @@ static naut_err peer_process(naut_download *d, const naut_metainfo *mi,
p->dead = true;
return NAUT_OK;
}
memcpy(p->peer_id, pid, sizeof p->peer_id);
p->have_peer_id = true;
pos = NAUT_HANDSHAKE_LEN;
p->hs_done = true;
}
@ -642,6 +721,7 @@ static naut_err peer_process(naut_download *d, const naut_metainfo *mi,
(uint32_t)m.payload_len, &pdone);
if (e == NAUT_OK) {
p->blocks_received++;
p->bytes_received += m.payload_len;
cancel_block(d, peers, npeers, p, m.index, m.begin);
} else if (e == NAUT_ERR_PROTO) {
if (expected) cancel_piece(d, peers, npeers, m.index);
@ -837,7 +917,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
uint32_t failed = 0;
struct pollfd *connect_fds = pfd;
for (int i = 0; i < npeers; i++) connect_fds[i].fd = -1;
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0, 0, t0);
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0, 0,
peers, npeers, t0);
for (int i = 0; i < npeers; i++) {
bool connected = false;
int fd = connect_start(&endpoints[i], &connected);
@ -871,7 +952,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
while (connecting > 0 && now() < connect_deadline &&
!stop_requested(config)) {
report_progress(config, d, &mi, (uint32_t)npeers, connecting,
active, failed, t0);
active, failed, peers, npeers, t0);
int ready = poll(connect_fds, (nfds_t)neps, 100);
if (ready < 0) {
if (errno == EINTR) continue;
@ -921,7 +1002,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
active, npeers, mi.num_pieces, (long long)mi.total_length);
emit_event(config, NAUT_EVENT_TORRENT_ADDED, 0, NULL, NULL);
report_progress(config, d, &mi, (uint32_t)npeers, 0, active, failed, t0);
report_progress(config, d, &mi, (uint32_t)npeers, 0, active, failed,
peers, npeers, t0);
while (!naut_download_complete(d) && run_error == NAUT_OK) {
service_control(config, st);
if (stop_requested(config)) {
@ -951,7 +1033,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
}
report_progress(config, d, &mi, (uint32_t)npeers, 0,
(uint32_t)live_peers,
(uint32_t)npeers - (uint32_t)live_peers, t0);
(uint32_t)npeers - (uint32_t)live_peers,
peers, npeers, t0);
int r = poll(pfd, nf, 200);
if (r < 0) {
if (errno == EINTR) continue;
@ -1007,7 +1090,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
naut_download_pieces_done(d), mi.num_pieces, dt, mb/dt,
naut_download_in_endgame(d) ? " (passed through endgame)" : "");
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0,
(uint32_t)npeers, t0);
(uint32_t)npeers, peers, npeers, t0);
emit_event(config, NAUT_EVENT_TORRENT_FINISHED, 0, NULL, NULL);
while (config->keep_alive && !stop_requested(config)) {
service_control(config, st);