AxlNet — Networking
TCP sockets, UDP sockets, socket abstraction layer, URL parsing, HTTP server, HTTP client, TLS, and network utilities (IPv4 address helpers, interface enumeration, ping).
Individual headers can be included separately or use the umbrella
<axl/axl-net.h>.
Headers:
<axl/axl-net.h>— Umbrella + network utilities<axl/axl-inet-address.h>— IP address and socket address types<axl/axl-socket.h>— Unified socket (stream/datagram)<axl/axl-socket-client.h>— High-level DNS + connect helper<axl/axl-tcp.h>— TCP sockets (low-level)<axl/axl-udp.h>— UDP sockets (low-level)<axl/axl-url.h>— URL parsing<axl/axl-http-core.h>— Low-level HTTP/1.1 parsing (shared by server + client)<axl/axl-http-server.h>— HTTP server<axl/axl-http-client.h>— HTTP client<axl/axl-tls.h>— TLS support (optional, requiresAXL_TLS=1)
Overview
AXL networking is built on UEFI’s TCP4/UDP4 protocol stack. The stack
must be initialized before use — either by the UEFI Shell (ifconfig)
or by calling axl_net_auto_init.
Application
├─ axl_http_server / axl_http_client
├─ axl_socket_client (DNS + connect)
├─ axl_socket (stream/datagram)
│ ├─ axl_tcp / axl_udp
│ └─ axl_socket_address / axl_inet_address
├─ axl_tls (optional, wraps TCP)
└─ UEFI TCP4 / UDP4 protocols
└─ IP4 → ARP → SNP (NIC driver)
Network Initialization
The recommended one-call shape is axl_net_bring_up — load drivers,
acquire an IP (DHCP or static), optionally read it back. Used by
HTTP services, REST tools, and one-shot fetch utilities — they all
open with the same preamble:
// DHCP — most common case
if (axl_net_bring_up(SIZE_MAX, NULL, NULL, NULL, 10, NULL) != AXL_OK) {
axl_printf("Network not available\n");
return -1;
}
// Static IP — pass the address (NULL netmask = /24 default,
// NULL gateway = none); also reads the resolved address back.
uint8_t ip[] = { 192, 168, 1, 100 };
AxlIPv4Address addr;
if (axl_net_bring_up(SIZE_MAX, ip, NULL, NULL, 0, &addr) != AXL_OK) {
return -1;
}
Standard option helpers
Most consumers (tools, services) take the same NIC / local-IP /
port options on the command line and run the same DHCP bring-up
preamble. <axl/axl-net-opts.h> ships a canonical option bag
plus a one-call init helper:
typedef struct {
AxlNetOpts net; // embed as sub-struct
const char *url;
bool verbose;
} MyOpts;
// DHCP bring-up driven by the bag — maps AXL_NET_NIC_AUTO to
// SIZE_MAX and runs axl_net_bring_up under the hood:
if (axl_net_init_from_opts(&opts.net, 10) != AXL_OK) {
axl_printf("network unavailable\n");
return 1;
}
// Use opts.net.local_ip for the local socket bind — outbound
// source for clients, listen address for servers (same bind(2)).
The bag carries three fields:
nic_index— which NIC to DHCP on;AXL_NET_NIC_AUTOpicks the first usable one.local_ip— IPv4 tobind(2)the local socket end to. Outbound source for clients (curl--interface-style), listen address for servers — same syscall, role implied by what the consumer does next.port—uint16_t; consumers define their own domain default.
Out of scope by design: installing a static IPv4 on the NIC.
That’s a firmware-ifconfig-layer concern (UEFI Shell
ifconfig, or axl_net_set_static_ip for tools that genuinely
need to mutate IP4Config2 policy). The options bag is for
stateless connection-side selectors only.
Pair with the descriptor-table composition helpers in
<axl/axl-config.h> (axl_config_descs_net,
axl_config_descs_append) to also inject the matching CLI / config
descriptors into a consumer’s own table without copy-paste — see
the AxlConfig docs. The AXL_NET_OPT_SOURCE_IP and
AXL_NET_OPT_LISTEN_IP selector bits both target the same
local_ip field; they differ only in CLI vocabulary
(--source-ip vs --listen-ip).
Three layered primitives sit underneath if a consumer needs finer control:
axl_net_drivers_up()— load NIC drivers, connect SNP, wait for link-up (5 s budget). No DHCP, no IP assignment.axl_net_auto_init(nic, dhcp_timeout)— drivers_up + DHCP wait. Used by the DHCP path ofbring_upinternally. Event-driven viaEFI_IP4_CONFIG2_PROTOCOL.RegisterDataNotify— sub-millisecond wakeup after DHCP completes (firmware that doesn’t support the notify falls back to a 1 s tick).axl_net_set_static_ip(nic, ip, netmask, gateway)— raw IP4Config2 setter; static path ofbring_upcalls it afterdrivers_up.
Socket Layer
AxlSocket is the recommended socket API for new code — a
GLib-GSocket-shaped abstraction over both stream (TCP) and datagram
(UDP) transports, with rich address types and blocking and async
forms. It delegates to the low-level AxlTcp / AxlUdp
primitives under the hood (see the Low-Level TCP / UDP
section below).
Address Types
AxlInetAddress wraps an IPv4 address with parsing, formatting,
and comparison:
// Create from string or bytes
AxlInetAddress *addr = axl_inet_address_new_from_string("192.168.1.1");
AxlInetAddress *lo = axl_inet_address_new_loopback();
const char *str = axl_inet_address_to_string(addr); // "192.168.1.1"
const uint8_t *bytes = axl_inet_address_to_bytes(addr);
axl_inet_address_free(addr);
axl_inet_address_free(lo);
AxlSocketAddress pairs an address with a port:
// From address + port (takes ownership of the AxlInetAddress)
AxlSocketAddress *sa = axl_socket_address_new(
axl_inet_address_new_from_string("10.0.0.1"), 8080);
// Or parse "host:port"
AxlSocketAddress *sa2 = axl_socket_address_new_from_string("10.0.0.1:8080", 0);
axl_socket_address_free(sa);
axl_socket_address_free(sa2);
Unified Socket
TCP client:
AXL_AUTOPTR(AxlSocket) sock = axl_socket_new(AXL_SOCKET_STREAM);
AxlSocketAddress *remote = axl_socket_address_new(
axl_inet_address_new_from_string("192.168.1.1"), 8080);
if (axl_socket_connect(sock, remote) == 0) {
axl_socket_send(sock, "hello", 5, 0);
char buf[64];
size_t len = sizeof(buf);
axl_socket_receive(sock, buf, &len, 5000);
}
axl_socket_address_free(remote);
TCP server (blocking):
AXL_AUTOPTR(AxlSocket) listener = axl_socket_new(AXL_SOCKET_STREAM);
axl_socket_listen(listener, 8080);
AxlSocket *client;
if (axl_socket_accept(listener, &client) == 0) {
// handle client...
axl_socket_free(client);
}
UDP send:
AXL_AUTOPTR(AxlSocket) sock = axl_socket_new(AXL_SOCKET_DATAGRAM);
AXL_AUTOPTR(AxlSocketAddress) dest = axl_socket_address_new(
axl_inet_address_new_from_string("192.168.1.100"), 514);
axl_socket_send_to(sock, msg, msg_len, dest);
Socket Client
AxlSocketClient combines DNS resolution and TCP connection:
AXL_AUTOPTR(AxlSocketClient) client = axl_socket_client_new();
AxlSocket *sock;
if (axl_socket_client_connect_to_host(client, "example.com", 80, &sock) == 0) {
axl_socket_send(sock, request, req_len, 0);
axl_socket_free(sock);
}
Or connect to a resolved address:
AxlSocketAddress *addr = axl_socket_address_new(
axl_inet_address_new_from_string("10.0.0.1"), 8080);
AxlSocket *sock;
axl_socket_client_connect(client, addr, &sock);
axl_socket_address_free(addr);
Async Operations
The socket layer supports async operations via AxlLoop. Callbacks
return bool — true keeps the op armed (accept-the-next-client or
re-issue-recv-on-same-buffer), false tears down. Returning false
permits closing the socket inside the callback: the loop does not
touch the socket again after a false return.
bool on_client(AxlSocket *client, AxlStatus status, void *data) {
if (status != 0) return true; // transient error; keep listening
// handle client...
axl_socket_free(client);
return true; // keep accepting more clients
}
AXL_AUTOPTR(AxlSocket) listener = axl_socket_new(AXL_SOCKET_STREAM);
axl_socket_listen(listener, 8080);
axl_socket_accept_async(listener, loop, on_client, NULL);
axl_loop_run(loop);
See sdk/examples/echo-server.c for a complete async echo server
built on this layer — it uses axl_socket_receive_async in
stays-armed mode (callback returns true to keep receiving).
Low-Level TCP / UDP
Most applications should use the Socket Layer above.
AxlTcpandAxlUdpare the primitives underneath – thin wrappers over UEFI’sTCP4_PROTOCOL/UDP4_PROTOCOL. Reach for them only when you need raw access to UEFI tokens, want the session-scoped cancellation pattern shown below, or are minimizing wrapper overhead. Seesdk/examples/tcp-echo-server.cfor the low-level counterpart to the socket-basedecho-server.c.
TCP Sockets
Blocking and async TCP sockets. The blocking API is simpler; the async API integrates with the event loop for non-blocking I/O.
Client (blocking):
AxlTcp *sock;
if (axl_tcp_connect("192.168.1.1", 8080, &sock) == 0) {
axl_tcp_send(sock, "GET / HTTP/1.0\r\n\r\n", 18, 5000);
char buf[4096];
size_t len = sizeof(buf);
axl_tcp_recv(sock, buf, &len, 5000);
axl_tcp_close(sock);
}
Server (async with event loop):
bool on_client(AxlTcp *client, AxlStatus status, void *data) {
if (status != 0) return true; // transient error; keep listening
// handle client connection...
axl_tcp_close(client);
return true; // keep accepting more clients
}
AxlTcp *listener;
axl_tcp_listen(8080, &listener);
axl_tcp_accept_async(listener, loop, /*cancel=*/NULL, on_client, NULL);
axl_loop_run(loop);
Session-scoped cancellation:
Every axl_tcp_*_async call accepts an optional AxlCancellable *.
Share one cancellable across all ops tied to a session — closing the
session cancels every in-flight op at once, each firing its callback
with status == AXL_CANCELLED.
typedef struct { AxlCancellable *cancel; AxlTcp *sock; } Session;
static bool on_connected(AxlTcp *sock, AxlStatus status, void *data) {
Session *s = data;
if (status == AXL_CANCELLED) return true; // session closed before connect
s->sock = sock;
axl_tcp_recv_async(sock, s->rxbuf, sizeof(s->rxbuf),
loop, s->cancel, on_data, s);
return true; // connect fires once; return value ignored for connect
}
Session *s = axl_new0(Session);
s->cancel = axl_cancellable_new();
axl_tcp_connect_async(host, port, loop, s->cancel, on_connected, s);
// Later, from any handler -- user closes the tab, subsystem shuts
// down, a parent cancellable fires: every op tagged with s->cancel
// stops and its callback fires exactly once with AXL_CANCELLED.
axl_cancellable_cancel(s->cancel);
UDP Sockets
Fire-and-forget datagram sending, request-response patterns, plus
async receive / send and connection-style peer locking. Mirrors
AxlTcp’s async / cancellable / source-IP-pinning shape.
AXL_AUTOPTR(AxlUdp) sock = NULL;
axl_udp_open(&sock, 0); // ephemeral local port
// or pin to a specific NIC:
// axl_udp_open_via(&sock, 0, &source_ip);
uint16_t bound;
char bound_addr[16];
axl_udp_get_local_addr(sock, bound_addr, sizeof(bound_addr), &bound);
AxlIPv4Address dest;
axl_ipv4_parse("192.168.1.100", dest.addr);
// Fire-and-forget (sync, 2 s timeout)
axl_udp_send(sock, &dest, 514, msg, msg_len);
// Request-response (e.g., DNS query)
char reply[512];
size_t reply_len;
axl_udp_sendrecv(sock, &dest, 53, query, query_len,
reply, sizeof(reply), &reply_len, 3000);
Async send + receive, with optional AxlCancellable:
bool on_recv(AxlUdp *s, AxlStatus status, const void *data, size_t len,
const AxlIPv4Address *from, uint16_t from_port, void *udata) {
if (status != AXL_OK) return false; // err / cancel — stop
process(data, len, from);
return true; // re-arm for next datagram
}
axl_udp_recv_async(sock, loop, /*cancel=*/NULL, on_recv, NULL);
bool on_sent(AxlUdp *s, AxlStatus status, void *udata) {
if (status != AXL_OK) log_warn("send failed");
return true; // ignored for send
}
axl_udp_send_async(sock, &dest, 514, msg, msg_len,
loop, /*cancel=*/NULL, on_sent, NULL);
Connection-style peer lock — kernel-side recv filter plus
NULL-dest shorthand on send:
axl_udp_connect(sock, &peer, 9999);
axl_udp_send(sock, NULL, 0, msg, msg_len); // uses configured peer
axl_udp_disconnect(sock); // back to "send anywhere"
Multicast / broadcast:
AxlIPv4Address mdns = { .addr = {224, 0, 0, 251} };
axl_udp_join_multicast(sock, &mdns); // mDNS group
axl_udp_set_broadcast(sock, true); // accept inbound broadcasts
// ... receive ...
axl_udp_leave_multicast(sock, NULL); // leave all groups
HTTP Server
Create an HTTP server with route handlers:
void on_hello(AxlHttpRequest *req, AxlHttpResponse *resp, void *data) {
axl_http_respond_text(resp, 200, "Hello from AXL!\n");
}
AXL_AUTOPTR(AxlHttpServer) s = axl_http_server_new(8080);
axl_http_server_add_route(s, "GET", "/hello", on_hello, NULL);
axl_http_server_run(s); // blocks, serving requests
For multiple routes, the variadic batch form collapses the per-call error checks into one:
axl_http_server_add_routes(s,
"GET", "/version", on_version, NULL,
"GET", "/health", on_health, NULL,
"POST", "/echo", on_echo, NULL,
NULL); // sentinel — required
REST request helpers
For REST-shaped handlers, three helpers route through the existing HTTP machinery so routes don’t reinvent content negotiation or JSON body parsing:
int handle_request(AxlHttpRequest *req, AxlHttpResponse *resp, void *data) {
if (axl_http_request_wants_json(req)) {
// ...emit JSON
}
AxlJsonReader r;
if (axl_http_request_get_json(req, &r)) {
int64_t value;
if (axl_json_get_int(&r, "key", &value)) { /* ... */ }
axl_json_free(&r);
}
return 0;
}
axl_http_request_accepts(req, mime) is the underlying primitive
(case-insensitive, multi-type lists, wildcards, q-value tolerant).
_wants_json is the application/json shorthand. _get_json
parses req->body into a caller-owned AxlJsonReader (caller
frees with axl_json_free).
The server supports middleware, WebSocket endpoints, authentication, response caching, streaming uploads, and WebDAV mounts. See the API reference for details.
WebDAV class-1 + MOVE/COPY
axl_http_server_add_webdav(s, prefix, &ops, user_data) mounts
a WebDAV handler at the given URL prefix. Verb scope:
OPTIONS, PROPFIND, GET, HEAD, PUT, DELETE, MKCOL, MOVE, COPY —
covers class-1 plus MOVE and COPY. PROPPATCH, LOCK, UNLOCK, and
If-header conditionals remain out of v1 scope (Windows Explorer,
macOS Finder, davfs2, cadaver work without them when the server
doesn’t advertise the lock class).
The consumer fills in an AxlWebDavOps callback table mapped
onto its own filesystem; the SDK owns the protocol — verb
dispatch, PROPFIND 207 Multi-Status XML emit (driven by
AxlXmlWriter; see <axl/axl-xml.h>),
Depth / Destination / Overwrite header parsing, RFC 1123
Last-Modified date formatting, RFC 3230 Want-Digest /
Digest header negotiation (opt-in via the consumer’s
optional digest callback), DAV: 1 advertisement on every
WebDAV-method response. GET inherits
axl_http_response_set_streamer (multi-GB safe, Range
requests via axl_http_response_set_content_range); PUT
inherits the upload-route chunk handler (write_open / chunk /
close(aborted)). Per-mount single-in-flight PUT — concurrent
PUTs to the same mount are refused rather than silently
trampling each other’s state.
static int my_stat(void *user, const char *path, AxlWebDavEntry *out);
static int my_list_dir(void *user, const char *path,
AxlWebDavEntry *out, size_t max, size_t *count);
static int my_read_open (void *user, const char *path,
uint64_t offset, void **out_ctx);
static int my_read_chunk(void *ctx, void *buf, size_t cap, size_t *got);
static void my_read_close(void *ctx);
/* ... write_open / write_chunk / write_close / mkdir / remove
/ move / copy / content_type ... */
static const AxlWebDavOps my_ops = {
.stat = my_stat,
.list_dir = my_list_dir,
.read_open = my_read_open,
.read_chunk = my_read_chunk,
.read_close = my_read_close,
/* ... */
};
axl_http_server_add_webdav(server, "/dav", &my_ops, my_user_data);
Up to 4 WebDAV mounts per server. The ops struct is COPIED
into the server; the consumer may free or re-use it after
add_webdav returns. user_data is borrowed and must outlive
the server.
Streaming uploads
axl_http_server_add_upload_route(server, method, path, handler, data)
registers a route that streams the body to handler in chunks
instead of buffering — required for multi-GB uploads (the body never
materializes in RAM, bypasses body.limit).
The AxlUploadHandler callback distinguishes three terminating
shapes by the chunk and aborted arguments:
chunk |
aborted |
meaning |
|---|---|---|
|
|
body chunk arrived; process it and return AXL_OK |
|
|
clean EOF — set |
|
|
TCP disconnect / recv error — release per-request state |
The abort call is mutually exclusive with the clean-EOF call: a handler that received the clean-EOF call will NOT also receive an abort, even if the response send subsequently fails. On abort the handler MUST NOT touch the connection or call any response setter — it exists only to release per-request state (open file handles, accumulators, allocations) accumulated across earlier chunk calls. Without this signal, that state leaks across requests.
Middleware registered via axl_http_server_use runs before the
upload handler sees a single byte. On rejection the connection is
force-closed (clients almost always send body bytes before reading
the rejection — staying in keep-alive desyncs the next request).
Header-based gating only — the body isn’t materialized so middleware
that needs the body can’t apply to upload routes.
HTTP Client
AXL_AUTOPTR(AxlHttpClient) c = axl_http_client_new();
AXL_AUTOPTR(AxlHttpClientResponse) resp = NULL;
if (axl_http_get(c, "http://192.168.1.1:8080/api/status", &resp) == 0) {
axl_printf("HTTP %zu\n", resp->status_code);
if (resp->body != NULL) {
axl_printf("%.*s\n", (int)resp->body_size, (char *)resp->body);
}
}
HTTPS URLs are automatically detected when built with AXL_TLS=1.
TLS (HTTPS)
Optional TLS 1.2 support using mbedTLS 3.6. Provides HTTPS server/client, self-signed certificate generation, and transparent TCP encryption.
Build requirement: make AXL_TLS=1 (adds ~200KB to the binary).
Without this flag, all TLS functions return -1/NULL/false.
Header: <axl/axl-tls.h>
AXL’s TLS module wraps mbedTLS to provide:
Self-signed ECDSA P-256 certificate generation
TLS 1.2 server contexts (for HTTPS)
TLS 1.2 client contexts (for HTTPS GET/POST)
Transparent integration with AxlHttpServer and AxlHttpClient
HTTPS Server
Generate a certificate and enable TLS on the HTTP server:
#include <axl.h>
axl_tls_init();
// Generate self-signed cert (valid 10 years, ECDSA P-256)
void *cert, *key;
size_t cert_len, key_len;
axl_tls_generate_self_signed("MyServer", NULL, 0,
&cert, &cert_len, &key, &key_len);
// Create HTTPS server
AxlHttpServer *s = axl_http_server_new(8443);
axl_http_server_use_tls(s, cert, cert_len, key, key_len);
axl_http_server_add_route(s, "GET", "/", handler, NULL);
axl_free(cert);
axl_free(key);
axl_http_server_run(s); // serves HTTPS
HTTPS Client
HTTPS is automatic — just use an https:// URL:
AXL_AUTOPTR(AxlHttpClient) c = axl_http_client_new();
AXL_AUTOPTR(AxlHttpClientResponse) resp = NULL;
// TLS handshake happens automatically
axl_http_get(c, "https://192.168.1.1:8443/api/status", &resp);
Certificate Generation
axl_tls_generate_self_signed creates an ECDSA P-256 certificate
with SHA-256 signature:
Subject:
CN=<name>,O=AximCodeValidity: current year to +10 years
SubjectAltName:
DNS:localhost,IP:127.0.0.1, plus any provided IP addressesOutput: DER-encoded certificate and private key (caller frees)
Entropy
mbedTLS needs random numbers for key generation and TLS handshakes. AXL provides entropy via:
EFI_RNG_PROTOCOL (hardware RNG) — preferred, used when available
Software fallback — system time + monotonic counter mixing. A warning is logged when the fallback is used.
Security Considerations
Self-signed certificates are not trusted by browsers or standard TLS clients. Use
curl --insecureor configure trust-on-first-use.Certificate verification is disabled for client connections (
MBEDTLS_SSL_VERIFY_NONE). This is appropriate for BMC/embedded use but not for public internet TLS.The software entropy fallback is not cryptographically strong. For production use on hardware without an RNG, consider providing your own entropy source.
API Reference
Network Utilities
Defines
-
AXL_NET_DRIVERS_OK
axl_net_ensure_drivers() return codes.
SNP is registered (already, or after load)
-
AXL_NET_DRIVERS_NOT_FOUND
no NIC drivers found on any mounted volume
-
AXL_NET_DRIVERS_NO_LINK
drivers loaded, but no SNP came up
Functions
-
int axl_net_get_ip_address(AxlIPv4Address *addr)
Get the local IPv4 address of the first configured NIC.
axl-net.h:
Networking umbrella header. Includes socket layer, TCP, UDP, URL, HTTP server, HTTP client, and network utilities.
Individual headers can be included separately:
#include <axl/axl-inet-address.h>— IP address + socket address#include <axl/axl-socket.h>— Unified socket#include <axl/axl-socket-client.h>— DNS + connect helper#include <axl/axl-tcp.h>— TCP sockets (low-level)#include <axl/axl-udp.h>— UDP sockets (low-level)#include <axl/axl-url.h>— URL parsing only#include <axl/axl-http-core.h>— HTTP raw-buffer parsers#include <axl/axl-http-server.h>— HTTP server#include <axl/axl-http-client.h>— HTTP client
- Parameters:
addr – receives the IPv4 address
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_net_ping(AxlIPv4Address *target, size_t timeout_ms, size_t *out_rtt_ms)
Send an ICMP echo request and measure round-trip time.
- Parameters:
target – target IPv4 address
timeout_ms – timeout in milliseconds
out_rtt_ms – receives round-trip time in milliseconds
- Returns:
AXL_OK on success, AXL_ERR on failure or timeout.
-
int axl_net_resolve(const char *hostname, AxlIPv4Address *addr)
Resolve a hostname to an IPv4 address via DNS4. Falls back to parsing the hostname as a dotted-decimal IP.
- Parameters:
hostname – hostname or IP string
addr – receives the resolved address
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
bool axl_net_is_available(void)
Check whether any IPv4 network is available.
- Returns:
true if at least one NIC has an IP address.
-
int axl_net_auto_init(size_t nic_index, size_t dhcp_timeout_sec)
Bring up networking: load drivers, run DHCP, wait for IP.
Performs a best-effort network initialization sequence:
Calls axl_net_ensure_drivers() to locate and load NIC drivers from the standard driver search path.
Connects all SNP handles to trigger protocol stack creation.
Selects a NIC (by
nic_index, or first available if SIZE_MAX).Waits up to
dhcp_timeout_secfor an IPv4 address via DHCP.
- Parameters:
nic_index – NIC index (SIZE_MAX = auto-select first)
dhcp_timeout_sec – DHCP timeout in seconds (0 = 10s default)
- Returns:
AXL_OK on success (IP address acquired), AXL_ERR on failure.
-
int axl_net_ensure_drivers(void)
Ensure network drivers are loaded and SNP is up.
Locates and loads
NetworkCommon.efiplus a known list of NIC drivers (Realtek, Intel/iPXE, Broadcom/iPXE, USB-CDC ECM/NCM, USB-RNDIS, ASIX-USB) from the standard driver search path used by axl_driver_ensure() — drivers/<arch>/<name> on the booted volume, the image’s own directory, drivers/<name> at the volume root, then drivers/<arch>/<name> on every other mounted FAT volume. After loading, ConnectController is run globally to wire the SNP/MNP/ IP4/TCP4 stack.Drivers absent from the volume are skipped silently — the cost of a missing entry is one file existence check. Drivers whose hardware isn’t present register their binding but never bind to a controller, which is also fine.
Short-circuits if an SNP handle already exists. Idempotent — safe to call multiple times.
Same trust caveat as axl_driver_ensure: this loads .efi files off any mounted FAT volume with full firmware privileges.
Typical use, before touching any networking:
if (axl_net_ensure_drivers() != AXL_NET_DRIVERS_OK) { axl_printf("MyTool: networking unavailable\n"); return 1; }
- Returns:
AXL_NET_DRIVERS_OK on success; AXL_NET_DRIVERS_NOT_FOUND if no NIC drivers were found on any mounted volume; AXL_NET_DRIVERS_NO_LINK if drivers were loaded but no SNP came up (likely no NIC plugged in).
-
int axl_net_drivers_up(void)
Load drivers, connect SNP, wait for link.
Decoupled from address assignment — does NOT run DHCP and does NOT touch IP4Config2. Composes axl_net_ensure_drivers + per-handle SNP reconnect + a 5 s link-up poll, which is the front half of axl_net_auto_init.
Used directly by axl_net_bring_up’s static-IP path (where the DHCP wait that auto_init would otherwise burn is dead time) and internally by axl_net_auto_init. Consumers that want IP assignment should call axl_net_bring_up or axl_net_auto_init — those layer DHCP / static configuration on top of this primitive.
- Returns:
AXL_OK on success (at least one NIC link came up); AXL_ERR if no NIC link was detected within the 5 s wait. Drivers and SNP reconnect are best-effort — failures there are not surfaced.
-
int axl_net_bring_up(size_t nic_index, const uint8_t *static_ipv4, const uint8_t *netmask, const uint8_t *gateway, size_t timeout_sec, AxlIPv4Address *addr_out)
Bring up networking with a single call — drivers + DHCP or static IP + address read-back.
Composes the typical “what every networked tool does at startup” sequence into one call so consumers don’t reinvent it. Behavior is controlled by
static_ipv4:static_ipv4== NULL → DHCP. Calls axl_net_auto_init (which itself runs axl_net_drivers_up and waits up totimeout_secfor a lease).static_ipv4!= NULL → static. Calls axl_net_drivers_up (load drivers + link wait, no DHCP timeout), then axl_net_set_static_ip withnetmask(defaulting to255.255.255.0if NULL) andgateway(NULL = no gateway). Sleeps 500 ms after to let IP4Config2 apply the change — the firmware applies the policy + address asynchronously and a subsequentGetDatacan still report the prior state without the settle.
In either case, on success
addr_outis populated via axl_net_get_ip_address (skipped ifaddr_outis NULL).Used by HTTP services (axl-webfs and similar), REST tools, and one-shot fetch-style utilities — they all open with the same “load drivers, get an IP, here’s my address” preamble. AxlService is NOT on the call path; this is plain network bring-up, callable from any AXL-consuming code.
- Parameters:
nic_index – NIC index (SIZE_MAX = auto-select)
static_ipv4 – NULL = DHCP; non-NULL = 4-byte static IPv4
netmask – 4-byte netmask (NULL = 255.255.255.0); ignored on DHCP path
gateway – 4-byte gateway (NULL = none); ignored on DHCP path
timeout_sec – DHCP wait (0 = 10 s default; ignored on static path)
addr_out – [out] resolved IPv4 (NULL = caller doesn’t care)
- Returns:
AXL_OK on success (network up, IP acquired,
addr_outpopulated if non-NULL); AXL_ERR if drivers couldn’t be loaded, no NIC came up, DHCP timed out, or static-IP configuration failed.
-
int axl_net_set_static_ip(size_t nic_index, const uint8_t ip[4], const uint8_t netmask[4], const uint8_t *gateway)
Configure a static IPv4 address on a NIC.
Sets the IP4Config2 policy to static and assigns the given address, subnet mask, and optional gateway. Pass NULL for
gatewayto leave it unconfigured.- Parameters:
nic_index – NIC index (from axl_net_list_interfaces)
ip – IPv4 address
netmask – subnet mask (e.g. {255,255,255,0})
gateway – gateway address (NULL = none)
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_ipv4_parse(const char *str, uint8_t octets[4])
Parse a dotted-decimal IPv4 address string.
Accepts strings like “192.168.1.1”. Each octet must be 0-255. No leading zeros validation — “01.02.03.04” is accepted.
- Parameters:
str – IPv4 string (e.g. “192.168.1.1”)
octets – receives the four octets
- Returns:
AXL_OK on success, AXL_ERR on invalid input.
-
int axl_ipv4_format(const uint8_t octets[4], char *buf, size_t size)
Format an IPv4 address as a dotted-decimal string.
Writes at most
sizebytes (including NUL). 16 bytes is always sufficient (“255.255.255.255” + NUL).- Parameters:
octets – four octets
buf – output buffer
size – buffer size (16 bytes sufficient)
- Returns:
AXL_OK on success, AXL_ERR if buffer is too small or args are NULL.
-
int axl_ipv6_format(const uint8_t octets[16], char *buf, size_t size)
Format 16 IPv6 octets to a colon-separated text representation.
Emits the canonical lowercase form with
::collapsing the longest run of all-zero 16-bit groups, per RFC 5952. Single zero groups are not collapsed; ties go to the leftmost run.Writes at most
sizebytes (including NUL). 40 bytes is always sufficient (max form: “ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff” + NUL, 39 chars + 1).- Parameters:
octets – sixteen octets
buf – output buffer
size – buffer size (40 bytes sufficient)
- Returns:
AXL_OK on success, AXL_ERR if buffer is too small or args are NULL.
-
bool axl_ipv4_equals(const uint8_t a[4], const uint8_t b[4])
True if
aequalsbbyte-for-byte.
-
bool axl_ipv4_in_subnet(const uint8_t dest[4], const uint8_t station[4], const uint8_t mask[4])
True if
destis in the same subnet asstationgivenmask. A zero mask is treated as “no policy” and returns false rather than the technically-true “every IP matches” — callers using this for routing decisions don’t want an unconfigured interface to claim every destination.
-
int axl_net_list_interfaces(AxlNetInterface *out, size_t *count)
List available network interfaces.
Fills out with up to *count interface descriptors. On return, *count is set to the number of entries filled. Call with out=NULL to query the number of interfaces.
- Parameters:
out – output array (NULL to query count)
count – [in/out] capacity / entries filled
- Returns:
AXL_OK on success, AXL_ERR on error.
-
struct AxlNetInterface
- #include <axl-net.h>
Network interface descriptor.
Public Members
-
char name[32]
interface name (“eth0”, “eth1”, …)
-
uint8_t mac[6]
MAC address.
-
bool link_up
true if link is up
-
uint32_t mtu
maximum transmission unit
-
bool has_ipv4
true if IPv4 is configured
-
uint8_t ipv4[4]
IPv4 address (valid if has_ipv4)
-
uint8_t netmask[4]
subnet mask (valid if has_ipv4)
-
uint8_t gateway[4]
default gateway (valid if has_ipv4)
-
char name[32]
AxlNetOpts
Canonical NIC / static-IP / port / listen-IP options bag,
embedded as a sub-struct in a consumer’s own options type and
paired with the axl_config_descs_net group-injection helper
in <axl/axl-config.h>.
Defines
-
AXL_NET_NIC_AUTO
NIC index sentinel meaning “auto-detect first usable NIC”.
axl-net-opts.h:
Canonical option bag and bring-up helper for AXL-consuming tools and services that take the same NIC / local-IP / port options on the command line. Every networked consumer (web servers, REST / IPMI clients, one-shot fetch utilities) was reinventing the same flag-set + sentinel-mapping + bring-up plumbing;
AxlNetOptsfactors it into a sub-struct the consumer embeds in its own options type, andaxl_net_init/axl_net_init_from_optsare the matching one-call DHCP bring-up.Pair with the
axl_config_descs_netgroup-injection helper in<axl/axl-config.h>to also pull the matching CLI / config descriptors into a consumer’s own table without copy-paste.Out of scope by design: setting a static IPv4 onto the NIC (the
ifconfig-equivalent layer). Callaxl_net_set_static_ipdirectly if a tool genuinely needs to mutateIP4Config2policy — that’s stateful system config, not a per-invocation connection option. Source / listen IP selection (local_ipbelow) is the connection-side knob and stays here.IPv4 only for v1. An IPv6 / family-tagged variant is a clean future addition.
Maps to
SIZE_MAXwhen passed through to the bring-up call.axl_config_descs_netusesAXL_NET_NIC_AUTO_STR(the stringified value of this sentinel) as the descriptor default, soaxl_config_newauto-applies the sentinel into the embeddednic_indexfield with no extra wiring from the consumer.
-
AXL_NET_NIC_AUTO_STR
Decimal-string form of
AXL_NET_NIC_AUTO, suitable as anAxlConfigDesc.default_valuefor anAXL_CFG_UINTfield.The string is the unsigned decimal representation of
UINT64_MAX— pre-stringified rather than computed at runtime becauseAxlConfigDesc.default_valueis aconst char *that must be a compile-time constant. Surfaced on the public API so consumers synthesizing their own descriptors keep the “auto” semantics consistent withaxl_config_descs_net.
-
AXL_NET_OPT_CLIENT
Client-side preset: NIC selector + outbound source-IP bind.
-
AXL_NET_OPT_SERVER
Server-side preset: NIC selector + port + listen-IP bind.
Enums
-
enum AxlNetOptKind
Bitmask of which
AxlNetOptsfields a consumer wants descriptors for. Pass toaxl_config_descs_net.AXL_NET_OPT_SOURCE_IPandAXL_NET_OPT_LISTEN_IPboth target the same underlyinglocal_ipfield — they differ only in CLI vocabulary (--source-ipfor clients,--listen-ipfor servers). A consumer normally sets at most one of the two, matching its role; the CLIENT and SERVER presets below pick the conventional name for each.Values:
-
enumerator AXL_NET_OPT_NIC
—nic → nic_index
-
enumerator AXL_NET_OPT_SOURCE_IP
—source-ip → local_ip (client)
-
enumerator AXL_NET_OPT_PORT
—port → port
-
enumerator AXL_NET_OPT_LISTEN_IP
—listen-ip → local_ip (server)
-
enumerator AXL_NET_OPT_NIC
Functions
-
int axl_net_init(uint64_t nic_index, size_t timeout_sec)
Bring up networking via DHCP on a chosen NIC.
Maps
nic_index == AXL_NET_NIC_AUTOtoSIZE_MAXand delegates toaxl_net_bring_upwith a NULL static address (DHCP path). Address read-back is implicit; this helper does not surface the resolved IP — callaxl_net_get_ip_addressseparately if you need it.Static-IP configuration is intentionally out of scope: it mutates
IP4Config2policy, which is the firmware’sifconfiglayer, not a per-tool connection option. Tools that need to install a static IP callaxl_net_set_static_ipdirectly, or defer to UEFI Shellifconfig.- Parameters:
nic_index – AXL_NET_NIC_AUTO or 0-based NIC index
timeout_sec – DHCP wait (0 = 10 s default)
- Returns:
AXL_OK on success, AXL_ERR on driver-load / link / DHCP failure.
-
int axl_net_init_from_opts(const AxlNetOpts *opts, size_t timeout_sec)
Bring up networking from an
AxlNetOptsbag.Thin thunk over
axl_net_initreadingopts->nic_index. Thelocal_ip/portfields are consumer-owned and unused at bring-up — they parameterize subsequent socket operations.- Parameters:
opts – options bag
timeout_sec – DHCP wait (0 = 10 s default)
- Returns:
AXL_OK on success, AXL_ERR on bring-up failure (or if
optsis NULL).
-
struct AxlNetOpts
- #include <axl-net-opts.h>
Canonical network options bag.
Embed as a sub-struct of the consumer’s own options type, then use
axl_config_descs_net(out, cap, kinds, offsetof(MyOpts, net))to emit matching CLI / config descriptors without copy-paste.Field semantics:
nic_index—AXL_NET_NIC_AUTO(the default) means auto-detect the first usable NIC; any other value is a 0-based index into the SNP handle list as returned byaxl_net_list_interfaces. Used at bring-up time to pick which NIC to run DHCP against. Post-bring-up, preferlocal_ipfor routing — you can name the interface you want by its station IP without knowing its handle index.local_ip— IPv4 to bind the local end of the socket to. Empty string means “let the kernel pick” (0.0.0.0). For clients this is the outbound source IP (curl--interface); for servers this is the listen / accept address. One field because both rolesbind(2)the same way — the role is implied by whether the consumer subsequently callsconnect()orlisten(). CLI vocabulary is selected by theAXL_NET_OPT_SOURCE_IP/_LISTEN_IPenum bits below.port—uint16_tso it round-trips through the descriptor’sAXL_CFG_UINTparser without truncation.0means “consumer-defined default” — AXL ships no canonical port, since every consumer’s domain default differs.
Sub-struct (not flat fields) so future option additions don’t collide with the consumer’s own field names.
AxlInetAddress / AxlSocketAddress
Typedefs
-
typedef struct AxlInetAddress AxlInetAddress
axl-inet-address.h:
IPv4 address and socket address types. AxlInetAddress wraps an IP address with parsing, formatting, and comparison. AxlSocketAddress pairs an address with a port number for use with AxlSocket.
AxlInetAddress *addr = axl_inet_address_new_from_string("192.168.1.1"); AxlSocketAddress *sa = axl_socket_address_new(addr, 8080); // sa now owns addr — do not free addr separately axl_socket_address_free(sa);
-
typedef struct AxlSocketAddress AxlSocketAddress
Functions
-
AxlInetAddress *axl_inet_address_new_from_string(const char *str)
Create an address from a dotted-decimal string.
Parses strings like “192.168.1.1”. Each octet must be 0-255.
- Parameters:
str – dotted-decimal IPv4 string (e.g. “10.0.0.1”)
- Returns:
new address, or NULL on invalid input.
-
AxlInetAddress *axl_inet_address_new_from_bytes(const uint8_t *bytes)
Create an address from raw bytes.
- Parameters:
bytes – 4-byte IPv4 address in network order
- Returns:
new address, or NULL on allocation failure.
-
AxlInetAddress *axl_inet_address_new_any(void)
Create the any-address (0.0.0.0).
- Returns:
new address, or NULL on allocation failure.
-
AxlInetAddress *axl_inet_address_new_loopback(void)
Create the loopback address (127.0.0.1).
- Returns:
new address, or NULL on allocation failure.
-
void axl_inet_address_free(AxlInetAddress *addr)
Free an address. NULL-safe.
- Parameters:
addr – address to free
-
const char *axl_inet_address_to_string(AxlInetAddress *addr)
Get the dotted-decimal string representation.
The string is lazily cached — the first call formats it, subsequent calls return the cached buffer. The pointer is valid for the lifetime of
addr.- Parameters:
addr – address
- Returns:
internal string (e.g. “192.168.1.1”), or NULL on error.
-
const uint8_t *axl_inet_address_to_bytes(const AxlInetAddress *addr)
Get the raw 4-byte address.
- Parameters:
addr – address
- Returns:
pointer to internal 4-byte array, valid for lifetime of
addr.
-
bool axl_inet_address_equal(const AxlInetAddress *a, const AxlInetAddress *b)
Check if two addresses are equal.
- Parameters:
a – first address
b – second address
- Returns:
true if both addresses have the same octets.
-
bool axl_inet_address_is_any(const AxlInetAddress *addr)
Check if this is the any-address (0.0.0.0).
- Parameters:
addr – address
- Returns:
true if all octets are zero.
-
bool axl_inet_address_is_loopback(const AxlInetAddress *addr)
Check if this is the loopback address (127.0.0.1).
- Parameters:
addr – address
- Returns:
true if addr is 127.0.0.1.
-
AxlSocketAddress *axl_socket_address_new(AxlInetAddress *addr, uint16_t port)
Create a socket address from an IP address and port.
Takes ownership of
addr— the caller must not free it after this call. Free the socket address with axl_socket_address_free(), which also frees the contained AxlInetAddress.- Parameters:
addr – IP address (ownership transferred)
port – port number
- Returns:
new socket address, or NULL on failure (addr is freed on failure).
-
AxlSocketAddress *axl_socket_address_new_from_string(const char *str, uint16_t default_port)
Create a socket address by parsing “host:port” or “host”.
If no port is present in the string,
default_portis used. The host part must be a dotted-decimal IPv4 address.- Parameters:
str – “192.168.1.1:8080” or “192.168.1.1”
default_port – port used when string has no “:port”
- Returns:
new socket address, or NULL on parse failure.
-
void axl_socket_address_free(AxlSocketAddress *sa)
Free a socket address and its contained AxlInetAddress. NULL-safe.
- Parameters:
sa – socket address to free
-
AxlInetAddress *axl_socket_address_get_address(const AxlSocketAddress *sa)
Get the IP address component.
The returned pointer is borrowed — do not free it. It remains valid for the lifetime of
sa.- Parameters:
sa – socket address
- Returns:
borrowed pointer, valid for lifetime of
sa.
-
uint16_t axl_socket_address_get_port(const AxlSocketAddress *sa)
Get the port number.
- Parameters:
sa – socket address
- Returns:
port number.
-
void axl_socket_address_to_ipv4(const AxlSocketAddress *sa, AxlIPv4Address *out_addr, uint16_t *out_port)
Extract as legacy AxlIPv4Address + port.
Convenience for interop with existing UDP APIs that take AxlIPv4Address and port separately.
- Parameters:
sa – socket address
out_addr – [out] receives IPv4 address
out_port – [out] receives port number
AxlSocket
Typedefs
-
typedef struct AxlLoop AxlLoop
axl-socket.h:
Unified socket abstraction. AxlSocket wraps AxlTcp (stream) and AxlUdp (datagram) behind a single API. Blocking and async (event-loop integrated) operations are both supported.
AxlSocket *sock = axl_socket_new(AXL_SOCKET_STREAM); AxlSocketAddress *remote = axl_socket_address_new( axl_inet_address_new_from_string("192.168.1.1"), 8080); axl_socket_connect(sock, remote); axl_socket_send(sock, "hello", 5, 0); axl_socket_address_free(remote); axl_socket_free(sock);
-
typedef struct AxlSocketAddress AxlSocketAddress
-
typedef bool (*AxlSocketCallback)(AxlSocket *sock, AxlStatus status, void *data)
Callback for async socket operations.
sockis the socket for the completed operation (new socket for accept, or the original socket for send/recv/connect).statusis an AxlStatus value: AXL_OK on success, AXL_ERR on UEFI error, AXL_CANCELLED if the cancellable on the *_async call was signalled before completion. On error,sockmay be NULL (accept) or half-initialized (connect). Always checkstatusbefore usingsock.Return value controls re-arming for ops that support it:
axl_socket_receive_async: true = re-arm with same buffer, false = stopaxl_socket_accept_async: true = keep listening (default), false = stop acceptingaxl_socket_connect_async,axl_socket_send_async: ignored (one-shot ops). Convention:return true;.
Enums
Functions
-
AxlSocket *axl_socket_new(AxlSocketType type)
Create a new socket.
For AXL_SOCKET_STREAM, the socket is unconnected — call axl_socket_connect() or axl_socket_listen() next. For AXL_SOCKET_DATAGRAM, a UDP socket is opened immediately on an ephemeral port.
- Parameters:
type – AXL_SOCKET_STREAM or AXL_SOCKET_DATAGRAM
- Returns:
new socket, or NULL on failure.
-
AxlSocket *axl_socket_new_from_tcp(AxlTcp *tcp)
Wrap an existing AxlTcp as a stream socket.
Takes ownership of
tcp— the caller must not close it. Useful for wrapping accepted connections from the raw TCP API.- Parameters:
tcp – connected TCP socket (ownership transferred)
- Returns:
new socket, or NULL on failure (tcp is closed on failure).
-
void axl_socket_free(AxlSocket *sock)
Close and free a socket. NULL-safe.
- Parameters:
sock – socket to free
-
int axl_socket_bind(AxlSocket *sock, uint16_t port)
Bind a datagram socket to a specific local port.
Closes the current ephemeral UDP socket and reopens on
port. Pass 0 to rebind to a new ephemeral port.- Parameters:
sock – datagram socket
port – local port (0 = ephemeral)
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_socket_connect(AxlSocket *sock, AxlSocketAddress *addr)
Connect a stream socket to a remote address. Blocking.
- Parameters:
sock – stream socket (unconnected)
addr – remote address (borrowed, not consumed)
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_socket_listen(AxlSocket *sock, uint16_t port)
Start listening on a stream socket.
- Parameters:
sock – stream socket
port – port to listen on
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_socket_accept(AxlSocket *sock, AxlSocket **out_client, size_t timeout_ms)
Accept a connection on a listening stream socket. Blocking.
Unlike axl_socket_send/_receive where timeout_ms=0 defaults to 10s, a sync accept’s semantic role is “wait for an incoming
client,” so timeout_ms=0 means wait forever (no timeout source). Pass a positive value to bound the wait; Ctrl-C ends the wait either way via the loop’s break observation.
- Parameters:
sock – listening stream socket
out_client – [out] receives accepted client socket
timeout_ms – timeout in ms (0 = wait forever)
- Returns:
AXL_OK on success, AXL_ERR on failure. timeout, or cancel.
-
int axl_socket_send(AxlSocket *sock, const void *data, size_t size, size_t timeout_ms)
Send data on a connected stream socket. Blocking.
- Parameters:
sock – connected stream socket
data – buffer to send
size – number of bytes
timeout_ms – timeout in ms (0 = default 10s)
- Returns:
AXL_OK on success, AXL_ERR on failure or timeout.
-
int axl_socket_send_to(AxlSocket *sock, const void *data, size_t size, AxlSocketAddress *dest)
Send a datagram to a specific address. Datagram sockets only.
- Parameters:
sock – datagram socket
data – buffer to send
size – number of bytes
dest – destination address (borrowed)
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_socket_receive(AxlSocket *sock, void *buf, size_t *size, size_t timeout_ms)
Receive data from a connected stream socket. Blocking.
sizeis in/out: on entry the buffer capacity, on return the number of bytes received.- Parameters:
sock – connected stream socket
buf – receive buffer
size – [in/out] buffer capacity / bytes received
timeout_ms – timeout in ms (0 = default 10s)
- Returns:
AXL_OK on success, AXL_ERR on failure or timeout.
-
AxlSocketAddress *axl_socket_get_local_address(AxlSocket *sock)
Get the local address of a connected or listening socket.
- Parameters:
sock – socket
- Returns:
new AxlSocketAddress (caller frees), or NULL.
-
AxlSocketAddress *axl_socket_get_remote_address(AxlSocket *sock)
Get the remote address of a connected stream socket.
- Parameters:
sock – connected stream socket
- Returns:
new AxlSocketAddress (caller frees), or NULL.
-
AxlSocketType axl_socket_get_type(AxlSocket *sock)
Get the socket type.
- Parameters:
sock – socket
- Returns:
AXL_SOCKET_STREAM or AXL_SOCKET_DATAGRAM.
-
int axl_socket_connect_async(AxlSocket *sock, AxlSocketAddress *addr, AxlLoop *loop, AxlSocketCallback cb, void *data)
Async connect — returns immediately, callback fires from loop.
- Parameters:
sock – stream socket (unconnected)
addr – remote address (borrowed)
loop – event loop
cb – callback on completion (return value ignored)
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure.
-
int axl_socket_accept_async(AxlSocket *sock, AxlLoop *loop, AxlSocketCallback cb, void *data)
Async accept — callback fires for each accepted client.
Re-arming is controlled by the callback’s return value:
truekeeps the listener armed,falsestops accepting.- Parameters:
sock – listening stream socket
loop – event loop
cb – callback per accepted client (return bool controls re-arm)
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure.
-
int axl_socket_send_async(AxlSocket *sock, const void *buf, size_t size, AxlLoop *loop, AxlSocketCallback cb, void *data)
Async send — callback fires when send completes.
The buffer must stay valid until the callback fires.
- Parameters:
sock – connected stream socket
buf – send buffer (must remain valid)
size – bytes to send
loop – event loop
cb – callback on completion (return value ignored)
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure.
-
int axl_socket_receive_async(AxlSocket *sock, void *buf, size_t size, AxlLoop *loop, AxlSocketCallback cb, void *data)
Async receive — callback fires when data arrives.
Works for both stream and datagram sockets. For stream sockets, data is received directly into
buf. For datagram sockets, the next datagram is copied intobuf(truncated if larger thansize).The buffer must stay valid across re-arms. Re-arming is controlled by the callback’s return value:
truere-arms with the same buffer,falsestops receiving.- Parameters:
sock – stream or datagram socket
buf – receive buffer (must remain valid across re-arms)
size – buffer size
loop – event loop
cb – callback when data received (return bool controls re-arm)
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure.
-
size_t axl_socket_receive_get_size(AxlSocket *sock)
Get bytes received by the last async receive.
Call from inside the receive callback to get the actual byte count. Works for both stream and datagram sockets.
- Parameters:
sock – socket from receive callback
- Returns:
bytes received, or 0 if no receive has completed.
AxlSocketClient
Typedefs
-
typedef struct AxlSocket AxlSocket
axl-socket-client.h:
High-level socket client. Performs DNS resolution and TCP connection in a single call. Reusable — one client can connect to multiple hosts.
AxlSocketClient *c = axl_socket_client_new(); AxlSocket *sock; if (axl_socket_client_connect_to_host(c, "192.168.1.1", 80, &sock) == 0) { axl_socket_send(sock, "GET / HTTP/1.0\r\n\r\n", 18, 0); axl_socket_free(sock); } axl_socket_client_free(c);
-
typedef struct AxlSocketAddress AxlSocketAddress
-
typedef struct AxlSocketClient AxlSocketClient
Functions
-
AxlSocketClient *axl_socket_client_new(void)
Create a new socket client.
- Returns:
new client, or NULL on allocation failure.
-
void axl_socket_client_free(AxlSocketClient *client)
Free a socket client. NULL-safe.
- Parameters:
client – client to free
-
void axl_socket_client_set_timeout(AxlSocketClient *client, size_t timeout_ms)
Set the connect timeout.
Reserved for future use. Currently the timeout is determined by the underlying TCP stack (typically 10 seconds).
- Parameters:
client – client
timeout_ms – timeout in ms (reserved, not yet applied)
-
int axl_socket_client_connect(AxlSocketClient *client, AxlSocketAddress *addr, AxlSocket **out_sock)
Connect to a resolved address. Blocking.
Creates a new AxlSocket, connects to
addr, and returns it.- Parameters:
client – client
addr – resolved address (borrowed)
out_sock – [out] receives connected socket
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_socket_client_connect_to_host(AxlSocketClient *client, const char *host, uint16_t port, AxlSocket **out_sock)
Resolve a hostname and connect. Blocking.
Performs DNS resolution (or parses a dotted-decimal IP), then creates and connects a stream socket. The resolved address is available via axl_socket_get_remote_address() on the returned socket.
- Parameters:
client – client
host – hostname or dotted-decimal IP
port – port number
out_sock – [out] receives connected socket
- Returns:
AXL_OK on success, AXL_ERR on DNS or connect failure.
AxlTcp
Typedefs
-
typedef struct AxlLoop AxlLoop
axl-tcp.h:
TCP socket abstraction. Blocking and async (event-driven) APIs. Async functions integrate with AxlLoop for non-blocking I/O.
-
typedef struct AxlTcp AxlTcp
-
typedef struct AxlCancellable AxlCancellable
-
typedef bool (*AxlTcpCallback)(AxlTcp *sock, AxlStatus status, void *data)
AxlTcpCallback:
Callback for async TCP operations.
sockis the resulting socket. On accept/connect success this is a freshly-connected socket; on recv/send success it is the same socket the op was started on. On connect/accept error or cancel sock is NULL (library closes the partial/listener state internally). On recv/send error or cancel sock is the still-valid connected socket — caller retains ownership and can reuse or close it.statusis an AxlStatus value:AXL_OK on success
AXL_ERR on UEFI error
AXL_CANCELLED if the cancellable passed to the *_async call was signalled before completion
Return value controls re-arming for ops that support it:
axl_tcp_recv_async: true = re-arm with same buffer, false = stopaxl_tcp_accept_async: true = keep listening (default), false = stop acceptingaxl_tcp_connect_async,axl_tcp_send_async: ignored (ops are one-shot by nature — connect fires once, each send owns its buffer). Convention:return true;in these callbacks.
Functions
-
int axl_tcp_connect(const char *host, uint16_t port, AxlTcp **out_sock)
Connect to a remote host via TCP4.
- Parameters:
host – IPv4 address string or hostname (DNS resolved)
port – remote port number
out_sock – receives the connected socket handle
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_tcp_connect_via(const char *host, uint16_t port, const AxlIPv4Address *source_ip, AxlTcp **out_sock)
Connect to a remote host via TCP4, with explicit interface selection.
Like axl_tcp_connect but adds optional pinning to a specific local interface by station IP. When
source_ipis non-NULL and non-zero, the connect uses only the network interface whose IP4 config matches that station address — useful when the host has multiple NICs and the destination is reachable via a specific one (e.g. an in-band BMC USB-NIC at 169.254.1.0/24).Pass
source_ip= NULL or {0,0,0,0} for the auto-pick path:skip interfaces whose station IP is 0.0.0.0
prefer an interface whose subnet contains the destination
fall back to the first valid (non-zero) interface
- Returns:
AXL_OK on success, AXL_ERR on failure (including “no
interface matches @p source_ip” when forced).
-
int axl_tcp_listen(uint16_t port, AxlTcp **out_listener)
Create a TCP4 listener on the given port.
- Parameters:
port – local port to listen on
out_listener – receives the listener handle
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_tcp_listen_via(uint16_t port, const AxlIPv4Address *source_ip, AxlTcp **out_listener)
Create a TCP4 listener pinned to a specific local interface.
Like axl_tcp_listen but takes an optional source IP that selects which network interface to bind on a multi-NIC host. When
source_ipis NULL or all-zeros, falls through to the auto-pick path used by axl_tcp_listen.- Returns:
AXL_OK on success, AXL_ERR on failure (including “no
interface has station IP @p source_ip” when forced).
-
int axl_tcp_accept(AxlTcp *listener, AxlTcp **out_client, size_t timeout_ms)
Accept one pending connection on a listener.
Unlike axl_tcp_send/_recv where timeout_ms=0 defaults to 10s, a sync accept’s semantic role is “wait for an incoming client,” so timeout_ms=0 means wait forever (no timeout source). Pass a positive value to bound the wait; Ctrl-C ends the wait either way via the loop’s break observation.
- Parameters:
listener – listener from axl_tcp_listen
out_client – receives the accepted client socket
timeout_ms – timeout in ms (0 = wait forever)
- Returns:
AXL_OK on success, AXL_ERR on failure. timeout, or cancel.
-
int axl_tcp_send(AxlTcp *sock, const void *data, size_t size, size_t timeout_ms)
Send data over a connected TCP socket.
- Parameters:
sock – connected socket
data – buffer to send
size – number of bytes to send
timeout_ms – timeout in ms (0 = default 10s)
- Returns:
AXL_OK on success, AXL_ERR on failure or timeout.
-
int axl_tcp_recv(AxlTcp *sock, void *buf, size_t *size, size_t timeout_ms)
Receive data from a connected TCP socket.
- Parameters:
sock – connected socket
buf – receive buffer
size – on entry, buffer size; on return, bytes received
timeout_ms – timeout in ms (0 = default 10s)
- Returns:
AXL_OK on success, AXL_ERR on failure or timeout.
-
int axl_tcp_poll(AxlTcp *sock)
Poll a TCP socket to drive its internal state machine.
- Parameters:
sock – socket to poll
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
void axl_tcp_close(AxlTcp *sock)
Close and free a TCP socket (listener or connected).
For a connected socket, initiates a graceful close (FIN exchange). Returns immediately when called from inside a running event loop: the firmware-level teardown (
Configure(NULL)+ service-bindingDestroyChild+ freeing the AxlTcp) is deferred until the firmware signals close-complete (~TIME_WAIT later for an active close), so the caller never blocks on the close path. When called outside a running loop (e.g. from a sync CLI tool, or during shutdown afteraxl_loop_runreturned) it falls back to a bounded synchronous wait (~3 s) and finalizes inline before returning.Ordering: close before freeing the loop. Always call
axl_tcp_closeBEFOREaxl_loop_freeon any loop the socket was registered with. Close has to drop loop sources for the socket, and the async-finalize path posts the close-complete event back to the loop. Freeing the loop first leaves both paths dereferencing freed memory.**
sockoutlives this call.** On the async path the AxlTcp struct lives until the firmware signals close-complete. Callers must not touchsockafteraxl_tcp_closereturns; treat the pointer as freed.- Parameters:
sock – socket to close (NULL-safe)
-
int axl_tcp_get_local_addr(AxlTcp *sock, char *addr, size_t size, uint16_t *out_port)
Query the local address of a connected or listening socket.
- Parameters:
sock – socket
addr – buffer for dotted-decimal address (min 16 bytes)
size – size of addr buffer
out_port – receives local port number
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_tcp_get_remote_addr(AxlTcp *sock, char *addr, size_t size, uint16_t *out_port)
Query the remote address of a connected socket.
- Parameters:
sock – connected socket
addr – buffer for dotted-decimal address (min 16 bytes)
size – size of addr buffer
out_port – receives remote port number
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_tcp_connect_async(const char *host, uint16_t port, AxlLoop *loop, AxlCancellable *cancel, AxlTcpCallback cb, void *data)
Async connect — initiates TCP connection, returns immediately.
The callback fires from the event loop when the connection completes, fails, or is cancelled via
cancel. On success the newly connected socket is passed; on error or cancellation the sock pointer is NULL and the partial socket is closed internally. Status is AXL_OK on success, AXL_ERR on UEFI error, or AXL_CANCELLED ifcancelwas signalled.Cancel is terminal for connect. The partial socket is closed by the library — caller has nothing to free on a cancel callback. Contrast with accept/recv/send below, which leave their socket intact on cancel.
- Parameters:
host – IPv4 address or hostname
port – remote port
loop – event loop to register with
cancel – optional cancel token (NULL = uncancellable)
cb – callback on completion (return value ignored)
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure.
-
int axl_tcp_connect_async_via(const char *host, uint16_t port, const AxlIPv4Address *source_ip, AxlLoop *loop, AxlCancellable *cancel, AxlTcpCallback cb, void *data)
Async sibling of axl_tcp_connect_via.
source_ipsemantics match the synchronous variant.
-
int axl_tcp_accept_async(AxlTcp *listener, AxlLoop *loop, AxlCancellable *cancel, AxlTcpCallback cb, void *data)
Async accept — waits for incoming connection via the event loop.
The callback fires each time a client connects. Re-arming is controlled by the callback’s return value:
truekeeps the listener armed for the next connection,falsestops accepting. Callaxl_tcp_closeon the listener to tear it down entirely.Cancel leaves the listener valid. On cancel, the callback fires with (NULL, AXL_CANCELLED, data) and the listener is no longer armed for accepts, but
axl_tcp_accept_asynccan be called again to resume listening. Close the listener withaxl_tcp_closewhen done.- Parameters:
listener – listener from axl_tcp_listen
loop – event loop
cancel – optional cancel token (NULL = uncancellable)
cb – callback per accepted client
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure.
-
int axl_tcp_recv_async(AxlTcp *sock, void *buf, size_t size, AxlLoop *loop, AxlCancellable *cancel, AxlTcpCallback cb, void *data)
Async receive — waits for data via the event loop.
The callback fires when data arrives in the caller’s buffer. Re-arming is controlled by the callback’s return value:
truere-arms the recv with the same buffer (typical for streaming servers);falsestops receiving (caller may start a fresh recv with a different buffer, or close the socket).Cancel leaves the socket connected. On cancel the callback fires with (sock, AXL_CANCELLED, data) — the sock is still a valid connected TCP socket, and the caller can start another recv or send, or close it. Contrast with connect, which destroys the sock on cancel.
- Parameters:
sock – connected socket
buf – receive buffer (must stay valid across re-arms)
size – buffer size
loop – event loop
cancel – optional cancel token (NULL = uncancellable)
cb – callback when data received (return bool controls re-arm)
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure.
-
size_t axl_tcp_recv_get_size(AxlTcp *sock)
Get the number of bytes received by the last axl_tcp_recv_async.
Call from inside the recv callback to get the actual byte count.
- Parameters:
sock – socket from recv callback
- Returns:
bytes received, or 0 if no recv has completed.
-
int axl_tcp_send_async(AxlTcp *sock, const void *buf, size_t size, AxlLoop *loop, AxlCancellable *cancel, AxlTcpCallback cb, void *data)
Async send — initiates send, callback on completion.
The buffer must stay valid until the callback fires.
No preemption. Calling this while a previous send is still in flight returns -1. To interrupt an in-flight send, pass an
AxlCancellableto the original call and signal it; the cancel callback fires cleanly withAXL_CANCELLED. Alternatively,axl_tcp_close(sock)tears down every pending op at once.Cancel leaves the socket connected. On cancel the callback fires with (sock, AXL_CANCELLED, data) — the sock is still a valid connected TCP socket. Partial bytes may have been transmitted before cancel took effect; the caller should treat the send outcome as unknown and resync at the application level if needed.
- Parameters:
sock – connected socket
buf – send buffer (must stay valid until callback)
size – bytes to send
loop – event loop
cancel – optional cancel token (NULL = uncancellable)
cb – callback when send completes (return value ignored)
data – opaque context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure or if a send is already in flight.
AxlUdp
Typedefs
-
typedef struct AxlCancellable AxlCancellable
axl-udp.h:
UDP datagram sockets. Fire-and-forget send, request-response send-receive, and async loop-integrated receive.
AxlUdp *sock; if (axl_udp_open(&sock, 0) == 0) { AxlIPv4Address dest; axl_ipv4_parse("192.168.1.100", &dest); axl_udp_send(sock, &dest, 514, msg, msg_len); axl_udp_close(sock); }
-
typedef bool (*AxlUdpSendCallback)(AxlUdp *sock, AxlStatus status, void *data)
AxlUdpSendCallback:
Callback for axl_udp_send_async. Mirrors AxlTcpCallback’s shape — send is one-shot, so the return value is ignored (convention:
return true).statusis an AxlStatus value:AXL_OK on a successful Transmit
AXL_ERR on a UEFI-reported send error
AXL_CANCELLED if the cancellable was signalled before completion
-
typedef bool (*AxlUdpCallback)(AxlUdp *sock, AxlStatus status, const void *data, size_t len, const AxlIPv4Address *from, uint16_t from_port, void *user_data)
AxlUdpCallback:
Callback for async UDP receive. Mirrors AxlTcpCallback shape: the consumer gets per-event
status, can stop in-place by returning false, and the op honors an optional AxlCancellable.statusis an AxlStatus value:AXL_OK on a delivered datagram (
data/len/fromdescribe the payload)AXL_ERR on a UEFI-reported recv error (token Status non-zero or RxData NULL);
datais NULL,lenis 0AXL_CANCELLED if the cancellable passed to axl_udp_recv_async was signalled before the next datagram arrived;
dataNULL,len0
Return value controls re-arming:
true (and
status== AXL_OK): re-arm Receive for the next datagram. Sock must remain valid until the callback returns.false: stop receiving. The library cancels the underlying UEFI Receive op and drops loop sources. The socket is NOT closed — caller may start a fresh recv or call axl_udp_close.
AXL_ERR / AXL_CANCELLED status: never re-arm regardless of return value (the UEFI op is already torn down on those paths).
Functions
-
int axl_udp_open(AxlUdp **sock, uint16_t local_port)
Open a UDP socket bound to a local port.
Uses the NIC’s DHCP-assigned or static IP address. Pass 0 for local_port to use an ephemeral port.
- Parameters:
sock – [out] receives socket handle
local_port – local port (0 = ephemeral)
- Returns:
AXL_OK on success, AXL_ERR if UDP4 stack is not available.
-
int axl_udp_open_via(AxlUdp **sock, uint16_t local_port, const AxlIPv4Address *source_ip)
Open a UDP socket with explicit source-NIC selection.
Like axl_udp_open but adds optional pinning to a specific local interface by station IP. When
source_ipis non-NULL and non-zero, walks the UDP4 service-binding handles and picks the one whose IP4Config2 station address matches — useful when the host has multiple NICs (e.g. an in-band BMC USB-NIC at 169.254.1.0/24 alongside a regular ethernet) and the consumer needs to control which interface emits the datagram.Pass
source_ip= NULL or {0,0,0,0} for the auto-pick path (same behavior as axl_udp_open). Mirrors the axl_tcp_listen_via shape.- Parameters:
sock – [out] receives socket handle
local_port – local port (0 = ephemeral)
source_ip – NULL or zeros = auto-pick
- Returns:
AXL_OK on success, AXL_ERR on failure (including “no
interface has station IP @p source_ip” when forced).
-
void axl_udp_close(AxlUdp *sock)
Close a UDP socket and release resources. NULL-safe.
- Parameters:
sock – socket to close
-
int axl_udp_get_local_addr(AxlUdp *sock, char *addr, size_t size, uint16_t *out_port)
Query the local address of an open UDP socket.
Useful in two cases:
After axl_udp_open with
local_port= 0 (ephemeral), to read back the kernel-assigned port so it can be advertised to a peer.For diagnostics — confirming the bound interface IP after DHCP / static configuration.
addris filled with the dotted-decimal station address (min 16 bytes);out_portreceives the bound local port.- Parameters:
sock – open socket
addr – buffer for dotted-decimal address (min 16 bytes)
size – size of addr buffer
out_port – receives bound local port number
- Returns:
AXL_OK on success, AXL_ERR if the socket is unconfigured or the underlying GetModeData call fails.
-
int axl_udp_send_async(AxlUdp *sock, const AxlIPv4Address *dest, uint16_t port, const void *buf, size_t len, AxlLoop *loop, AxlCancellable *cancel, AxlUdpSendCallback cb, void *data)
Async send — initiates Transmit, callback on completion.
Mirrors axl_tcp_send_async. The buffer at
bufmust stay valid until the callback fires (the UEFI Transmit op references it directly — no intermediate copy).No preemption. Calling this while a previous send on
sockis still in flight returns AXL_ERR. To interrupt an in-flight send, pass an AxlCancellable to the original call and signal it; the cancel callback fires cleanly with AXL_CANCELLED.- Parameters:
sock – socket
dest – destination IPv4 address
port – destination port
buf – send buffer (must stay valid until cb)
len – bytes to send
loop – event loop
cancel – optional cancel token (NULL = uncancellable)
cb – callback when send completes (return ignored)
data – caller context
- Returns:
AXL_OK if initiated, AXL_ERR on immediate failure or if a send is already in flight on this socket.
-
int axl_udp_connect(AxlUdp *sock, const AxlIPv4Address *peer, uint16_t port)
Pin the socket to a single peer (Linux-style
connect()on UDP).Re-configures the underlying UDP4 instance with
RemoteAddress+RemotePortset topeer/port. After this:The kernel filters incoming datagrams to ones from the peer (other senders’ packets are dropped).
Subsequent
axl_udp_send/axl_udp_send_asynccalls accept NULLdest— the configured peer is used. Passing a non-NULLdeststill overrides the peer for that packet.
Idempotent: calling again with a different peer rebinds. Use axl_udp_disconnect to clear the lock without picking a new peer.
- Parameters:
sock – socket
peer – peer address
port – peer port
- Returns:
AXL_OK on success, AXL_ERR on Configure failure.
-
int axl_udp_disconnect(AxlUdp *sock)
Clear a peer lock previously installed by axl_udp_connect.
After this, the socket accepts datagrams from any sender again and
axl_udp_sendrequires an explicitdest.- Parameters:
sock – socket
- Returns:
AXL_OK on success, AXL_ERR on Configure failure.
-
int axl_udp_join_multicast(AxlUdp *sock, const AxlIPv4Address *group)
Join an IPv4 multicast group on this socket.
After joining, the socket receives datagrams sent to
group(in addition to its station unicast address). Useful for service-discovery protocols (mDNS at 224.0.0.251, SSDP at 239.255.255.250) and other multicast traffic.Idempotent only if the underlying UEFI driver is — UEFI 2.x doesn’t define behavior for joining the same group twice.
- Parameters:
sock – socket
group – multicast group address (224.0.0.0/4)
- Returns:
AXL_OK on success, AXL_ERR if
groupis not a valid 224.0.0.0/4 multicast address or the UEFI Groups() call fails.
-
int axl_udp_leave_multicast(AxlUdp *sock, const AxlIPv4Address *group)
Leave a previously-joined multicast group.
Pass
group= NULL to leave ALL groups this socket has joined (matches UEFI 2.x UDP4.Groups() semantics forJoinFlag=FALSEwithMulticastAddress = NULL).- Parameters:
sock – socket
group – group to leave, or NULL for all
- Returns:
AXL_OK on success, AXL_ERR if the UEFI Groups() call fails.
-
int axl_udp_set_broadcast(AxlUdp *sock, bool enable)
Enable or disable reception of broadcast datagrams.
Re-Configures the underlying UDP4 instance with
AcceptBroadcast = enable. Default for a freshly-opened socket is FALSE (broadcasts dropped). Sending broadcasts (e.g. to 255.255.255.255 or a subnet broadcast) does not require this — it gates the recv-side filter only.- Parameters:
sock – socket
enable – true to accept inbound broadcasts
- Returns:
AXL_OK on success, AXL_ERR on Configure failure.
-
int axl_udp_send(AxlUdp *sock, const AxlIPv4Address *dest, uint16_t port, const void *data, size_t len)
Send a UDP datagram. Blocking with 2-second timeout.
Fire-and-forget — no response expected.
destmay be NULL only if the socket has been pinned to a peer via axl_udp_connect (the configured peer is used). Otherwisedestis required.- Parameters:
sock – socket
dest – destination IPv4 address (NULL = use connected peer)
port – destination port (ignored if dest is NULL)
data – payload
len – payload length
- Returns:
AXL_OK on success, AXL_ERR on error or timeout.
-
int axl_udp_sendrecv(AxlUdp *sock, const AxlIPv4Address *dest, uint16_t port, const void *tx_data, size_t tx_len, void *rx_buf, size_t rx_size, size_t *rx_len, size_t timeout_ms)
Send a datagram and wait for a response.
Sends tx_data, then polls for an incoming datagram up to timeout_ms milliseconds. Useful for DNS queries, NTP, etc.
- Parameters:
sock – socket
dest – destination IPv4 address
port – destination port
tx_data – request payload
tx_len – request length
rx_buf – [out] response buffer
rx_size – response buffer capacity
rx_len – [out] bytes received
timeout_ms – receive timeout in ms
- Returns:
AXL_OK on success, AXL_ERR on error or timeout.
-
int axl_udp_recv_async(AxlUdp *sock, AxlLoop *loop, AxlCancellable *cancel, AxlUdpCallback cb, void *data)
Async receive — fires
cbfor each incoming datagram via the event loop, until the callback returns false, the cancellable is signalled, or the socket is closed.Mirrors axl_tcp_recv_async: takes an optional AxlCancellable, the callback receives per-event status, and the callback’s bool return controls re-arming.
Replaces the pre-parity-sweep
axl_udp_recv_start/axl_udp_recv_stoppair: returning false from the callback now stops in-place (no separate stop call needed).- Parameters:
sock – socket
loop – event loop
cancel – optional cancel token (NULL = uncancellable)
cb – receive callback
data – user data for callback
- Returns:
AXL_OK on success, AXL_ERR on error.
AxlUrl
Functions
-
int axl_url_parse(const char *url, AxlUrl **out_parsed)
Parse a URL string into components.
Supports the RFC 3986 generic URI shape:
scheme://[user[:password]@]host[:port][/path][?query][#fragment]Userinfo is recognized only when an
@appears in the authority (between://and the first/,?, or#); an@later in the path or query is left alone. Percent-decoding is NOT applied to any field — the parser returns raw bytes for round-trip fidelity.- Parameters:
url – URL string (e.g. “http://user:pass@host:8080/path?q=1#frag”)
out_parsed – receives allocated AxlUrl; free with axl_url_free()
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
void axl_url_free(AxlUrl *url)
Free an AxlUrl returned by axl_url_parse.
- Parameters:
url – URL to free (NULL-safe)
-
char *axl_url_build(const char *scheme, const char *host, uint16_t port, const char *path)
Build a URL string from components.
- Parameters:
scheme – “http” or “https”
host – hostname or IP
port – port number (0 = use default for scheme)
path – path starting with “/” (NULL = “/”)
- Returns:
allocated URL string, or NULL on failure. Caller frees.
-
int axl_url_encode(const char *src, char *out, size_t size)
Percent-encode a string for use in a URL.
Encodes all characters except unreserved characters (A-Z, a-z, 0-9, ‘-’, ‘.’, ‘_’, ‘~’) and optionally ‘/’ (preserved by default for path encoding). Each encoded byte becomes XX.
- Parameters:
src – input string (UTF-8)
out – output buffer
size – output buffer size
- Returns:
number of bytes written (excluding NUL), or -1 on error or truncation.
-
int axl_url_decode(const char *src, char *out, size_t size)
Decode a percent-encoded URL string.
Replaces XX sequences with the corresponding byte. Passes through characters that are not percent-encoded.
- Parameters:
src – percent-encoded string
out – output buffer
size – output buffer size
- Returns:
number of bytes written (excluding NUL), or -1 on error or truncation.
-
struct AxlUrl
- #include <axl-url.h>
axl-url.h:
URL parser and builder. Parsed URL components per RFC 3986 (subset). Heap-allocated by axl_url_parse; freed by axl_url_free. NULL string fields mean the component wasn’t present in the input — distinguish from “present but empty” via empty-string check (e.g.
user:@host→ password is"", not NULL).Public Members
-
char *scheme
URL scheme without
://(“http”, “https”, …)
-
char *user
userinfo user portion before
:(NULL if nouser[:pass]@in authority)
-
char *password
userinfo password portion after
:(NULL if no:in userinfo; “” ifuser:@host)
-
char *host
hostname or IP literal (no userinfo, no port)
-
char *path
path component starting with
/(“/” if absent)
-
char *query
raw query string after
?(NULL if no?)
-
char *fragment
fragment after
#(NULL if no#)
-
uint16_t port
port number (default for scheme if not specified)
-
char *scheme
AxlHttpCore
Low-level HTTP/1.1 parsing helpers shared by the server, the client, and any consumer code (proxies, middleware, custom transports).
Functions
-
size_t axl_http_find_header_end(const char *buf, size_t len)
Find the end of HTTP headers (CRLF CRLF) in a buffer.
axl-http-core.h:
Low-level HTTP/1.1 parsing helpers shared by axl-http-server, axl-http-client, and consumer code (proxies, middleware, custom transports). All functions operate on raw byte buffers and use AxlHashTable for header storage.
Higher-level header utilities like axl_http_parse_range live in axl-http-server.h.
Scans
buffor the “\r\n\r\n” sequence that terminates the request/response header block.- Parameters:
buf – raw data buffer
len – buffer length in bytes
- Returns:
offset of the byte just past the “\r\n\r\n”, or 0 if not found (also 0 if
lenis less than 4).
-
int axl_http_parse_request_line(const char *line, size_t line_len, char **method, char **path, char **query)
Parse an HTTP request line: “METHOD PATH?QUERY HTTP/1.x”.
On success, allocates and returns the method, path, and (optional) query string. The caller frees each non-NULL output with axl_free. On error, all outputs are set to NULL.
- Parameters:
line – start of the request line
line_len – length up to (but not including) CRLF
method – receives method string (allocated; caller frees)
path – receives path string (allocated; caller frees)
query – receives query string (allocated; NULL if none)
- Returns:
AXL_OK on success, AXL_ERR if the line is malformed or allocation fails.
-
int axl_http_parse_status_line(const char *line, size_t line_len, size_t *status_code)
Parse an HTTP status line: “HTTP/1.x NNN Reason”.
- Parameters:
line – start of the status line
line_len – length up to (but not including) CRLF
status_code – receives the numeric status code (e.g. 200)
- Returns:
AXL_OK on success, AXL_ERR if the line is malformed.
-
int axl_http_parse_headers(const char *data, size_t data_len, AxlHashTable **headers)
Parse HTTP headers from raw bytes into a hash table.
Reads header lines starting at
datauntil the empty line that separates headers from the body. Header names are stored in lowercase to enable case-insensitive lookups. The returned table owns its keys and values; the caller frees it with axl_hash_table_free.- Parameters:
data – start of header block (after request/status line + CRLF)
data_len – length of data
headers – receives populated hash table (caller frees)
- Returns:
AXL_OK on success, AXL_ERR on error.(table is left as NULL).
-
size_t axl_http_get_content_length(AxlHashTable *headers)
Read the Content-Length value from a parsed header table.
Looks up the lowercase “content-length” key. Stops parsing at the first non-digit character.
- Parameters:
headers – parsed headers (from axl_http_parse_headers)
- Returns:
Content-Length value, or 0 if the header is absent or
headersis NULL.
AxlHttpServer
Defines
-
AXL_WS_CONNECT
-
AXL_WS_TEXT
-
AXL_WS_BINARY
-
AXL_WS_DISCONNECT
-
AXL_ROUTE_NO_AUTH
-
AXL_ROUTE_AUTH
-
AXL_ROUTE_ADMIN
-
AXL_CACHE_FOREVER
Typedefs
-
typedef struct AxlLoop AxlLoop
axl-http-server.h:
HTTP server with routing, middleware pipeline, WebSocket, authentication, response caching, and upload streaming.
-
typedef int (*AxlHttpHandler)(AxlHttpRequest *req, AxlHttpResponse *resp, void *data)
Route handler callback.
- Return:
AXL_OK on success, AXL_ERR on failure.
-
typedef int (*AxlHttpMiddleware)(AxlHttpRequest *req, AxlHttpResponse *resp, void *data)
Middleware callback. Return 0 to continue pipeline, -1 to short-circuit.
- Return:
AXL_OK to continue, AXL_ERR to abort.
-
typedef struct AxlHttpServer AxlHttpServer
-
typedef int (*AxlResponseStreamer)(void *ctx, void *out_buf, size_t out_buf_size, size_t *out_size)
Producer callback for streaming response bodies.
Called repeatedly by the dispatcher to fill outgoing chunks. Implementations read the next chunk from their backing source (file, generated content, network passthrough) into
out_bufand report the byte count viaout_size. ReturningAXL_OKwith*out_size== 0 signals end-of-stream.Re-entrancy: same constraints as
AxlUploadHandler— runs on the loop’s normal dispatch level (TPL_APPLICATION foreground, TPL_CALLBACK driver). Don’t block, don’t allocate gratuitously, keep each invocation short — the dispatcher is single-threaded and a slow streamer stalls other connections.- Param ctx:
opaque user data from axl_http_response_set_streamer
- Param out_buf:
caller-supplied buffer to fill
- Param out_buf_size:
capacity of
out_bufin bytes- Param out_size:
[out] bytes written into
out_bufthis call; 0 = EOF- Return:
AXL_OKto continue (including the EOF case with*out_size== 0);AXL_ERRto abort the response (the dispatcher resets the connection and invokes the cleanup hook).
-
typedef void (*AxlResponseCleanup)(void *ctx)
Optional finalizer called when a streaming response ends.
Fires exactly once for any response that successfully installs a streamer via axl_http_response_set_streamer, regardless of how the response ended (EOF, streamer error, connection reset before EOF). Use it to close files, free buffers, or release any resource
ctxholds onto. Pass NULL if the streamer manages its own lifecycle.- Param ctx:
the same
ctxpointer registered with the streamer.
-
typedef int (*AxlWsHandler)(size_t event, const void *frame, size_t frame_size, void *data)
WebSocket event callback.
- Return:
AXL_OK on success, AXL_ERR on failure.
-
typedef int (*AxlAuthCallback)(AxlHttpRequest *req, AxlAuthInfo *auth_out, void *data)
Authentication callback.
- Return:
AXL_OK on success (authenticated), AXL_ERR on failure.
-
typedef int (*AxlUploadHandler)(AxlHttpRequest *req, AxlHttpResponse *resp, const void *chunk, size_t chunk_size, void *data, bool aborted)
Upload streaming callback, called per chunk as body data arrives.
Called repeatedly with chunks up to the configured upload.chunk.size. Three terminating call shapes — the handler must distinguish them:
chunk != NULL, aborted == false: a body chunk arrived. Process it and return AXL_OK to continue, AXL_ERR to abort and send 500.
chunk == NULL, chunk_size == 0, aborted == false: clean EOF. Set
respfields here; the response is sent after return.chunk == NULL, chunk_size == 0, aborted == true: the connection was torn down mid-upload (TCP disconnect, recv error, server shutdown).
respis NOT transmitted. The handler MUST NOT touch the connection, send a response, or call any response setter (axl_http_response_set_*) — the call exists only to release per-request state (open file handles, accumulators, allocations) the handler accumulated across earlier chunk calls. Without this signal, that state leaks into the next request on the same handler globals — caused cross-request data corruption in axl-webfs’s PUT path.
Fires exactly once per upload: clean-EOF and abort calls are mutually exclusive — a handler that already received the clean-EOF call will NOT also receive an abort, even if the response send subsequently fails. Return value is ignored on the abort call.
- Return:
AXL_OK on success, AXL_ERR to abort the upload and send 500.
Functions
-
AxlHttpServer *axl_http_server_new(uint16_t port)
Create a new HTTP server bound to the given port.
- Parameters:
port – TCP port to listen on
- Returns:
server instance, or NULL on failure.
-
void axl_http_server_free(AxlHttpServer *server)
Free an HTTP server and all resources.
- Parameters:
server – server to free (NULL-safe)
-
int axl_http_server_set(AxlHttpServer *s, const char *key, const char *value)
Set a server option by key.
Supported keys: “max.connections”, “body.limit”, “keep.alive.sec”.
- Parameters:
s – server
key – option key
value – option value (string)
- Returns:
AXL_OK on success, AXL_ERR on unknown key or invalid value.
-
const char *axl_http_server_get(AxlHttpServer *s, const char *key)
Get a server option value as string.
- Parameters:
s – server
key – option key
- Returns:
option value, or NULL for unknown keys.
-
int axl_http_server_set_max_connections(AxlHttpServer *s, size_t max)
Set maximum simultaneous connections.
- Parameters:
s – server
max – maximum simultaneous connections (default 8)
- Returns:
AXL_OK on success, AXL_ERR if
sis NULL or the underlying setter rejected the value.
-
int axl_http_server_set_body_limit(AxlHttpServer *s, size_t max_bytes)
Set maximum request body size.
- Parameters:
s – server
max_bytes – maximum request body size in bytes (default 4 MB)
- Returns:
AXL_OK on success, AXL_ERR on error.
-
int axl_http_server_set_keep_alive(AxlHttpServer *s, size_t timeout_sec)
Set keep-alive timeout.
- Parameters:
s – server
timeout_sec – keep-alive timeout in seconds (default 30)
- Returns:
AXL_OK on success, AXL_ERR on error.
-
int axl_http_server_use(AxlHttpServer *s, AxlHttpMiddleware mw, void *data)
Register middleware executed in registration order. Return 0 from mw to continue pipeline, -1 to short-circuit.
- Parameters:
s – server
mw – middleware function
data – context passed to mw
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_add_route(AxlHttpServer *s, const char *method, const char *path, AxlHttpHandler handler, void *data)
Register a route handler.
- Parameters:
s – server
method – HTTP method (“GET”, “POST”, etc.) or NULL for any
path – path pattern; trailing slash-star matches prefix
handler – handler function
data – context passed to handler
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_add_routes(AxlHttpServer *s, ...)
Register multiple route handlers in one call.
Variadic batch form of axl_http_server_add_route. Each route is a four-arg group
(method, path, handler, data)repeated until a sentinelNULLmethod terminates the list. Stops on the first registration failure and returnsAXL_ERR— earlier successfully- registered routes stay installed (the server’s route table is append-only and the failure is most likely “table full,” which the caller can surface to the user).axl_http_server_add_routes(server, "GET", "/", handle_root, NULL, "GET", "/x", handle_x, o, "PUT", "/y", handle_y, o, "DELETE", "/z", handle_z, o, NULL); // sentinel — requiredReplaces the equivalent five separate
axl_http_server_add_routecalls and the per-call error-check chain. Route precedence is the same as for repeated single-route calls — exact path before prefix, method-specific before method-wildcard — independent of the order routes are registered.- Parameters:
s – server
- Param :
(method, path, handler, data) groups, terminated by NULL method
- Returns:
AXL_OK if every route registered; AXL_ERR on the first failure (with that route and all later groups in the list NOT registered).
-
int axl_http_server_add_static(AxlHttpServer *s, const char *prefix, const char *fs_path)
Serve static files from a filesystem path.
- Parameters:
s – server
prefix – URL prefix (e.g. “/”)
fs_path – filesystem path (UTF-8)
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_start(AxlHttpServer *s, AxlLoop *loop)
Bring the server up on a caller-owned event loop.
Allocates the per-connection pool sized from the server’s
max.connectionsconfig, opens the TCP listener (pinned tolisten.ipif set, else auto-pick), and registers the async accept onloopso each incoming connection re-arms automatically. The server is fully wired and listening when this returns; the caller drivesloopviaaxl_loop_run(foreground) oraxl_loop_attach_driver(DXE driver mode).axl_http_server_runis the convenience wrapper that creates its own loop, calls this, and runs the loop to completion.- Parameters:
s – server
loop – event loop from axl_loop_new
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_run(AxlHttpServer *s)
Run the server standalone — creates a loop, calls
axl_http_server_start, and blocks inaxl_loop_rununtilaxl_loop_quit.- Parameters:
s – server
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
bool axl_http_request_accepts(const AxlHttpRequest *req, const char *mime)
Does the request’s
Acceptheader advertise interest inmime?Routes from
req->headers[“accept”] into axl_http_accepts — the same matcher used elsewhere in the HTTP machinery: case-insensitive, handles multi-type lists, recognizes the catch-all wildcard, and tolerates;q= parameters (matches regardless of q-value, so an explicitq=0reject is still treated as “accepts” — a future tightening if a consumer needs strict negotiation). Missing Accept header returns false.- Parameters:
req – incoming request
mime – MIME type to look for (“application/json”)
- Returns:
true if
reqwould accept a response of MIME typemime.
-
bool axl_http_request_wants_json(const AxlHttpRequest *req)
Convenience: does the request want JSON?
Equivalent to axl_http_request_accepts (req, “application/json”). Common-enough pattern in REST handlers that it earns its own name —
if (axl_http_request_wants_json(req)) { ... }reads at the right level. Used when a single endpoint serves both HTML and JSON representations and picks based on the client’s Accept header.- Parameters:
req – incoming request
-
bool axl_http_request_get_json(const AxlHttpRequest *req, AxlJsonReader *out)
Parse the request body as JSON.
Calls axl_json_parse on
req->body/req->body_size. The reader references the body buffer directly — do not freereq->bodywhile the reader is in use, and callaxl_json_freeonoutwhen done.Strict RFC 8259. For JSON5, parse manually with axl_json_parse_flags.
- Parameters:
req – incoming request
out – [out] reader to fill (caller owns; free with axl_json_free)
- Returns:
true on success (
outpopulated and ready foraxl_json_object_get/ etc); false on NULL inputs, empty body, or JSON parse error.
-
void axl_http_response_set_json(AxlHttpResponse *r, const char *json)
Set JSON body and “application/json” content type (default 200).
- Parameters:
r – response
json – JSON string
-
void axl_http_response_set_text(AxlHttpResponse *r, const char *text)
Set plain text body and “text/plain” content type (default 200).
- Parameters:
r – response
text – plain text string
-
void axl_http_response_set_status(AxlHttpResponse *r, size_t code)
Set or override the HTTP status code.
- Parameters:
r – response
code – HTTP status code
-
void axl_http_response_set_file(AxlHttpResponse *r, const char *path)
Set response body from a file, inferring content type from extension.
- Parameters:
r – response
path – filesystem path (UTF-8)
-
void axl_http_response_set_static(AxlHttpResponse *r, const void *body, size_t size, const char *content_type)
Set response body to a borrowed static buffer that the SDK must NOT free.
Use this for embedded read-only assets —
.rodataC string literals, static const arrays of HTML / JS / CSS, immutable binary blobs xxd’d into the binary. Sets AxlHttpResponse.body to body, body_size to size, marks AxlHttpResponse.body_static = true so the dispatch loop skips its post-sendaxl_free. Passing such a pointer to the axl_http_response_set_text /_jsonfamily would force a copy (waste); assigning it to AxlHttpResponse.body directly causes heap corruption (the dispatch loop would treat the literal as anaxl_malloc’d buffer and free it).content_type is borrowed (typically a string literal). NULL leaves the existing content-type unchanged.
If a previous body was set via the copy-based helpers, this function frees it before installing the static buffer.
- Parameters:
r – response
body – pointer to read-only / static buffer
size – size of body in bytes
content_type – MIME type (borrowed); NULL = leave as-is
-
void axl_http_response_set_streamer(AxlHttpResponse *r, AxlResponseStreamer streamer, void *ctx, AxlResponseCleanup cleanup, size_t total_size, const char *content_type)
Set a streaming response body via producer callback.
Replaces the contiguous-body model for large or unbounded responses. The dispatcher allocates a chunk-sized tx buffer, sends headers, then calls
streamerrepeatedly to fill the buffer andaxl_tcp_send_async’s each filled chunk (chained completions). EOF (returned via*out_size = 0) terminates the response.Sets Content-Length from
total_sizewhen known (the typical file-serve case). Pass(size_t)-1to signal “unknown length” and emit Transfer-Encoding: chunked instead — each chunk goes on the wire framed as<hex-size>\r\n<data>\r\n, terminated by a0\r\n\r\nfinal chunk.ctxis opaque to the SDK; the dispatcher passes it back unchanged on each streamer invocation.cleanup(NULL-able) fires exactly once when the response ends — successful EOF, streamer error, OR connection reset. Use it to close the filectxwraps, free buffers, etc.Mutually exclusive with
body/body_static/ axl_http_response_set_text /_json/_file. Setting a streamer overrides any prior body assignment (and frees a previously-set non-static body).static int file_streamer(void *ctx, void *buf, size_t cap, size_t *out) { AxlFile *f = (AxlFile *)ctx; return axl_fread(f, buf, cap, out); } static void file_close(void *ctx) { axl_fclose((AxlFile *)ctx); } AxlFile *f = axl_fopen("fs0:/big.iso", "r"); uint64_t size = 0; axl_file_size(f, &size); axl_http_response_set_streamer(resp, file_streamer, f, file_close, (size_t)size, "application/octet-stream");
- Parameters:
r – response
streamer – producer callback (must be non-NULL)
ctx – opaque user data passed back to streamer / cleanup
cleanup – finalizer (NULL = streamer self-cleans)
total_size – Content-Length, or (size_t)-1 for chunked
content_type – MIME type (borrowed; NULL = leave as-is)
-
void axl_http_response_set_range(AxlHttpResponse *r, const void *data, size_t offset, size_t length, size_t total_size)
Set a byte-range response (HTTP 206) with Content-Range header.
Copies
lengthbytes starting at(uint8_t *)data + offsetinto a freshly-allocated body, setsstatus_code = 206, setscontent_type = "application/octet-stream", and emits aContent-Range: bytes <offset>-<offset+length-1>/<total_size>header per RFC 9110 §15.3.7. Allocatesr->headersif not already present.- Parameters:
r – response
data – full data buffer
offset – byte offset into data
length – number of bytes to send
total_size – total size of the resource
-
void axl_http_response_set_content_range(AxlHttpResponse *r, uint64_t start, uint64_t end, uint64_t total)
Set the
Content-Rangeheader on a 206 response.Use when sending a partial-content response via axl_http_response_set_streamer or any other path that doesn’t go through axl_http_response_set_range (which sets the header automatically). Callers must set
status_code = 206separately — this helper only formats and inserts the header.Allocates
r->headersif not already present, usingaxl_hash_table_new_fullwithaxl_free_impldestructors for BOTH keys and values. If the consumer pre-allocates themselves, it MUST be created with the same destroy-func contract (e.g. via). Mixing in aaxl_hash_table_new_full(
axl_str_hash, axl_str_equal, axl_free_impl, axl_free_impl)
axl_hash_table_new_str()-shaped table would leak both the strdup’d key (str-table double-strdups) and value (str-table doesn’t own values). Other axl-http-server callers (e.g. WebSocket upgrade handler) build their own headers tables — they don’t compose with this helper today, but new consumers should follow the full-destroy-funcs convention.Format per RFC 9110 §15.3.7:
bytes <start>-<end>/<total>, end inclusive (start <= end < total).- Parameters:
r – response
start – first byte index of the slice
end – last byte index of the slice (inclusive)
total – total size of the resource
-
bool axl_http_parse_range(const char *range_header, uint64_t file_size, AxlHttpRange *out)
Parse an HTTP Range request header.
Supports a single “bytes=START-END” range (not multi-range). Handles “bytes=START-”, “bytes=-SUFFIX”, and “bytes=START-END”. Clamps end to file_size - 1. Sets out->valid on success.
- Parameters:
range_header – Range header value (e.g. “bytes=0-499”)
file_size – total file size
out – receives the parsed range
- Returns:
true if a valid range was parsed, false otherwise.
-
bool axl_http_accepts(const char *accept_header, const char *media_type)
Check if an HTTP Accept header includes a media type.
Searches the comma-separated Accept header value for
media_type(e.g. “application/json”, “text/html”). Matching is case-insensitive and ignores quality parameters. Also matches wildcard types (wildcard accepts everything).- Parameters:
accept_header – Accept header value (may be NULL)
media_type – media type to check (e.g. “application/json”)
- Returns:
true if
media_typeis acceptable.
-
int axl_http_server_use_tls(AxlHttpServer *s, const void *cert_der, size_t cert_len, const void *key_der, size_t key_len)
Enable TLS on the server with DER-encoded cert and key.
After this call, all accepted connections use TLS. The cert and key can be generated with axl_tls_generate_self_signed(). Requires AXL_TLS=1 at build time.
- Parameters:
s – server
cert_der – DER-encoded certificate
cert_len – certificate length
key_der – DER-encoded private key
key_len – key length
- Returns:
AXL_OK on success, AXL_ERR if TLS not available or cert/key invalid.
-
int axl_http_server_add_websocket(AxlHttpServer *s, const char *path, AxlWsHandler handler, void *data)
Register a WebSocket endpoint.
- Parameters:
s – server
path – WebSocket endpoint path
handler – WebSocket event handler
data – opaque caller data
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_ws_broadcast(AxlHttpServer *s, const char *path, const void *data, size_t size)
Broadcast data to all connected WebSocket clients on a path.
- Parameters:
s – server
path – WebSocket endpoint path
data – data to broadcast
size – data size in bytes
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_use_auth(AxlHttpServer *s, AxlAuthCallback cb, void *data)
Register an authentication handler for the server.
- Parameters:
s – server
cb – authentication callback
data – opaque caller data
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_add_route_auth(AxlHttpServer *s, const char *method, const char *path, AxlHttpHandler handler, void *data, uint32_t auth_flags)
Register a route handler with authentication requirements.
- Parameters:
s – server
method – HTTP method or NULL for any
path – path pattern
handler – handler function
data – context passed to handler
auth_flags – AXL_ROUTE_* flags
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_use_cache(AxlHttpServer *s, size_t max_entries)
Enable response caching on the server.
- Parameters:
s – server
max_entries – maximum cache entries
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_set_route_ttl(AxlHttpServer *s, const char *path, size_t ttl_ms)
Set cache TTL for a specific route path.
Stores a
path → ttl_msmapping; the next cached response whose request path equalspathexactly uses this TTL instead of the server-wide default fromaxl_http_server_use_cache. Prefix routes (e.g./css/followed by a wildcard) are not matched — set the TTL on the exact sub-paths you expect, or rely on the server default.- Parameters:
s – server
path – exact request path
ttl_ms – time-to-live in milliseconds, or AXL_CACHE_FOREVER
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
void axl_http_server_cache_invalidate(AxlHttpServer *s, const char *prefix)
Invalidate cached responses whose path starts with
prefix.Walks the cache and removes every entry whose path portion begins with
prefix(the leading “METHOD “ token in the internal cache key is skipped). Pass NULL or “” to clear the whole cache.- Parameters:
s – server
prefix – path prefix to invalidate (NULL or “” for all)
-
int axl_http_server_add_upload_route(AxlHttpServer *s, const char *method, const char *path, AxlUploadHandler handler, void *data)
Register a streaming upload route.
- Parameters:
s – server
method – HTTP method
path – path pattern
handler – upload handler
data – opaque caller data
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_server_add_webdav(AxlHttpServer *s, const char *prefix, const AxlWebDavOps *ops, void *user_data)
Mount a WebDAV handler at
prefix.Registers verb routes (OPTIONS, PROPFIND, GET, HEAD, PUT, DELETE, MKCOL, MOVE) under
<prefix>/<wildcard>that driveops. Theopstable is COPIED into the server — caller may free / re-use the struct after this returns.user_datais borrowed and must outlive the server.Up to 4 WebDAV mounts per server. Cleanup is automatic on axl_http_server_free.
- Parameters:
prefix – URL prefix, e.g. “/dav”
ops – callback table (copied)
user_data – opaque, passed back to ops
- Returns:
AXL_OK on success, AXL_ERR on bad arguments or if the server already has 4 mounts.
-
struct AxlHttpRequest
-
struct AxlHttpResponse
Public Members
-
size_t status_code
-
AxlHashTable *headers
-
void *body
Response body bytes. Ownership: the SDK calls axl_free on this pointer after the response is sent, unless body_static is set. Handlers that assign body directly must therefore pass an axl_malloc’d buffer. Assigning a
.rodata/ static const literal here is a heap corruption bug — use axl_http_response_set_static for embedded read-only assets, or one of the copy-based helpers (axl_http_response_set_text/_json/_file) which allocate internally.
-
size_t body_size
-
const char *content_type
Content-Type header value. Borrowed pointer — the SDK does NOT free this. Static string literals are fine; if a caller allocates dynamically, the caller is responsible for the lifetime (must outlive the response send).
-
bool body_static
When true, the SDK will NOT free body after the response is sent. Set by axl_http_response_set_static; ignore otherwise. Default false (zero-init) preserves the “axl_malloc’d, SDK frees” contract for every existing caller — no migration required.
-
int (*streamer)(void *ctx, void *out_buf, size_t out_buf_size, size_t *out_size)
When non-NULL, the dispatcher streams the body by calling streamer repeatedly. See axl_http_response_set_streamer. Set body / body_size / body_static via the setter rather than touching these fields directly.
-
void *streamer_ctx
Opaque user data passed to streamer on each invocation. Owned by the caller; lifetime managed via streamer_cleanup.
-
void (*streamer_cleanup)(void *ctx)
Optional finalizer called once the streaming response either completes (EOF, all bytes sent) OR is aborted (streamer error, connection reset before EOF). Receives streamer_ctx so the caller can close files / free buffers. NULL means the streamer self-cleans via its EOF / error transitions.
-
size_t streamer_total_size
Total response body size in bytes. Used as Content-Length when known. Pass
(size_t)-1to signal unknown length — the dispatcher emits Transfer-Encoding: chunked instead.
-
size_t status_code
-
struct AxlHttpRange
- #include <axl-http-server.h>
Parsed byte range from an HTTP Range request header.
-
struct AxlAuthInfo
-
struct AxlWebDavOps
- #include <axl-http-server.h>
Consumer-supplied filesystem callback table.
Every callback receives
user(the value passed to axl_http_server_add_webdav) and a path RELATIVE to the registered prefix (e.g. with prefix/davand request URL/dav/foo/bar.txt, the consumer sees/foo/bar.txt). Root path is/and refers to the WebDAV mount itself — list_dir(“/”) returns the top-level entries (one virtual entry per UEFI volume, say).Callbacks return
AXL_OKon success,AXL_ERRon failure. The SDK maps the failure to an HTTP status: stat / list_dir / read failures → 404; mkdir / write_open / move failures → 409 (parent missing) or 500 (other); remove failure → 404 or 423 if locked (latter not fully wired in v1).Streaming callbacks (read/write):
read_openreturns an opaque ctx the SDK threads intoread_chunk(drives the response-body streamer) andread_close(idempotent finalize).write_openlikewise;write_chunkreceives one chunk per dispatcher buffer;write_close(aborted)runs on EOF (aborted=false) OR mid-upload TCP teardown (aborted=true). Same shape as AxlUploadHandler’s clean-EOF/abort contract.
Public Members
-
int (*list_dir)(void *user, const char *path, AxlFsEntry *out, size_t max, size_t *count)
PROPFIND backing — list children of a directory.
-
int (*stat)(void *user, const char *path, AxlFsEntry *out)
Stat — for PROPFIND on a single resource.
-
int (*read_open)(void *user, const char *path, uint64_t offset, void **out_ctx)
Streaming read — drives axl_http_response_set_streamer for GET.
-
int (*read_chunk)(void *ctx, void *buf, size_t buf_size, size_t *bytes_read)
-
void (*read_close)(void *ctx)
-
int (*write_open)(void *user, const char *path, void **out_ctx)
Streaming write — drives the upload-route chunk handler for PUT.
-
int (*write_chunk)(void *ctx, const void *data, size_t len)
-
void (*write_close)(void *ctx, bool aborted)
-
int (*mkdir)(void *user, const char *path)
Lifecycle — MKCOL / DELETE / MOVE / COPY.
-
int (*remove)(void *user, const char *path)
-
int (*move)(void *user, const char *src, const char *dst, bool overwrite)
-
int (*copy)(void *user, const char *src, const char *dst, bool overwrite, int depth)
COPY: replicate
srctodst, leavingsrcin place.depthis 0 (collection itself only, no contents) or -1 (infinity / deep). The SDK rejects Depth: 1 before reaching here per RFC 4918 §9.8.3. Returning AXL_ERR maps to 409. To get RFC-correct 404 (rather than 409) for missing-source, also set stat — the SDK pre-statssrcwhen stat is wired.
-
const char *(*content_type)(void *user, const char *path)
Content-Type hint for GET responses (optional). Returning NULL or omitting the callback uses application/octet-stream.
-
int (*digest)(void *user, const char *path, const char *algo, char *out_hex, size_t hex_size)
Optional: produce a content digest for end-to-end integrity verification (RFC 3230). When wired AND the client sends a
Want-Digest: <algo>[, ...]request header on GET / HEAD, the SDK iterates the requested algorithms (in client-listed order) and calls this callback for each — the first call that returns AXL_OK wins, and the SDK emits the matchingDigest: <algo>=<hex>response header.algois the lowercased canonical algorithm name as the client requested it (typically"sha-256"; legacy"sha-1"and"md5"are also forwarded if requested).out_hexis a caller-allocated buffer ofhex_sizebytes the consumer fills with the lowercase hex digest + trailing NUL. (Consumer always produces hex; the SDK emits hex per the RFC 3230id-sha-*alias convention. The buffer is sized to fit SHA-512 hex output.) Return AXL_OK on success, AXL_ERR for any failure (unknown algo, unreadable file, OOM); on AXL_ERR the SDK silently moves on to the next algorithm in the Want-Digest list, or omits the header entirely if none succeed. Same omission behavior as a non-wired callback.Per RFC 3230 §4.3.2, the digest covers the FULL file even for 206 Partial Content responses — mount clients accumulate the value across their first Range read.
-
void (*before_response)(void *user, AxlHttpRequest *req, AxlHttpResponse *resp)
Optional last-call hook to mutate the response before the SDK hands it to the dispatcher for wire send. Fires AFTER the SDK’s per-verb logic has set status, headers, body / streamer, but BEFORE the dispatcher serializes. Consumer may add or replace headers via
resp->headers(lazy-alloc it if NULL); readingreq->path/req->methodto scope behavior is fine.Fires for every WebDAV verb the handler dispatched. For PUT, fires once on clean EOF (when the response status is set), NOT per chunk. For HEAD, fires once with the headers-only response.
Use cases: custom property emission (ETag, Cache-Control, resource-versioning headers), audit-trail header injection, rate-limit hints. For RFC 3230 Digest emission specifically, wire the
digestcallback instead — the SDK already does the Want-Digest parsing.
AxlHttpClient
Typedefs
-
typedef struct AxlHttpClient AxlHttpClient
axl-http-client.h:
HTTP client with GET, POST, PUT, DELETE, and file download.
Configuration uses string key-value pairs (like librdkafka):
AxlHttpClient *c = axl_http_client_new(); axl_http_client_set(c, "timeout.ms", "30000"); axl_http_client_set(c, "keep.alive", "false"); axl_http_client_set(c, "max.redirects", "0"); axl_http_client_set(c, "header.User-Agent", "MyApp/1.0");
Supported options: “timeout.ms” — per-operation timeout in milliseconds (default: “10000”) “keep.alive” — connection reuse: “true” (default) or “false” “max.redirects” — redirect limit (default: “5”), “0” to disable “tls.verify” — certificate verification: “true” (default) or “false” “header.<Name>” — default header sent with every request
-
typedef int (*AxlRequestBodyStreamer)(void *ctx, void *out_buf, size_t out_buf_size, size_t *out_size)
Producer callback for streaming PUT/POST request bodies.
Called repeatedly by the client to fill the next outgoing chunk. The implementation reads from its backing source (file, buffer, generated content) into
out_bufand reports the byte count viaout_size. A return of AXL_OK with*out_size== 0 signals end-of-body — the client emits the final empty chunk (chunked transfer) or stops sending (Content-Length transfer).- Return:
AXL_OK to continue (including the EOF case); AXL_ERR to abort the request. The client tears the connection down on AXL_ERR; the cleanup callback (if any) still fires.
Functions
-
AxlHttpClient *axl_http_client_new(void)
Create a new HTTP client with default options.
- Returns:
client instance, or NULL on failure.
-
void axl_http_client_free(AxlHttpClient *c)
Free an HTTP client and close any open connection.
- Parameters:
c – client to free (NULL-safe)
-
int axl_http_client_set(AxlHttpClient *c, const char *key, const char *value)
Set a client option.
All values are strings, parsed internally. See header comment for the list of supported options.
- Parameters:
c – client
key – option name
value – option value (string)
- Returns:
AXL_OK on success, AXL_ERR on unknown option.
-
const char *axl_http_client_get(AxlHttpClient *c, const char *key)
Get a client option value.
Returns a pointer to the internally stored string. The pointer remains valid until the option is changed or the client is freed.
- Parameters:
c – client
key – option name
- Returns:
option value string, or NULL for unknown options.
-
int axl_http_get(AxlHttpClient *c, const char *url, AxlHttpClientResponse **out_resp)
HTTP GET request.
- Parameters:
c – client
url – full URL string
out_resp – receives response; free with axl_http_client_response_free()
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_post(AxlHttpClient *c, const char *url, const void *body, size_t size, const char *content_type, AxlHttpClientResponse **out_resp)
HTTP POST request.
- Parameters:
c – client
url – full URL string
body – request body
size – body size in bytes
content_type – MIME type (e.g. “application/json”)
out_resp – receives response
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_put(AxlHttpClient *c, const char *url, const void *body, size_t size, const char *content_type, AxlHttpClientResponse **out_resp)
HTTP PUT request.
- Parameters:
c – client
url – full URL string
body – request body
size – body size in bytes
content_type – MIME type
out_resp – receives response
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_delete(AxlHttpClient *c, const char *url, AxlHttpClientResponse **out_resp)
HTTP DELETE request.
- Parameters:
c – client
url – full URL string
out_resp – receives response
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_request(AxlHttpClient *c, const char *method, const char *url, const void *body, size_t body_size, const char *content_type, AxlHashTable *extra_headers, AxlHttpClientResponse **out_resp)
Generic HTTP request with optional per-request headers.
- Parameters:
c – client
method – HTTP method (“GET”, “POST”, “PUT”, “DELETE”, etc.)
url – full URL string
body – request body, or NULL
body_size – body size in bytes
content_type – MIME type, or NULL
extra_headers – optional hash table of additional headers (NULL for none)
out_resp – receives response
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
int axl_http_request_streaming(AxlHttpClient *c, const char *method, const char *url, AxlRequestBodyStreamer streamer, void *ctx, void (*cleanup_fn)(void *ctx), size_t total_size, const char *content_type, AxlHashTable *extra_headers, AxlHttpClientResponse **out_resp)
Issue an HTTP request with a streaming request body.
Mirrors axl_http_request but builds the body via
streamerrather than a contiguous buffer. Use for multi-chunk uploads where the body isn’t materialized in RAM (UEFI Shellcpto a mounted volume, large-file PUT, generated content, etc.).When
total_sizeis known, the client emits aContent-Lengthheader and sends the body as raw bytes; if the streamer signals EOF beforetotal_sizebytes are produced the request fails with AXL_ERR. Pass(size_t)-1to useTransfer-Encoding: chunkedinstead — useful when the producer doesn’t know the total length up front.cleanup_fn(if non-NULL) fires once after the request completes — success, error, OR streamer abort — so consumers can release the producer state at a single site instead of threading cleanup through every error return.Streaming requests do NOT retry on stale connections or follow redirects: the producer callback can only be consumed once. Callers who need either behavior must re-invoke this function with a fresh streamer state.
- Parameters:
method – “PUT”, “POST”, etc.
streamer – producer callback
ctx – opaque, passed to streamer + cleanup
cleanup_fn – optional finalizer (NULL = none)
total_size – body length in bytes; (size_t)-1 = chunked
- Returns:
AXL_OK on success, AXL_ERR on failure (connection reset, streamer returned AXL_ERR, Content-Length mismatch, etc.).
-
int axl_http_request_stream_file(AxlHttpClient *c, const char *method, const char *url, const char *path, const char *content_type, AxlHashTable *extra_headers, AxlHttpClientResponse **out_resp)
Issue an HTTP request whose body is the contents of a local file, streamed via AxlStream.
Convenience wrapper over axl_http_request_streaming for the common “upload this file” case. Opens
pathread-only via axl_fopen, sizes the body via axl_file_info, and streams the bytes to the wire without materializing the whole file in RAM. The stream is closed when the request completes.Use this for
cp/ upload semantics where the producer is just “the bytes on disk.” For producer/consumer patterns where the source isn’t a file (ring buffers, generated content), use axl_http_request_streaming with a custom producer callback.- Parameters:
path – local file path (UTF-8)
- Returns:
AXL_OK on success, AXL_ERR on failure (file unreadable, producer error, connection reset, etc.).
-
void axl_http_client_response_free(AxlHttpClientResponse *resp)
Free a client response.
- Parameters:
resp – response to free (NULL-safe)
-
int axl_http_download(AxlHttpClient *c, const char *url, const char *local_path)
Download a URL to a local file.
- Parameters:
c – client
url – full URL string
local_path – filesystem path to write (UTF-8)
- Returns:
AXL_OK on success, AXL_ERR on failure.
-
struct AxlHttpClientResponse