aboutsummaryrefslogtreecommitdiffstats
path: root/usr/httpd.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-22 21:54:03 +0200
committeruser <user@clank>2026-09-22 21:54:03 +0200
commitdcd1f1320c102f9cc4cff7eee387ec538304f7d1 (patch)
treee0b48e345c8d34a5f110378e795b8690120ebd55 /usr/httpd.c
parentbuild: rebuild every object when include/*.inc changes (diff)
downloadgbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.tar.gz
gbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.tar.xz
gbos-dcd1f1320c102f9cc4cff7eee387ec538304f7d1.zip
net: one socket per client - four-tuple demux, spawning accept, bounded close
A server could only ever hold ONE connection, which makes an ircd pointless: listen() turned the listener into the connection, and net_find_tcp demuxed on local port alone, so two clients on 6667 were indistinguishable. - net_find_tcp now matches an established socket on the full four-tuple (local port + peer IP + peer port) and only falls back to a LISTENing socket when none matches. - A SYN at a listener no longer consumes it: tcp_spawn_conn clones a new socket (local port + owning pid, so exit/kill still reclaims it) and the listener keeps listening. accept() returns that socket; closing it leaves the listener alone. A full table drops the SYN, and the client's retransmit is taken once a slot frees. - tcp_peer_is_current preserves BC: net_find_tcp scans with the socket index there, and the compare needs BC for the rx buffer base. Without this the scan died at the first mismatching socket, so a second client's handshake ACK never reached its socket and it hung in SYNRCVD - tcpdump showed our SYN+ACK going out and the client's NICK retransmitted five times into silence. - listen() only conflicts with another LISTENER on the port; connections share it by design now. - MAX_SOCKS 4 -> 6 (listener + 3 clients + 2 spare; 537 bytes of WRAM0 still free), and SK_ACC marks a spawned connection as not yet accepted. tcp_close's settle is also time-boxed to ~250 ms of real time. It counted PUMPS (8 x 8000), which is ~30s of wall clock when the peer never answers - invisible with well-behaved peers, but a daemon refusing a client that stayed connected froze itself, and every other client with it, for half a minute. httpd keeps its listener open across requests now, so the next client's SYN is accepted immediately instead of being dropped between close and re-listen: three parallel clients go 0.1/1.1/2.1s -> 0.4/1.0/0.1s.
Diffstat (limited to 'usr/httpd.c')
-rw-r--r--usr/httpd.c31
1 files changed, 16 insertions, 15 deletions
diff --git a/usr/httpd.c b/usr/httpd.c
index 335e824..e704e8c 100644
--- a/usr/httpd.c
+++ b/usr/httpd.c
@@ -33,7 +33,8 @@
#define REQSZ 120 /* the request line is all we parse */
#define PATHSZ 48
-static unsigned char sock;
+static unsigned char lsock; /* the listener, held for our lifetime */
+static unsigned char sock; /* the client being served right now */
static unsigned char rbuf[BUFSZ];
static char obuf[OBUFSZ];
static unsigned char olen;
@@ -256,22 +257,22 @@ void main(void) {
puts(" (run it with & and kill it to stop)");
nl();
+ lsock = net_socket(SOCK_TCP);
+ if (lsock == 0xFF) {
+ puts("httpd: no socket");
+ nl();
+ sexit(1);
+ }
+ if (net_listen(lsock, port) == 0xFF) {
+ puts("httpd: listen failed (port in use?)");
+ nl();
+ net_close(lsock);
+ sexit(1);
+ }
for (;;) {
- sock = net_socket(SOCK_TCP);
- if (sock == 0xFF) {
- puts("httpd: no socket");
- nl();
- sexit(1);
- }
- if (net_listen(sock, port) == 0xFF) {
- puts("httpd: listen failed");
- nl();
- net_close(sock);
- sexit(1);
- }
- while (net_accept(sock) != 0) /* one RX pump per call */
+ while ((sock = net_accept(lsock)) == 0xFE) /* one RX pump per call */
msleep(20);
handle();
- net_close(sock); /* FIN: this is what ends the body */
+ net_close(sock); /* FIN: this is what ends the body; listener stays */
}
}