feat(net,gui): real internet access -- redirects, HTTPS guard, netcheck, and a live ONLINE indicator

The stack could already reach the real internet once the UDP-checksum and
scheduler fixes landed; this makes it work on real-world URLs and gives the
desktop a first-class "is the internet up?" answer.

HTTP client
  - Follows 3xx redirects (Location header, absolute or relative) up to five
    hops, so a bare host that 301s to a path or a canonical host resolves to
    actual content. Verified: `webget google.com/` follows the 301 to
    www.google.com and returns its HTTP 200 homepage.
  - Refuses https:// targets -- at the initial URL and on redirect -- with a
    clear message instead of speaking plaintext to port 443 and timing out.
  - Split the URL parser out so both the initial request and redirect targets
    go through the same path; added a case-insensitive header finder.

netcheck (CLI)
  One command that walks the whole chain -- link, address, gateway ping, DNS,
  and a real HTTP fetch -- and reports each PASS/FAIL, ending in ONLINE or
  DEGRADED. The one-shot answer to "is the internet working?".

Network Monitor (GUI)
  A live Internet: ONLINE/OFFLINE badge with the resolved IP, HTTP status and
  latency. It auto-probes once on open and re-checks on the Check button, all
  through the async HTTP worker so the window never blocks.

Verified against the real internet through QEMU's NAT: DNS resolved
example.com to a live Cloudflare address; `netcheck` passed all five layers
(Internet: ONLINE); the GUI badge showed "ONLINE 104.20.23.154 HTTP 200 240
ms"; redirect following and the HTTPS refusal both behaved. Builds clean on
x86_64, aarch64 and armv8i.

Known limit: no TLS, so HTTPS URLs are reported rather than fetched -- plain
HTTP only.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fh5z6V7nGPbHvqVG5p4CXX
This commit is contained in:
subhobhai943andClaude Opus 4.8 committed 2026-08-27 02:44:53 +05:30
1 parent dd4922767a
commit f6e4511047
4 files changed
+308 -33

No files matched your search

+78 -2
View File
@@ -26,6 +26,7 @@
#include <net/tcp.h>
#include <net/udp.h>
#include <net/socket.h>
#include <net/http_client.h>
#include <drivers/e1000.h>
#define NETMON_HISTORY 64
@@ -46,8 +47,57 @@ typedef struct {
bool primed; // a baseline reading has been taken
int scroll; // first connection row shown
// Internet reachability probe (async, via the HTTP worker).
http_fetch_t* probe;
int inet_state; // 0 unknown, 1 checking, 2 online, 3 offline
char inet_detail[72];
bool auto_checked;
} netmon_data_t;
#define INET_UNKNOWN 0
#define INET_CHECKING 1
#define INET_ONLINE 2
#define INET_OFFLINE 3
#define NETMON_PROBE_HOST "example.com/" // a plain-HTTP host to reach for
// Advance the reachability probe: collect a finished result, or note that one
// is still in flight. Runs every paint; the fetch itself is on the worker.
static void netmon_poll_probe(netmon_data_t* nd) {
if (!nd->probe) return;
int st = nd->probe->state;
if (st == HTTP_STATE_RUNNING) { nd->inet_state = INET_CHECKING; return; }
if (st == HTTP_STATE_DONE && nd->probe->status_code > 0) {
char ipx[16];
ip_to_str(nd->probe->ip, ipx);
nd->inet_state = INET_ONLINE;
snprintf(nd->inet_detail, sizeof(nd->inet_detail), "%s HTTP %d %u ms",
ipx, nd->probe->status_code, nd->probe->elapsed_ms);
} else {
nd->inet_state = INET_OFFLINE;
snprintf(nd->inet_detail, sizeof(nd->inet_detail), "%s",
st == HTTP_STATE_ERROR ? nd->probe->err : "no response");
}
http_fetch_release(nd->probe);
nd->probe = NULL;
}
static void netmon_start_probe(netmon_data_t* nd) {
if (nd->probe) return; // one in flight already
nd->probe = http_fetch_start(NETMON_PROBE_HOST, 4096);
if (nd->probe) {
nd->inet_state = INET_CHECKING;
} else {
// The single worker slot is busy (e.g. the Web app is fetching).
nd->inet_state = INET_UNKNOWN;
snprintf(nd->inet_detail, sizeof(nd->inet_detail), "worker busy, try again");
}
}
// ===========================================================================
// Sampling
// ===========================================================================
@@ -217,8 +267,32 @@ static void netmon_paint(gui_window_t* win) {
(unsigned)tcp_get_connections_count(), tcp_get_listener_count());
gui_label_aligned(0, row + 12, w - 12, buf, GUI_THEME_TEXT_MUTED, GUI_ALIGN_RIGHT);
// --- internet reachability -------------------------------------------
// Kick off one probe automatically the first time the window paints, then
// let the user re-check on demand. The fetch runs on the worker thread, so
// this stays responsive while a check is in flight.
if (!nd->auto_checked) { nd->auto_checked = true; netmon_start_probe(nd); }
netmon_poll_probe(nd);
int irow = row + 28;
static const char* const inet_labels[] = { "UNKNOWN", "checking...", "ONLINE", "OFFLINE" };
uint32_t inet_col = (nd->inet_state == INET_ONLINE) ? GUI_THEME_SUCCESS
: (nd->inet_state == INET_OFFLINE) ? GUI_THEME_DANGER
: (nd->inet_state == INET_CHECKING)? GUI_THEME_WARNING
: GUI_THEME_TEXT_DIM;
gui_label(10, irow, "Internet:", GUI_THEME_TEXT_MUTED);
gui_badge(84, irow - 1, inet_labels[nd->inet_state],
inet_col, GUI_COLOR_BLACK);
if (nd->inet_detail[0]) {
gui_label(150, irow, nd->inet_detail, GUI_THEME_TEXT_DIM);
}
if (gui_button(5, w - 82, irow - 3, 72, 18,
nd->probe ? "checking" : "Check")) {
netmon_start_probe(nd);
}
// --- connection table -------------------------------------------------
int tbl_y = row + 30;
int tbl_y = row + 46;
gui_separator(10, tbl_y - 4, w - 20);
gui_label_bold(10, tbl_y, "PROTO LOCAL", GUI_THEME_TEXT_DIM);
gui_label(w / 2 + 6, tbl_y, "REMOTE", GUI_THEME_TEXT_DIM);
@@ -306,7 +380,9 @@ static void netmon_event(gui_window_t* win, const gui_event_t* ev) {
gui_widget_feed_event(win, ev);
if (ev->type == GUI_EVENT_CLOSE && win->user_data) {
kfree(win->user_data);
netmon_data_t* nd = (netmon_data_t*)win->user_data;
if (nd->probe) http_fetch_release(nd->probe); // safe mid-fetch
kfree(nd);
win->user_data = NULL;
}
}