ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
file_serving.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 file_serving.h
6 * @brief Layer 7 static file responses: open a path on a mount and page it out over a slot.
7 *
8 * serve_file_internal() opens the file and writes the response head; file_send_pump() writes one
9 * send-buffer window per call until the file is drained, so a response larger than the buffer is
10 * spread across dispatches instead of held in one. protocore_file_holds_slot() reports whether a slot is
11 * mid-paging.
12 *
13 * @author Douglas Quigg (dstroy0)
14 * @date 2026
15 */
16
17#ifndef PROTOCORE_FILE_SERVING_H
18#define PROTOCORE_FILE_SERVING_H
19
20#include "protocore_config.h" // the entry point: protocore_types.h for the widths
21
22#if PROTOCORE_ENABLE_FILE_SERVING
23
24#include "network_drivers/presentation/http/http_parser/http_parser.h" // the complete type a public struct below holds by value
25#include "network_drivers/presentation/http/route/http_route/http_route.h" // the complete type a public struct below holds by value
26#include "server/storage/mnt/mnt.h" // the complete type a public struct below holds by value
27
29
30// PROTOCORE_FILE_SERVING_BORROW - the bytes this module runs out of - is stated in protocore_config.h, which sums
31// it into its arena. A caller takes them once and passes the pointer to every call. How they
32// are carved is this module's and is never named here.
33
34#include "network_drivers/presentation/http/http_parser/http_parser.h" // HttpReq: the type a parameter points at
35
36#include "network_drivers/presentation/http/route/http_route/http_route.h" // HttpRoute: the type a parameter points at
37
38#include "server/storage/mnt/mnt.h" // protocore_mnt_backend: the type a parameter points at
39
40/** @brief What http_rfc1123 takes: epoch, out, cap. */
41typedef struct
42{
43 int64_t epoch;
44 char *out;
45 size_t cap;
46} FileServingHttpRfc1123Args;
47
48/** @brief What serve_static_request takes: slot_id, req, r. */
49typedef struct
50{
51 uint8_t slot_id;
52 HttpReq *req;
53 const HttpRoute *r;
54} FileServingServeStaticRequestArgs;
55
56/** @brief What serve_file_internal takes: slot_id, head, file_sys, ... */
57typedef struct
58{
59 uint8_t slot_id;
60 proto_bool head;
61 const protocore_mnt_backend *file_sys;
62 const char *fs_path;
63 const char *content_type;
64 const char *content_encoding;
65} FileServingServeFileInternalArgs;
66
67/** @brief What file_send_pump takes: slot_id. */
68typedef struct
69{
70 uint8_t slot_id;
71} FileServingFileSendPumpArgs;
72
73/** @brief What holds_slot takes: slot. */
74typedef struct
75{
76 uint8_t slot;
77} FileServingHoldsSlotArgs;
78
79/** @brief What serve_file takes: slot_id, file_sys, fs_path, ... */
80typedef struct
81{
82 uint8_t slot_id; ///< Connection slot index
83 const protocore_mnt_backend *file_sys; ///< Backend to read from; NULL uses whatever is mounted (the board's)
84 const char *fs_path; ///< Request path to the file, resolved against the mount root
85 const char *content_type; ///< MIME type string, e.g. "text/html"
86} FileServingServeFileArgs;
87
88/** @brief What serve_static takes: url_prefix, file_sys, fs_root. */
89typedef struct
90{
91 const char *url_prefix; ///< URL prefix to mount (with or without a trailing `*`)
92 const protocore_mnt_backend *file_sys; ///< Backend to serve from; NULL uses whatever is mounted (the board's)
93 const char *fs_root; ///< Subtree on that backend (persistent string)
94} FileServingServeStaticArgs;
95
96/**
97 * @brief Layer 7 static file responses: open a path on a mount and page it out over a slot. serve_file_internal() opens
98 * the file and writes the response head; file_send_pump() writes one send-buffer window per call until the file is
99 * drained, so a response larger than the buffer is spread across dispatches instead of held in one.
100 * protocore_file_holds_slot() reports whether a slot is mid-paging.
101 *
102 * A caller sets the members a call takes, invokes it through ::FileServing with the bytes it runs
103 * out of, and reads the outcome off the same handle.
104 *
105 * FileServing.http_rfc1123_args.epoch = ...;
106 * FileServing.http_rfc1123_args.out = ...;
107 * FileServing.http_rfc1123_args.cap = ...;
108 * FileServing.http_rfc1123(work);
109 *
110 * @var FileServingNs::http_rfc1123_args what http_rfc1123 takes: epoch, out, cap
111 * @var FileServingNs::serve_static_request_args what serve_static_request takes: slot_id, req, r
112 * @var FileServingNs::serve_file_internal_args what serve_file_internal takes: slot_id, head, file_sys,
113 * @var FileServingNs::file_send_pump_args what file_send_pump takes: slot_id
114 * @var FileServingNs::holds_slot_args what holds_slot takes: slot
115 * @var FileServingNs::serve_file_args what serve_file takes: slot_id, file_sys, fs_path,
116 * @var FileServingNs::serve_static_args what serve_static takes: url_prefix, file_sys, fs_root
117 * @var FileServingNs::ok a call's true/false outcome
118 * @var FileServingNs::http_rfc1123 format epoch as an RFC 1123 GMT date into out (cap bytes); out is ...
119 * @var FileServingNs::serve_static_request dispatch a ROUTE_STATIC match: resolve the FS path and serve it ...
120 * @var FileServingNs::serve_file_internal open fs_path on file_sys and stream it as 200 with the given type ...
121 * @var FileServingNs::file_send_pump resume a pending file response: page out one send-buffer window, ...
122 * @var FileServingNs::holds_slot true while a file response is paging out on slot
123 * @var FileServingNs::serve_file serve a file from the mounted volume. Opens fs_path through the ...
124 * @var FileServingNs::serve_static mount a filesystem subtree at a URL prefix (one-call static ...
125 *
126 * @c work is PROTOCORE_FILE_SERVING_BORROW bytes the CALLER took, at an address it knows. It is not held past the call,
127 * so nothing here aliases it. How those bytes are carved is this module's and is never named here.
128 */
129typedef struct
130{
131 FileServingHttpRfc1123Args http_rfc1123_args;
132 FileServingServeStaticRequestArgs serve_static_request_args;
133 FileServingServeFileInternalArgs serve_file_internal_args;
134 FileServingFileSendPumpArgs file_send_pump_args;
135 FileServingHoldsSlotArgs holds_slot_args;
136 FileServingServeFileArgs serve_file_args;
137 FileServingServeStaticArgs serve_static_args;
138 proto_bool ok;
139} FileServingVars;
140
141/** @brief The operands and the outcome. */
142extern FileServingVars FileServingV;
143
144/** @brief The entries. */
145typedef struct
146{
147 void (*const http_rfc1123)(uint8_t *work);
148 void (*const serve_static_request)(uint8_t *work);
149 void (*const serve_file_internal)(uint8_t *work);
150 void (*const file_send_pump)(uint8_t *work);
151 void (*const holds_slot)(uint8_t *work);
152 void (*const serve_file)(uint8_t *work);
153 void (*const serve_static)(uint8_t *work);
154} FileServingNs;
155
156// What the table binds, defined once in the .c and taking one parameter each: everything
157// else an entry needs is an operand in FileServingV or a region of the borrow at a fixed offset.
158void protocore_file_serving_http_rfc1123(uint8_t *work);
159void protocore_file_serving_serve_static_request(uint8_t *work);
160void protocore_file_serving_serve_file_internal(uint8_t *work);
161void protocore_file_serving_file_send_pump(uint8_t *work);
162void protocore_file_serving_holds_slot(uint8_t *work);
163void protocore_file_serving_serve_file(uint8_t *work);
164void protocore_file_serving_serve_static(uint8_t *work);
165
166// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
167// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
168// `FileServing.http_rfc1123(work)` resolves to a named function and becomes a DIRECT call. An extern table
169// leaves the call indirect and the symbol live at every level, -O2 -flto included.
170static const FileServingNs FileServing __attribute__((unused)) = {
171 .http_rfc1123 = protocore_file_serving_http_rfc1123,
172 .serve_static_request = protocore_file_serving_serve_static_request,
173 .serve_file_internal = protocore_file_serving_serve_file_internal,
174 .file_send_pump = protocore_file_serving_file_send_pump,
175 .holds_slot = protocore_file_serving_holds_slot,
176 .serve_file = protocore_file_serving_serve_file,
177 .serve_static = protocore_file_serving_serve_static,
178};
179
180/**
181 * @brief The PROTOCORE_FILE_SERVING_BORROW bytes this module's state lives in.
182 *
183 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
184 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
185 * walks, so the state lasts the life of the program.
186 *
187 * @return the span.
188 */
189uint8_t *protocore_file_serving_span(void);
190
192
193#endif // PROTOCORE_ENABLE_FILE_SERVING
194
195#endif // PROTOCORE_FILE_SERVING_H
HttpParser..
The route table: where a request goes.
The mount: which store is behind the filesystem, and the vtable it answers through.
Internal route entry stored in the routing table.
Definition http_route.h:56
A storage backend. Each open call returns a small handle (>= 0) or -1.
Definition mnt.h:88
#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