ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
cloudevents.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 cloudevents.h
6 * @brief The CloudEvents envelope: the structured-mode JSON build and the binary-mode header read.
7 *
8 * CloudEvents is a CNCF specification, not an IETF one. Everything here follows CloudEvents
9 * Version 1.0.2 and its two companion documents: the JSON Event Format for CloudEvents Version 1.0.2
10 * and the HTTP Protocol Binding for CloudEvents Version 1.0.2.
11 *
12 * The core specification, section "Context Attributes", splits the attributes an event carries into
13 * "REQUIRED Attributes" - `id`, `source`, `specversion`, `type` - and "OPTIONAL Attributes" -
14 * `datacontenttype`, `dataschema`, `subject`, `time`. Section "Event Data" gives the payload, which
15 * "will be encapsulated within `data`". `specversion` is written for the caller: the section by that
16 * name states a producer "MUST use a value of `1.0` when referring to this version of the
17 * specification", so every envelope built here carries ::PROTOCORE_CLOUDEVENTS_SPECVERSION.
18 *
19 * The HTTP Protocol Binding sec 1.3 names the content modes, and this module covers two of the
20 * three:
21 *
22 * - **Structured Content Mode** (sec 3.2): the whole event is one JSON object in the message body,
23 * and sec 3.2.1 requires the `Content-Type` to be the event format's media type, which the JSON
24 * Event Format sec 3 fixes at ::PROTOCORE_CLOUDEVENTS_MEDIA_TYPE. @ref CloudEventsNs::build_structured
25 * writes that object into a caller buffer.
26 * - **Binary Content Mode** (sec 3.1): sec 3.1.3.1 maps every context attribute to an HTTP header
27 * "with the same name as the attribute name but prefixed with `ce-`", sec 3.1.1 carries
28 * `datacontenttype` in `Content-Type` instead, and sec 3.1.2 makes the message body the `data`
29 * byte-sequence. @ref CloudEventsNs::read_binary takes an inbound message's attributes off those
30 * headers.
31 *
32 * Batched Content Mode (sec 3.3) and its `application/cloudevents-batch+json` media type are not
33 * built here.
34 *
35 * Emitting a binary-mode event from a handler is the response headers plus the body, so no call
36 * covers it: add `ce-id`, `ce-source`, `ce-type` and `ce-specversion`, and write the data as the
37 * body.
38 *
39 * Every string is referenced, never copied, so what a caller sets has to outlive the call that
40 * reads it.
41 *
42 * The module exports one symbol, @ref CloudEvents. Everything in cloudevents.c has internal linkage.
43 *
44 * @author Douglas Quigg (dstroy0)
45 * @date 2026
46 */
47
48#ifndef PROTOCORE_CLOUDEVENTS_H
49#define PROTOCORE_CLOUDEVENTS_H
50
51#include "protocore_config.h" // the entry point: protocore_types.h for the widths
52
53#if PROTOCORE_ENABLE_CLOUDEVENTS
54
56
57#include "network_drivers/presentation/http/http_parser/http_parser.h" // HttpReq: the message a binary read parses
58
59/** @brief The `specversion` every envelope carries (CloudEvents 1.0.2, section "specversion"). */
60#define PROTOCORE_CLOUDEVENTS_SPECVERSION "1.0"
61
62/**
63 * @brief The media type a structured-mode message is carried as (JSON Event Format 1.0.2 sec 3).
64 *
65 * What the HTTP Protocol Binding 1.0.2 sec 3.2.1 requires the `Content-Type` to be set to.
66 */
67#define PROTOCORE_CLOUDEVENTS_MEDIA_TYPE "application/cloudevents+json"
68
69/**
70 * @brief The context attributes one event carries (CloudEvents 1.0.2, section "Context Attributes").
71 *
72 * A build reads these; a binary-mode read writes them. `specversion` is not among them: the module
73 * writes ::PROTOCORE_CLOUDEVENTS_SPECVERSION itself.
74 */
75typedef struct
76{
77 const char *id; ///< REQUIRED `id`: non-empty, unique within the producer's scope
78 const char *source; ///< REQUIRED `source`: non-empty URI-reference naming the producer context
79 const char *type; ///< REQUIRED `type`: non-empty, reverse-DNS prefixed by convention
80 const char *subject; ///< OPTIONAL `subject`: non-empty when present; NULL or "" omits it
81 const char *datacontenttype; ///< OPTIONAL `datacontenttype`: an RFC 2046 media type; NULL or "" omits it
82} CloudEventAttrArgs;
83
84/**
85 * @brief The payload, in the two shapes a JSON serializer takes it in (JSON Event Format 1.0.2 sec 3.1.1).
86 *
87 * At most one is read, @c json first. Both NULL is an event with no `data` (CloudEvents 1.0.2
88 * section "Event Data": OPTIONAL).
89 */
90typedef struct
91{
92 const char *json; ///< `data` as a pre-formatted JSON value, emitted verbatim
93 const char *str; ///< `data` as a plain string, emitted as a JSON string with its escapes
94} CloudEventDataArgs;
95
96/** @brief Where a structured-mode message body lands (HTTP Protocol Binding 1.0.2 sec 3.2). */
97typedef struct
98{
99 char *out; ///< the buffer the JSON object is written into
100 size_t cap; ///< octets that buffer holds, the NUL included
101} CloudEventEnvelopeArgs;
102
103/** @brief The inbound message a binary-mode read parses (HTTP Protocol Binding 1.0.2 sec 3.1). */
104typedef struct
105{
106 const HttpReq *req; ///< the parsed request whose `ce-` prefixed headers carry the attributes
107} CloudEventMessageArgs;
108
109/**
110 * @brief The CloudEvents envelope: structured-mode build, binary-mode read.
111 *
112 * A caller sets the members a call takes, invokes it through ::CloudEvents, and reads the outcome
113 * off the same handle.
114 *
115 * @ref CloudEventsNs::attr is both directions: a build reads the attributes a caller set, and a read
116 * writes the attributes it found on the message. A read clears @ref CloudEventsNs::data, because
117 * binary mode puts the payload in the HTTP body (sec 3.1.2), not in an attribute.
118 *
119 * No slot member: one event is built or read at a time, so no call names a row.
120 *
121 * No storage member: every octet a call touches belongs to the caller or to the request, so nothing
122 * survives a call.
123 *
124 * @var CloudEventsNs::attr the context attributes: a build's input, a read's output
125 * @var CloudEventsNs::data the payload a build serializes under `data`
126 * @var CloudEventsNs::envelope where a structured-mode build writes its JSON object
127 * @var CloudEventsNs::msg the message a binary-mode read takes its attributes off
128 * @var CloudEventsNs::ok a build wrote the whole object, or a read found all three REQUIRED attributes
129 * @var CloudEventsNs::n octets a build wrote, excluding the NUL; 0 when it wrote none
130 * @var CloudEventsNs::build_structured build the one JSON object of Structured Content Mode into @c envelope
131 * @var CloudEventsNs::read_binary take @c attr off the `ce-` prefixed headers of Binary Content Mode
132 *
133 * build_structured emits
134 * `{"specversion":"1.0","id":...,"source":...,"type":...[,"subject":...][,"datacontenttype":...][,"data":...]}`.
135 * It reports 0 when a REQUIRED attribute is absent or empty, when @c envelope names no buffer, or
136 * when the object does not fit @c envelope.cap.
137 *
138 * read_binary points @c attr at the request's own header storage, so the attributes live exactly as
139 * long as the request does. `datacontenttype` comes off `Content-Type`, which sec 3.1.1 makes the
140 * only place it may ride.
141 */
142typedef struct
143{
144 CloudEventAttrArgs attr; ///< what an event says about itself
145 CloudEventDataArgs data; ///< what it carries
146 CloudEventEnvelopeArgs envelope; ///< where a structured build writes
147 CloudEventMessageArgs msg; ///< what a binary read parses
148 proto_bool ok;
149 size_t n;
150} CloudEventsVars;
151
152/** @brief The operands and the outcome. */
153extern CloudEventsVars CloudEventsV;
154
155/** @brief The entries. */
156typedef struct
157{
158 void (*const build_structured)(uint8_t *work);
159 void (*const read_binary)(uint8_t *work);
160} CloudEventsNs;
161
162// What the table binds, defined once in the .c and taking one parameter each: everything
163// else an entry needs is an operand in CloudEventsV or a region of the borrow at a fixed offset.
164void protocore_cloud_events_build_structured(uint8_t *work);
165void protocore_cloud_events_read_binary(uint8_t *work);
166
167// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
168// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
169// `CloudEvents.build_structured(work)` resolves to a named function and becomes a DIRECT call. An extern table
170// leaves the call indirect and the symbol live at every level, -O2 -flto included.
171static const CloudEventsNs CloudEvents __attribute__((unused)) = {
172 .build_structured = protocore_cloud_events_build_structured,
173 .read_binary = protocore_cloud_events_read_binary,
174};
175
177
178#endif // PROTOCORE_ENABLE_CLOUDEVENTS
179
180#endif // PROTOCORE_CLOUDEVENTS_H
HttpParser..
#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