ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
http_delivery.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file http_delivery.h
6 * @brief HTTP delivery optimizations: stale-while-revalidate, Range/206 delta fetch, SW precache
7 * (PC_ENABLE_HTTP_DELIVERY).
8 *
9 * Three pure cores that make HTTP serving cheaper on a constrained device, each mapping to a real web
10 * standard:
11 *
12 * - **Stale-while-revalidate** (RFC 5861): given a cached response's age and its `max-age` +
13 * `stale-while-revalidate` windows, decide FRESH / serve-stale-and-revalidate / EXPIRED, and build the
14 * matching `Cache-Control` header so a browser keeps the UI responsive while the device refreshes in
15 * the background.
16 * - **Delta / offset log fetch** (RFC 7233 byte ranges): parse a `Range: bytes=...` request against a
17 * resource of known length (all three forms - `X-Y`, `X-`, `-N`), and build the `Content-Range` header
18 * for the `206 Partial Content` reply, so a client streams only the new tail of a growing log.
19 * - **Service-worker precache manifest**: emit the versioned `{"version":..,"precache":[..]}` JSON a
20 * generated service worker consumes to cache-inject the app shell for offline / instant loads.
21 *
22 * Pure, zero heap, no stdlib (hand-rolled decimal parse/format), host-testable.
23 */
24
25#ifndef PROTOCORE_HTTP_DELIVERY_H
26#define PROTOCORE_HTTP_DELIVERY_H
27
28#include "protocore_config.h"
29#include <stddef.h>
30#include <stdint.h>
31
32#if PC_ENABLE_HTTP_DELIVERY
33
34/** @brief Freshness verdict for a cached response. */
35/** @brief Cache-freshness verdict (the sole return of pc_delivery_swr). */
36enum class DeliveryVerdict : uint8_t
37{
38 DELIVERY_FRESH = 0, ///< age <= max-age: serve from cache, no revalidation.
39 DELIVERY_STALE_REVALIDATE = 1, ///< within the stale-while-revalidate window: serve stale, refresh in bg.
40 DELIVERY_EXPIRED = 2 ///< past both windows: must revalidate before serving.
41};
42
43/**
44 * @brief RFC 5861 freshness decision.
45 * @param age_s seconds since the response was generated.
46 * @param max_age_s the `max-age` window.
47 * @param swr_s the `stale-while-revalidate` window past max-age.
48 * @return DELIVERY_FRESH / DELIVERY_STALE_REVALIDATE / DELIVERY_EXPIRED.
49 */
50DeliveryVerdict pc_delivery_swr(uint32_t age_s, uint32_t max_age_s, uint32_t swr_s);
51
52/**
53 * @brief Build a `Cache-Control` value: `public, max-age=N[, stale-while-revalidate=M]`.
54 * The swr directive is omitted when @p swr_s is 0.
55 * @return length written (excl NUL), or 0 on overflow / bad args.
56 */
57size_t pc_delivery_cache_control(uint32_t max_age_s, uint32_t swr_s, char *out, size_t cap);
58
59// Byte-range serving is NOT here. `server/http_range.h` (`http_parse_byte_range`, PC_ENABLE_RANGE)
60// owns the RFC 7233 range math and is already wired into static file serving and the edge cache -
61// it emits `Accept-Ranges`, the 206 `Content-Range`, and a 416 with `bytes */<size>`, which this
62// header's earlier duplicate could not signal. Two parsers for one concern is how a request ends up
63// answered by the wrong one, so the duplicate was removed rather than given a second call site.
64
65/**
66 * @brief Emit the service-worker precache manifest: `{"version":"..","precache":["/a","/b",...]}`.
67 * @param paths asset paths to precache (borrowed).
68 * @param n number of paths.
69 * @param version cache version tag (busts the SW cache on change).
70 * @return length written (excl NUL), or 0 on overflow / bad args. Strings are JSON-escaped.
71 */
72size_t pc_delivery_sw_manifest(const char *const *paths, size_t n, const char *version, char *out, size_t cap);
73
74#if defined(ARDUINO)
75class PC;
76
77/**
78 * @brief Serve the service worker and its precache manifest.
79 *
80 * Registers two GET routes on @p srv, which together are the client half of the delivery story:
81 * - `/sw.js` the worker (a flash-resident asset; registers with `navigator.serviceWorker`)
82 * - `/precache.json` the versioned manifest built by pc_delivery_sw_manifest()
83 *
84 * The worker precaches @p paths and then serves them stale-while-revalidate, so the browser gets the
85 * shell instantly and the device is only asked for a refresh in the background. Bump @p version
86 * whenever the shell changes: the worker names its cache after it, so a new version invalidates the
87 * old shell exactly once.
88 *
89 * @param paths asset paths to precache (borrowed; must outlive the server).
90 * @param n number of paths (<= PC_DELIVERY_PRECACHE_MAX).
91 * @param version cache version tag, e.g. a firmware version string.
92 * @return true if both routes were registered.
93 */
94bool pc_delivery_serve_sw(PC &srv, const char *const *paths, size_t n, const char *version);
95#endif // ARDUINO
96
97#endif // PC_ENABLE_HTTP_DELIVERY
98#endif // PROTOCORE_HTTP_DELIVERY_H
Single-port HTTP server with deterministic, zero-allocation execution.
Definition protocore.h:348
User-facing configuration for ProtoCore.