ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
http_delivery.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - 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 * (PROTOCORE_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" // the entry point: protocore_types.h for the widths
29
30#if PROTOCORE_ENABLE_HTTP_DELIVERY
31
33
34// This module holds nothing between calls, so it carves no borrow and states none. An entry
35// takes one all the same, and never reads it, so every namespace in the tree is invoked the
36// same way.
37
38/** @brief Cache-freshness verdict (the sole return of swr). */
39typedef enum PROTO_ENUM_PACKED
40{
41 DELIVERY_FRESH = 0, ///< age <= max-age: serve from cache, no revalidation.
42 DELIVERY_STALE_REVALIDATE = 1, ///< within the stale-while-revalidate window: serve stale, refresh in bg.
43 DELIVERY_EXPIRED = 2 ///< past both windows: must revalidate before serving.
44} DeliveryVerdict;
45
46/** @brief What swr takes: age_s, max_age_s, swr_s. */
47typedef struct
48{
49 uint32_t age_s; ///< seconds since the response was generated
50 uint32_t max_age_s; ///< the `max-age` window
51 uint32_t swr_s; ///< the `stale-while-revalidate` window past max-age
52} HttpDeliverySwrArgs;
53
54/** @brief What cache_control takes: max_age_s, ... */
55typedef struct
56{
57 uint32_t max_age_s;
58 uint32_t swr_s;
59 char *out;
60 size_t cap;
61} HttpDeliveryCacheControlArgs;
62
63/** @brief What sw_manifest takes: paths, n, ... */
64typedef struct
65{
66 const char *const *paths; ///< asset paths to precache (borrowed)
67 size_t n; ///< number of paths
68 const char *version; ///< cache version tag (busts the SW cache on change)
69 char *out;
70 size_t cap;
71} HttpDeliverySwManifestArgs;
72
73/** @brief What serve_sw takes: paths, n, version. */
74typedef struct
75{
76 const char *const *paths; ///< asset paths to precache (borrowed; must outlive the server)
77 size_t n; ///< number of paths (<= PROTOCORE_DELIVERY_PRECACHE_MAX)
78 const char *version; ///< cache version tag, e.g. a firmware version string
79} HttpDeliveryServeSwArgs;
80
81/**
82 * @brief HTTP delivery optimizations: stale-while-revalidate, Range/206 delta fetch, SW precache
83 * (PROTOCORE_ENABLE_HTTP_DELIVERY).
84 *
85 * A caller sets the members a call takes, invokes it through ::HttpDelivery with the bytes it runs
86 * out of, and reads the outcome off the same handle.
87 *
88 * HttpDelivery.swr_args.age_s = ...;
89 * HttpDelivery.swr_args.max_age_s = ...;
90 * HttpDelivery.swr_args.swr_s = ...;
91 * HttpDelivery.swr(work);
92 * // HttpDelivery.value is what the call reports
93 *
94 * @var HttpDeliveryNs::swr_args what swr takes: age_s, max_age_s, swr_s
95 * @var HttpDeliveryNs::cache_control_args what cache_control takes: max_age_s,
96 * @var HttpDeliveryNs::sw_manifest_args what sw_manifest takes: paths, n,
97 * @var HttpDeliveryNs::serve_sw_args what serve_sw takes: paths, n, version
98 * @var HttpDeliveryNs::ok true if both routes were registered
99 * @var HttpDeliveryNs::value DELIVERY_FRESH / DELIVERY_STALE_REVALIDATE / DELIVERY_EXPIRED
100 * @var HttpDeliveryNs::n length written (excl NUL), or 0 on overflow / bad args
101 * @var HttpDeliveryNs::swr RFC 5861 freshness decision
102 * @var HttpDeliveryNs::cache_control build a `Cache-Control` value: `public, max-age=N[, ...
103 * @var HttpDeliveryNs::sw_manifest emit the service-worker precache manifest: ...
104 * @var HttpDeliveryNs::serve_sw serve the service worker and its precache manifest. Registers two
105 * ...
106 *
107 * @c work is bytes the CALLER holds. This module reads none of them: it carries nothing
108 * between calls, so there is no state to keep and nothing to wipe. The parameter is there so
109 * a caller drives every namespace the same way.
110 */
111typedef struct
112{
113 HttpDeliverySwrArgs swr_args;
114 HttpDeliveryCacheControlArgs cache_control_args;
115 HttpDeliverySwManifestArgs sw_manifest_args;
116 HttpDeliveryServeSwArgs serve_sw_args;
117 proto_bool ok;
118 DeliveryVerdict value;
119 size_t n;
120} HttpDeliveryVars;
121
122/** @brief The operands and the outcome. */
123extern HttpDeliveryVars HttpDeliveryV;
124
125/** @brief The entries. */
126typedef struct
127{
128 void (*const swr)(uint8_t *work);
129 void (*const cache_control)(uint8_t *work);
130 void (*const sw_manifest)(uint8_t *work);
131 void (*const serve_sw)(uint8_t *work);
132} HttpDeliveryNs;
133
134// What the table binds, defined once in the .c and taking one parameter each: everything
135// else an entry needs is an operand in HttpDeliveryV or a region of the borrow at a fixed offset.
136void protocore_http_delivery_swr(uint8_t *work);
137void protocore_http_delivery_cache_control(uint8_t *work);
138void protocore_http_delivery_sw_manifest(uint8_t *work);
139void protocore_http_delivery_serve_sw(uint8_t *work);
140
141// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
142// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
143// `HttpDelivery.swr(work)` resolves to a named function and becomes a DIRECT call. An extern table
144// leaves the call indirect and the symbol live at every level, -O2 -flto included.
145static const HttpDeliveryNs HttpDelivery __attribute__((unused)) = {
146 .swr = protocore_http_delivery_swr,
147 .cache_control = protocore_http_delivery_cache_control,
148 .sw_manifest = protocore_http_delivery_sw_manifest,
149 .serve_sw = protocore_http_delivery_serve_sw,
150};
151
153
154#endif // PROTOCORE_ENABLE_HTTP_DELIVERY
155
156#endif // PROTOCORE_HTTP_DELIVERY_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97