ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
client.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#ifndef PROTOCORE_TCP_CLIENT_H
4#define PROTOCORE_TCP_CLIENT_H
5
6/**
7 * @file client.h
8 * @brief Layer 4 (Transport) - the active OPEN: the outbound client transport.
9 *
10 * RFC 9293 sec 3.9.1.1 gives OPEN an active/passive flag; this is the active side, the dialing
11 * peer of the passive OPEN in server.h.
12 *
13 * A small fixed pool of outbound connections so the application's clients
14 * (services/net/http_client, services/iot/mqtt, services/net/ws_client) no longer each own a
15 * private raw stack at L7. As with the server transport, every raw stack call is marshaled into
16 * the stack's own context (see lower.h), so the main-loop/worker task never races it. All storage
17 * is static (no heap).
18 *
19 * The receive ring carries **wire bytes**: for a plaintext connection those are
20 * the application bytes; for a TLS connection they are ciphertext and the caller
21 * carries plaintext octets: there is no client-side TLS engine in the library, so nothing
22 * BIO at protocore_client_send() / protocore_client_read().
23 *
24 * Nothing here blocks. open() takes a slot, starts the resolve and returns a cid straight away; the
25 * slot carries its own timer and steps from resolving to connected each time the caller asks
26 * connected() or is_closed(). The caller drives that from its own loop, and the whole open - the
27 * name lookup included - is bounded by the timeout_ms it passed.
28 */
29
30#include "protocore_config.h" // the entry point: the enable gate below, and the widths
31
32#if PROTOCORE_ENABLE_TCP_CLIENT
33
35
36/** @brief RFC 9293 sec 3.9.1.1 active OPEN: where a connection is dialled, and how long it may take. */
37typedef struct
38{
39 const char *host; ///< the name it dials; read on every step until it resolves, so it must outlive the open
40 uint16_t port; ///< the port it dials
41 uint32_t timeout_ms; ///< what the whole open, resolve included, is given
42} TcpDialArgs;
43
44/** @brief The bytes a send or a read moves. Nothing a dial reads. */
45typedef struct
46{
47 const void *data; ///< bytes for a send
48 size_t len; ///< how many
49 uint8_t *buf; ///< where a read writes
50 size_t cap; ///< how much room it has
51} TcpClientIoArgs;
52
53/**
54 * @brief The outbound side of TCP.
55 *
56 * A caller sets the members a call takes, invokes it through ::TcpClient, and reads the outcome off
57 * the same handle. The slot pool itself is behind @ref internal.
58 *
59 * @var TcpClientNs::cid the slot a call acts on
60 * @var TcpClientNs::dial what an active OPEN dials
61 * @var TcpClientNs::io the bytes a send or a read moves
62 * @var TcpClientNs::ok a call's true/false outcome
63 * @var TcpClientNs::i32 the cid an open took, or < 0 when none is free
64 * @var TcpClientNs::n a byte count a call reports
65 * @var TcpClientNs::open take a slot and start resolving; cid >= 0, or < 0 when none is free
66 * @var TcpClientNs::connected step the open along, and report whether the handshake completed
67 * @var TcpClientNs::is_closed step the open along, and report a close, an error, or the timeout
68 * @var TcpClientNs::send queue wire bytes for transmission
69 * @var TcpClientNs::available wire bytes buffered and ready to read
70 * @var TcpClientNs::read drain buffered wire bytes
71 * @var TcpClientNs::close tear the connection down and free the slot
72 *
73 * open() returns before the connection exists. @ref host is read on every step until the name
74 * resolves, so it has to outlive the open. The caller polls connected() until it is true, or
75 * is_closed() is, and closes the slot on either failure.
76 */
77typedef struct
78{
79 int cid; ///< the slot every call names
80 TcpDialArgs dial; ///< what an active OPEN dials (RFC 9293 sec 3.9.1.1)
81 TcpClientIoArgs io; ///< the bytes a send or a read moves
82 proto_bool ok;
83 int32_t i32;
84 size_t n;
85} TcpClientVars;
86
87/** @brief The operands and the outcome. */
88extern TcpClientVars TcpClientV;
89
90/** @brief The entries. */
91typedef struct
92{
93 void (*const open)(uint8_t *work);
94 void (*const connected)(uint8_t *work);
95 void (*const is_closed)(uint8_t *work);
96 void (*const send)(uint8_t *work);
97 void (*const available)(uint8_t *work);
98 void (*const read)(uint8_t *work);
99 void (*const close)(uint8_t *work);
100} TcpClientNs;
101
102// What the table binds, defined once in the .c and taking one parameter each: everything
103// else an entry needs is an operand in TcpClientV or a region of the borrow at a fixed offset.
104void protocore_tcp_client_open(uint8_t *work);
105void protocore_tcp_client_connected(uint8_t *work);
106void protocore_tcp_client_is_closed(uint8_t *work);
107void protocore_tcp_client_send(uint8_t *work);
108void protocore_tcp_client_available(uint8_t *work);
109void protocore_tcp_client_read(uint8_t *work);
110void protocore_tcp_client_close(uint8_t *work);
111
112// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
113// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
114// `TcpClient.open(work)` resolves to a named function and becomes a DIRECT call. An extern table
115// leaves the call indirect and the symbol live at every level, -O2 -flto included.
116static const TcpClientNs TcpClient __attribute__((unused)) = {
117 .open = protocore_tcp_client_open,
118 .connected = protocore_tcp_client_connected,
119 .is_closed = protocore_tcp_client_is_closed,
120 .send = protocore_tcp_client_send,
121 .available = protocore_tcp_client_available,
122 .read = protocore_tcp_client_read,
123 .close = protocore_tcp_client_close,
124};
125
126/**
127 * @brief The PROTOCORE_TCP_CLIENT_BORROW bytes this module's state lives in.
128 *
129 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
130 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
131 * walks, so the state lasts the life of the program.
132 *
133 * @return the span.
134 */
135uint8_t *protocore_tcp_client_span(void);
136
138
139#endif // PROTOCORE_ENABLE_TCP_CLIENT
140
141#endif // PROTOCORE_TCP_CLIENT_H
#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