ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
endpoint.c
Go to the documentation of this file.
2#include <ascii-chat/common.h>
4#include "transport.h"
5#include "worker.h"
6
7#define QUIC_ENDPOINT_MAX_TRANSPORTS 64
8
23
34
35static void wake_endpoint(void *context) {
36 quic_endpoint_t *endpoint = context;
37 (void)quic_udp_wake(endpoint->udp);
38}
39
40static void close_connection(quic_connection_t *connection, void *context) {
41 quic_endpoint_t *endpoint = context;
43 (void)quic_udp_close_peer(endpoint->udp, connection);
45}
46
47/* The transport has already detached its receiver. Destroy may be called by
48 * the normal network connection owner after an authenticated transport is adopted. */
49static void transport_destroyed(acip_transport_t *transport, void *context) {
50 quic_endpoint_t *endpoint = context;
51 mutex_lock(&endpoint->lane);
52 endpoint_transport_t **link = &endpoint->transports;
53 while (*link && (*link)->transport != transport)
54 link = &(*link)->next;
55 endpoint_transport_t *item = *link;
56 if (item) {
57 if (item->connection && quic_udp_contains(endpoint->udp, item->connection)) {
58 (void)quic_udp_remove(endpoint->udp, item->connection);
59 }
60 *link = item->next;
61 --endpoint->count;
62 }
63 mutex_unlock(&endpoint->lane);
64 SAFE_FREE(item);
65}
66
67/* Called on the endpoint lane. */
68static asciichat_error_t attach(quic_endpoint_t *endpoint, quic_connection_t *connection, bool claimed,
70 if (endpoint->stopping || endpoint->count >= QUIC_ENDPOINT_MAX_TRANSPORTS)
71 return SET_ERRNO(ERROR_BUFFER_FULL, "QUIC endpoint transport limit reached");
72 endpoint_transport_t *item = SAFE_CALLOC(1, sizeof(*item), endpoint_transport_t *);
73 if (!item)
74 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate QUIC endpoint transport");
76 connection, &endpoint->lane, wake_endpoint, endpoint, transport_destroyed, endpoint, &item->transport);
77 if (result != ASCIICHAT_OK) {
78 SAFE_FREE(item);
79 return result;
80 }
81 quic_transport_set_close_on_lane(item->transport, close_connection, endpoint);
84 item->connection = connection;
85 item->claimed = claimed;
86 item->next = endpoint->transports;
87 endpoint->transports = item;
88 ++endpoint->count;
89 if (out)
90 *out = item->transport;
91 return ASCIICHAT_OK;
92}
93
94static asciichat_error_t accept_connection(quic_connection_t *connection, void *context) {
95 return attach(context, connection, false, NULL);
96}
97
98/* Called by the worker on the lane. Keep failed adapters alive until
99 * their owners release them, but detach the connection before removing it. */
100static bool connection_failed(quic_connection_t *connection, asciichat_error_t reason, void *context) {
101 quic_endpoint_t *endpoint = context;
102 if (!connection) {
103 endpoint->terminal_error = reason;
104 for (endpoint_transport_t *current = endpoint->transports; current; current = current->next) {
105 (void)quic_transport_fail_on_lane(current->transport, reason);
106 if (current->connection && quic_udp_remove(endpoint->udp, current->connection) == ASCIICHAT_OK)
107 current->connection = NULL;
108 }
109 return false;
110 }
111 endpoint_transport_t *item = endpoint->transports;
112 while (item && item->connection != connection)
113 item = item->next;
114 bool handled = !item && !quic_udp_contains(endpoint->udp, connection);
115 if (item && quic_transport_fail_on_lane(item->transport, reason) == ASCIICHAT_OK &&
116 quic_udp_remove(endpoint->udp, connection) == ASCIICHAT_OK) {
117 item->connection = NULL;
119 endpoint_transport_t **link = &endpoint->transports;
120 while (*link != item)
121 link = &(*link)->next;
122 *link = item->next;
123 --endpoint->count;
124 SAFE_FREE(item);
125 }
126 handled = true;
127 }
128 return handled;
129}
130
131asciichat_error_t quic_endpoint_open(const struct sockaddr *address, socklen_t length, bool listener,
132 const quic_config_t *config, quic_endpoint_t **out) {
133 if (!out)
134 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC endpoint output");
135 *out = NULL;
136 if (!address || !config)
137 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC endpoint configuration");
138 quic_endpoint_t *endpoint = SAFE_CALLOC(1, sizeof(*endpoint), quic_endpoint_t *);
139 if (!endpoint)
140 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate QUIC endpoint");
141 if (mutex_init(&endpoint->lane, "quic_endpoint") != 0) {
142 SAFE_FREE(endpoint);
143 return SET_ERRNO(ERROR_THREAD, "Cannot initialize QUIC endpoint lane");
144 }
145 bool webtransport = config->webtransport_origin || config->webtransport_path;
146 bool http3 = webtransport || config->enable_http3 || config->http_server;
147 if (http3 && config->alpn && strcmp(config->alpn, "h3") != 0) {
148 mutex_destroy(&endpoint->lane);
149 SAFE_FREE(endpoint);
150 return SET_ERRNO(ERROR_INVALID_PARAM, "HTTP/3 requires the h3 ALPN");
151 }
152 if (webtransport &&
153 (!listener || !config->enable_datagrams || !config->webtransport_origin || !config->webtransport_path ||
154 strncmp(config->webtransport_origin, "https://", sizeof("https://") - 1) != 0 ||
155 config->webtransport_path[0] != '/' ||
156 strnlen(config->webtransport_origin, sizeof(endpoint->tls->webtransport_origin)) >=
157 sizeof(endpoint->tls->webtransport_origin) ||
158 strnlen(config->webtransport_path, sizeof(endpoint->tls->webtransport_path)) >=
159 sizeof(endpoint->tls->webtransport_path) ||
160 (config->alpn && strcmp(config->alpn, "h3") != 0))) {
161 mutex_destroy(&endpoint->lane);
162 SAFE_FREE(endpoint);
163 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid WebTransport listener configuration");
164 }
165 const char *alpn = config->alpn ? config->alpn : http3 ? "h3" : "acip/1";
166 asciichat_error_t result =
167 listener ? quic_tls_server_create(alpn, config->certificate_file, config->private_key_file, &endpoint->tls)
168 : quic_tls_client_create(alpn, config->ca_file, &endpoint->tls);
169 if (result == ASCIICHAT_OK) {
170 endpoint->tls->enable_datagrams = config->enable_datagrams;
171 endpoint->tls->webtransport = webtransport;
172 endpoint->tls->http3 = http3;
173 endpoint->tls->accept_native = webtransport && config->accept_native;
174 endpoint->tls->http_server = config->http_server;
175 if (endpoint->tls->accept_native) {
176 static const unsigned char protocols[] = {2, 'h', '3', 6, 'a', 'c', 'i', 'p', '/', '1'};
177 memcpy(endpoint->tls->alpn, protocols, sizeof(protocols));
178 endpoint->tls->alpn_length = sizeof(protocols);
179 }
180 if (webtransport) {
181 memcpy(endpoint->tls->webtransport_origin, config->webtransport_origin,
182 strlen(config->webtransport_origin) + (size_t)1);
183 memcpy(endpoint->tls->webtransport_path, config->webtransport_path,
184 strlen(config->webtransport_path) + (size_t)1);
185 }
188 endpoint->listener = listener;
189 result = quic_udp_endpoint_create(address, length, listener ? endpoint->tls : NULL,
190 listener ? accept_connection : NULL, listener ? endpoint : NULL, &endpoint->udp);
191 }
192 if (result == ASCIICHAT_OK)
193 result = quic_worker_start_locked_failure(endpoint->udp, &endpoint->lane, connection_failed, endpoint,
194 &endpoint->worker);
195 if (result != ASCIICHAT_OK) {
196 quic_udp_endpoint_destroy(&endpoint->udp);
197 quic_tls_destroy(&endpoint->tls);
198 mutex_destroy(&endpoint->lane);
199 SAFE_FREE(endpoint);
200 return result;
201 }
202 *out = endpoint;
203 return ASCIICHAT_OK;
204}
205
207 if (!endpoint || !endpoint->listener || !endpoint->tls || !endpoint->tls->http3)
208 return SET_ERRNO(ERROR_INVALID_PARAM, "Endpoint is not an HTTP/3 listener");
209 mutex_lock(&endpoint->lane);
211 if (endpoint->stopping || endpoint->count)
212 result = SET_ERRNO(ERROR_INVALID_STATE, "HTTP/3 handler must be attached before connections");
213 else
214 endpoint->tls->http_server = server;
215 mutex_unlock(&endpoint->lane);
216 return result;
217}
218
219asciichat_error_t quic_endpoint_address(quic_endpoint_t *endpoint, struct sockaddr_storage *address,
220 socklen_t *length) {
221 if (!endpoint)
222 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC endpoint");
223 mutex_lock(&endpoint->lane);
224 asciichat_error_t result = quic_udp_endpoint_address(endpoint->udp, address, length);
225 mutex_unlock(&endpoint->lane);
226 return result;
227}
228
229asciichat_error_t quic_connect(quic_endpoint_t *endpoint, const struct sockaddr *remote, socklen_t remote_length,
230 const char *server_name, acip_transport_t **out) {
231 if (!out)
232 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC transport output");
233 *out = NULL;
234 if (!endpoint || endpoint->listener || !remote || !server_name || !*server_name)
235 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC connect configuration");
236 mutex_lock(&endpoint->lane);
237 quic_connection_t *connection = NULL;
238 asciichat_error_t result = endpoint->stopping ? SET_ERRNO(ERROR_INVALID_STATE, "QUIC endpoint stopped")
239 : endpoint->terminal_error ? SET_ERRNO(endpoint->terminal_error, "QUIC endpoint failed")
241 ? SET_ERRNO(ERROR_BUFFER_FULL, "QUIC endpoint transport limit reached")
242 : quic_udp_connect(endpoint->udp, endpoint->tls, server_name, remote, remote_length,
243 time_get_ns(), &connection);
244 if (result == ASCIICHAT_OK) {
245 result = attach(endpoint, connection, true, out);
246 if (result != ASCIICHAT_OK)
247 (void)quic_udp_remove(endpoint->udp, connection);
248 }
249 mutex_unlock(&endpoint->lane);
250 if (result == ASCIICHAT_OK)
251 wake_endpoint(endpoint);
252 return result;
253}
254
256 if (!out)
257 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing QUIC accept output");
258 *out = NULL;
259 if (!endpoint || !endpoint->listener)
260 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC listener");
261 mutex_lock(&endpoint->lane);
262 endpoint_transport_t *item = endpoint->transports;
263 bool generic_http3 = endpoint->tls->http3 && !endpoint->tls->webtransport;
264 while (item && (item->claimed || generic_http3 ||
266 item = item->next;
268 if (item) {
269 item->claimed = true;
270 *out = item->transport;
271 } else {
272 result = endpoint->terminal_error ? SET_ERRNO(endpoint->terminal_error, "QUIC endpoint failed")
273 : endpoint->stopping ? SET_ERRNO(ERROR_INVALID_STATE, "QUIC endpoint stopped")
274 : SET_ERRNO(ERROR_NETWORK_TIMEOUT, "No QUIC connection is available");
275 }
276 mutex_unlock(&endpoint->lane);
277 return result;
278}
279
281 if (!endpoint || !transport || !*transport)
282 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC transport release");
283 mutex_lock(&endpoint->lane);
284 endpoint_transport_t **link = &endpoint->transports;
285 while (*link && (*link)->transport != *transport)
286 link = &(*link)->next;
287 if (!*link || !(*link)->claimed) {
288 mutex_unlock(&endpoint->lane);
289 return SET_ERRNO(ERROR_INVALID_PARAM, "Transport does not belong to QUIC endpoint");
290 }
291 endpoint_transport_t *item = *link;
293 if (result == ASCIICHAT_OK && item->connection)
294 result = quic_udp_remove(endpoint->udp, item->connection);
295 if (result == ASCIICHAT_OK) {
296 *link = item->next;
297 --endpoint->count;
298 }
299 mutex_unlock(&endpoint->lane);
300 if (result != ASCIICHAT_OK)
301 return result;
303 SAFE_FREE(item);
304 *transport = NULL;
305 return ASCIICHAT_OK;
306}
307
309 if (!endpoint)
310 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC endpoint");
311 if (endpoint->stopped)
312 return ASCIICHAT_OK;
313 mutex_lock(&endpoint->lane);
314 endpoint->stopping = true;
315 mutex_unlock(&endpoint->lane);
316 asciichat_error_t worker_result = endpoint->worker ? quic_worker_stop(&endpoint->worker) : ASCIICHAT_OK;
317 if (endpoint->worker)
318 return worker_result;
320 for (;;) {
321 mutex_lock(&endpoint->lane);
322 endpoint_transport_t *item = endpoint->transports;
323 while (item && item->claimed)
324 item = item->next;
325 if (item)
326 item->claimed = true;
327 mutex_unlock(&endpoint->lane);
328 if (!item)
329 break;
330 acip_transport_t *transport = item->transport;
331 result = quic_release(endpoint, &transport);
332 if (result != ASCIICHAT_OK)
333 return result;
334 }
335 mutex_lock(&endpoint->lane);
336 for (endpoint_transport_t *item = endpoint->transports; item; item = item->next) {
338 if (result != ASCIICHAT_OK)
339 break;
340 if (item->connection) {
341 result = quic_udp_remove(endpoint->udp, item->connection);
342 if (result != ASCIICHAT_OK)
343 break;
344 item->connection = NULL;
345 }
346 }
347 if (result == ASCIICHAT_OK)
348 endpoint->stopped = true;
349 mutex_unlock(&endpoint->lane);
350 return result == ASCIICHAT_OK ? worker_result : result;
351}
352
354 if (!endpoint || !*endpoint)
355 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC endpoint destruction");
356 if (!(*endpoint)->stopped || (*endpoint)->count)
357 return SET_ERRNO(ERROR_INVALID_STATE, "Stop QUIC endpoint and release all transports first");
358 quic_udp_endpoint_destroy(&(*endpoint)->udp);
359 quic_tls_destroy(&(*endpoint)->tls);
360 mutex_destroy(&(*endpoint)->lane);
361 SAFE_FREE(*endpoint);
362 *endpoint = NULL;
363 return ASCIICHAT_OK;
364}
365
366asciichat_error_t quic_endpoint_migrate(quic_endpoint_t *endpoint, const struct sockaddr *local_address,
367 socklen_t length) {
368 if (!endpoint || endpoint->listener || !local_address)
369 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid QUIC client endpoint migration");
370 mutex_lock(&endpoint->lane);
371 bool available = !endpoint->stopping && !endpoint->terminal_error && endpoint->count == 1 &&
372 endpoint->transports->connection && endpoint->worker;
373 mutex_unlock(&endpoint->lane);
374 if (!available)
375 return SET_ERRNO(ERROR_INVALID_STATE, "QUIC migration requires one live client connection");
376 asciichat_error_t result = quic_worker_stop(&endpoint->worker);
377 if (result != ASCIICHAT_OK)
378 return result;
379 mutex_lock(&endpoint->lane);
380 result = quic_udp_migrate(endpoint->udp, local_address, length, time_get_ns());
381 asciichat_error_t restart =
382 quic_worker_start_locked_failure(endpoint->udp, &endpoint->lane, connection_failed, endpoint, &endpoint->worker);
383 if (restart != ASCIICHAT_OK)
384 (void)connection_failed(NULL, restart, endpoint);
385 mutex_unlock(&endpoint->lane);
386 return restart != ASCIICHAT_OK ? restart : result;
387}
void errno_scope_end(asciichat_errno_scope_t scope, asciichat_errno_outcome_t outcome)
asciichat_errno_scope_t errno_scope_begin(void)
#define SET_ERRNO(code,...)
@ ASCIICHAT_ERRNO_DISMISSED
⚙️ Common definitions, error codes, macros, and types shared throughout the application
asciichat_error_t quic_release(quic_endpoint_t *endpoint, acip_transport_t **transport)
Definition endpoint.c:280
#define QUIC_ENDPOINT_MAX_TRANSPORTS
Definition endpoint.c:7
asciichat_error_t quic_accept(quic_endpoint_t *endpoint, acip_transport_t **out)
Definition endpoint.c:255
asciichat_error_t quic_connect(quic_endpoint_t *endpoint, const struct sockaddr *remote, socklen_t remote_length, const char *server_name, acip_transport_t **out)
Definition endpoint.c:229
asciichat_error_t quic_endpoint_address(quic_endpoint_t *endpoint, struct sockaddr_storage *address, socklen_t *length)
Definition endpoint.c:219
struct endpoint_transport endpoint_transport_t
This is the handoff point for acip quic endpoint. Callers fill lane, tls, udp, worker before passing ...
asciichat_error_t quic_endpoint_migrate(quic_endpoint_t *endpoint, const struct sockaddr *local_address, socklen_t length)
Definition endpoint.c:366
asciichat_error_t quic_endpoint_open(const struct sockaddr *address, socklen_t length, bool listener, const quic_config_t *config, quic_endpoint_t **out)
Definition endpoint.c:131
asciichat_error_t quic_endpoint_stop(quic_endpoint_t *endpoint)
Definition endpoint.c:308
asciichat_error_t quic_endpoint_set_http_server(quic_endpoint_t *endpoint, http_server_t *server)
Definition endpoint.c:206
asciichat_error_t quic_endpoint_destroy(quic_endpoint_t **endpoint)
Definition endpoint.c:353
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:72
@ ERROR_BUFFER_FULL
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:75
@ ERROR_THREAD
struct acip_transport acip_transport_t
Forward declaration of ACIP transport.
Definition host.h:506
struct quic_endpoint quic_endpoint_t
bool quic_connection_accept_ready(const quic_connection_t *connection)
struct quic_connection quic_connection_t
static bool size_t gotel_query_t * out
Definition queries.h:8
void quic_transport_set_close_on_lane(acip_transport_t *transport, void(*close)(quic_connection_t *, void *), void *context)
asciichat_error_t quic_transport_attach_on_lane_with_waker(quic_connection_t *connection, mutex_t *lane, void(*wake)(void *), void *context, void(*on_destroy)(acip_transport_t *, void *), void *destroy_context, acip_transport_t **out)
asciichat_error_t quic_transport_destroy_unclaimed_on_lane(acip_transport_t **transport, asciichat_error_t reason)
asciichat_error_t quic_transport_fail_on_lane(acip_transport_t *transport, asciichat_error_t reason)
bool prefer_visual_datagrams
Select negotiated visual datagrams when supported.
bool prefer_audio_datagrams
Select negotiated datagrams for audio, otherwise reliable delivery.
When code needs to work with asciichat errno snapshot, it keeps total, copied here instead of scatter...
This is the handoff point for acip quic endpoint. Callers fill lane, tls, udp, worker before passing ...
Definition endpoint.c:17
struct endpoint_transport * next
Definition endpoint.c:18
quic_connection_t * connection
Definition endpoint.c:19
acip_transport_t * transport
Definition endpoint.c:20
bool prefer_visual_datagrams
Definition quic.h:22
const char * private_key_file
Definition quic.h:16
const char * ca_file
Definition quic.h:17
bool enable_datagrams
Definition quic.h:20
http_server_t * http_server
Definition quic.h:26
const char * alpn
Definition quic.h:14
bool prefer_audio_datagrams
Definition quic.h:23
bool accept_native
Definition quic.h:21
const char * certificate_file
Definition quic.h:15
bool enable_http3
Definition quic.h:24
const char * webtransport_path
Definition quic.h:19
const char * webtransport_origin
Definition quic.h:18
bool prefer_visual_datagrams
Definition endpoint.c:31
mutex_t lane
Definition endpoint.c:25
bool prefer_audio_datagrams
Definition endpoint.c:31
endpoint_transport_t * transports
Definition endpoint.c:29
bool listener
Definition endpoint.c:31
quic_tls_t * tls
Definition endpoint.c:26
size_t count
Definition endpoint.c:30
bool stopping
Definition endpoint.c:31
quic_udp_endpoint_t * udp
Definition endpoint.c:27
quic_worker_t * worker
Definition endpoint.c:28
asciichat_error_t terminal_error
Definition endpoint.c:32
bool accept_native
Definition tls.h:20
unsigned char alpn[256]
Definition tls.h:13
char webtransport_origin[512]
Definition tls.h:22
unsigned int alpn_length
Definition tls.h:14
char webtransport_path[512]
Definition tls.h:23
bool http3
Definition tls.h:19
http_server_t * http_server
Definition tls.h:21
bool enable_datagrams
Definition tls.h:17
bool webtransport
Definition tls.h:18
void acip_transport_destroy(acip_transport_t *transport)
Destroy transport and free all resources.
⏱️ High-precision timing utilities using sokol_time.h and uthash
asciichat_error_t quic_tls_client_create(const char *alpn, const char *ca_file, quic_tls_t **out)
Definition tls.c:90
void quic_tls_destroy(quic_tls_t **tls)
Definition tls.c:202
asciichat_error_t quic_tls_server_create(const char *alpn, const char *certificate_file, const char *private_key_file, quic_tls_t **out)
Definition tls.c:67
struct http_server http_server_t
Definition tls.h:8
bool quic_udp_contains(const quic_udp_endpoint_t *endpoint, const quic_connection_t *connection)
Definition udp.c:330
asciichat_error_t quic_udp_endpoint_address(const quic_udp_endpoint_t *endpoint, struct sockaddr_storage *address, socklen_t *length)
Definition udp.c:106
asciichat_error_t quic_udp_wake(quic_udp_endpoint_t *endpoint)
Definition udp.c:319
void quic_udp_endpoint_destroy(quic_udp_endpoint_t **endpoint)
Definition udp.c:372
asciichat_error_t quic_udp_endpoint_create(const struct sockaddr *address, socklen_t length, quic_tls_t *server_tls, quic_udp_accept_fn accept, void *context, quic_udp_endpoint_t **out)
Definition udp.c:67
asciichat_error_t quic_udp_migrate(quic_udp_endpoint_t *endpoint, const struct sockaddr *address, socklen_t length, uint64_t now_ns)
Definition udp.c:383
asciichat_error_t quic_udp_close_peer(quic_udp_endpoint_t *endpoint, quic_connection_t *connection)
Definition udp.c:339
asciichat_error_t quic_udp_connect(quic_udp_endpoint_t *endpoint, quic_tls_t *client_tls, const char *server_name, const struct sockaddr *remote, socklen_t remote_length, uint64_t now_ns, quic_connection_t **out)
Definition udp.c:144
asciichat_error_t quic_udp_remove(quic_udp_endpoint_t *endpoint, quic_connection_t *connection)
Definition udp.c:356
struct quic_udp_endpoint quic_udp_endpoint_t
Definition udp.h:14
asciichat_error_t quic_worker_start_locked_failure(quic_udp_endpoint_t *endpoint, mutex_t *lane, quic_worker_failure_fn failure, void *context, quic_worker_t **out)
Definition worker.c:107
asciichat_error_t quic_worker_stop(quic_worker_t **worker)
Definition worker.c:118
struct quic_worker quic_worker_t
Definition worker.h:11