ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
presentation.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 presentation.h
6 * @brief Layer 6 (Presentation) - wires the transport ring buffer to the HTTP parser.
7 *
8 * This layer owns two responsibilities:
9 * 1. Hold HTTP's own per-slot state - the request tally, the request deadline, the response sink,
10 * and the h2/h3 fields - keyed on the transport slot index.
11 * 2. Expose ::HttpConn, the calls the session layer dispatches through and the application layer
12 * drives by slot.
13 *
14 * The parsing itself lives in `http_parser.h`, which this includes so callers need one include.
15 *
16 * @author Douglas Quigg (dstroy0)
17 * @date 2026
18 */
19
20#ifndef PROTOCORE_PRESENTATION_H
21#define PROTOCORE_PRESENTATION_H
22
24#include "network_drivers/session/session.h" // the per-connection tables this reads
25#include "network_drivers/transport/tcp/evt/evt.h" // EvtType: the event a handler is dispatched on
26
27// ---------------------------------------------------------------------------
28// Slot-indexed wrappers called by the session and application layers
29// ---------------------------------------------------------------------------
30
31/**
32 * @brief Reset the HTTP parser for a connection slot.
33 *
34 * Delegates to http_parser_reset() for the slot's request struct.
35 * Silently ignores out-of-range slot IDs.
36 *
37 * @param slot_id Index into conn_pool / http_pool (0 … MAX_CONNS-1).
38 */
39
40#if PROTOCORE_ENABLE_KEEPALIVE
41/**
42 * @brief Requests served on each connection slot (HTTP keep-alive fairness bound).
43 *
44 * Reset to 0 by http_conn_open() when a connection is accepted; incremented by
45 * keepalive_eval() per response it elects to keep alive. Lives here (not in
46 * TcpConn) so the transport layer stays free of HTTP semantics. Defined in
47 * presentation.c.
48 */
49extern uint16_t http_req_count[MAX_CONNS];
50#endif
51
52/**
53 * @brief Self-framing response sink (Layer 5 TX seam).
54 *
55 * HTTP/2 installs it at ALPN, HTTP/3 at dispatch, so the response methods route through it instead
56 * of building an HTTP/1.1 message. Null means plain HTTP/1.1, the default builder.
57 */
58
59/**
60 * @brief HTTP's own per-slot state, keyed on the transport slot index.
61 *
62 * All of it HTTP semantics, so it lives here rather than in TcpConn, the same way
63 * ::http_req_count already does. Sized CONN_POOL_SLOTS, not MAX_CONNS: the HTTP/3 dispatch slot is
64 * a reserved index above the TCP range. Defined in presentation.c.
65 *
66 * @var http_req_start_ms protocore_millis() at the first byte of the in-progress request (0 = none).
67 * The request-header deadline (PROTOCORE_REQUEST_TIMEOUT_MS, slow-loris
68 * defense) measures against this; unlike the transport's idle timer a
69 * trickle byte cannot reset it. Armed by the HTTP layer on the first byte.
70 * @var http_resp_sink the TX seam above, per slot.
71 */
72
73#if PROTOCORE_ENABLE_KEEPALIVE
74
75/**
76 * @brief Whether the connection carrying @p slot_id's request is reused for the next one.
77 *
78 * Reads the parsed request: a message whose boundary is not known closes, HTTP/1.1 is persistent
79 * unless Connection carries "close", and 1.0 is the reverse. A kept connection counts against
80 * PROTOCORE_KEEPALIVE_MAX_REQUESTS and closes once it reaches the bound.
81 */
82#endif
83
84#if PROTOCORE_ENABLE_KEEPALIVE || PROTOCORE_ENABLE_WEBSOCKET
85/**
86 * @brief Whether @p token appears as an element of the Connection header value @p hdr.
87 *
88 * The value is a comma-delimited list ("Keep-Alive, Upgrade"), matched case insensitively on whole
89 * elements so a longer token cannot match on its prefix.
90 */
91#endif
92
93/**
94 * @brief Initialize a slot for a freshly-accepted HTTP connection.
95 *
96 * Resets the HTTP parser (like http_reset()) and, when keep-alive is enabled,
97 * zeroes the slot's persistent request counter. The session layer calls this on
98 * EvtType::EVT_CONNECT; http_reset() is used for the lighter inter-request reset that must
99 * not clear the counter. With keep-alive off this is identical to http_reset().
100 *
101 * @param slot_id Index into conn_pool / http_pool (0 … MAX_CONNS-1).
102 */
103
104/**
105 * @brief Drain the transport ring buffer and advance the HTTP parser.
106 *
107 * Reads all available bytes from the slot's transport ring buffer and feeds
108 * each byte to `http_parser_feed()`. Stops early if the parser reaches a
109 * terminal state (PARSE_COMPLETE, PARSE_ERROR, PARSE_ENTITY_TOO_LARGE,
110 * PARSE_URI_TOO_LONG).
111 *
112 * Silently ignores out-of-range slot IDs.
113 *
114 * @param slot_id Connection slot to parse.
115 */
116
117/**
118 * @brief The HTTP connection ProtoHandler (the L5 dispatch seam).
119 *
120 * The accept/data/close handlers - the data path multiplexes the TLS handshake,
121 * HTTP/2 ALPN, and the WebSocket upgrade before the HTTP/1.1 parser. Returned by
122 * accessor (not self-registered) so this module carries no dependency on the
123 * session layer; Session.proto->register_builtins() installs it.
124 */
125struct ProtoHandler;
126
127/** @brief RFC 9110 sec 5.6.1: a comma-separated header, and the token looked for in it. */
128typedef struct
129{
130 const char *hdr; ///< the field value scanned
131 const char *token; ///< the token looked for, case-insensitive
133
134/**
135 * @brief Layer 6 - the HTTP connection: what wires a transport slot to the HTTP parser.
136 *
137 * A caller sets the members a call takes, invokes it through ::HttpConn, and reads the outcome off
138 * the same handle.
139 *
140 * @var HttpConnNs::slot the connection a call acts on
141 * @var HttpConnNs::hdr_args the header value a token test reads, and the element it looks for
142 * @var HttpConnNs::poll the per-slot poll pump the application installs
143 * @var HttpConnNs::ok a call's true/false outcome
144 * @var HttpConnNs::handler the ProtoHandler a lookup reports
145 * @var HttpConnNs::reset reset the parser between requests, leaving the keep-alive tally
146 * @var HttpConnNs::conn_open initialize a slot for a freshly-accepted connection
147 * @var HttpConnNs::parse drain the slot's bytes and advance the parser
148 * @var HttpConnNs::keepalive_eval whether the connection is reused for the next request
149 * @var HttpConnNs::has_token whether hdr_args.token appears as an element of hdr_args.hdr
150 * @var HttpConnNs::proto_handler the L5 dispatch seam this module registers into
151 * @var HttpConnNs::set_poll install the per-slot poll pump
152 */
153typedef struct
154{
155 uint8_t slot; ///< the connection every call names
156 void (*poll)(uint8_t slot); ///< what set_poll installs as the per-tick step
157 HttpHdrArgs hdr_args; ///< the header a token scan reads
159 const struct ProtoHandler *handler;
160#if PROTOCORE_ENABLE_KEEPALIVE
161#endif
162#if PROTOCORE_ENABLE_KEEPALIVE || PROTOCORE_ENABLE_WEBSOCKET
163#endif
165
166/** @brief The operands and the outcome. */
168
169/** @brief The entries. */
170typedef struct
171{
172 void (*const reset)(uint8_t *work);
173 void (*const conn_open)(uint8_t *work);
174 void (*const parse)(uint8_t *work);
175 void (*const keepalive_eval)(uint8_t *work);
176 void (*const has_token)(uint8_t *work);
177 void (*const proto_handler)(uint8_t *work);
178 void (*const set_poll)(uint8_t *work);
179} HttpConnNs;
180
181// What the table binds, defined once in the .c and taking one parameter each: everything
182// else an entry needs is an operand in HttpConnV or a region of the borrow at a fixed offset.
183void protocore_http_conn_reset(uint8_t *work);
185void protocore_http_conn_parse(uint8_t *work);
190
191// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
192// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
193// `HttpConn.reset(work)` resolves to a named function and becomes a DIRECT call. An extern table
194// leaves the call indirect and the symbol live at every level, -O2 -flto included.
195static const HttpConnNs HttpConn __attribute__((unused)) = {
199 .keepalive_eval = protocore_http_conn_keepalive_eval,
201 .proto_handler = protocore_http_conn_proto_handler,
203};
204
205/**
206 * @brief The PROTOCORE_HTTP_CONN_BORROW bytes this module's state lives in.
207 *
208 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
209 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
210 * walks, so the state lasts the life of the program.
211 *
212 * @return the span.
213 */
215
216#endif
#define MAX_CONNS
Maximum simultaneous TCP connections (fixed static pool; ~3.95 KB of internal RAM per slot).
What the stack callbacks post to a listener's queue: the event type and the record.
HttpParser..
uint8_t * protocore_http_conn_span(void)
The PROTOCORE_HTTP_CONN_BORROW bytes this module's state lives in.
void protocore_http_conn_conn_open(uint8_t *work)
void protocore_http_conn_has_token(uint8_t *work)
void protocore_http_conn_keepalive_eval(uint8_t *work)
void protocore_http_conn_proto_handler(uint8_t *work)
void protocore_http_conn_reset(uint8_t *work)
void protocore_http_conn_parse(uint8_t *work)
HttpConnVars HttpConnV
The operands and the outcome.
void protocore_http_conn_set_poll(uint8_t *work)
Layer 5 (Session) - where a connection is opened, closed and controlled.
The entries.
void(*const reset)(uint8_t *work)
uint8_t slot
the connection every call names
const struct ProtoHandler * handler
HttpHdrArgs hdr_args
the header a token scan reads
proto_bool ok
RFC 9110 sec 5.6.1: a comma-separated header, and the token looked for in it.
const char * token
the token looked for, case-insensitive
const char * hdr
the field value scanned
Per-protocol connection event/poll callbacks (the server's dispatch vtable).
_Bool proto_bool
The truth value.
Definition types.h:64