ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
lib/network/http/server/server.c
Go to the documentation of this file.
3#include "server_internal.h"
4#include "http2.h"
5#include <ascii-chat/common.h>
6#include <libplatform/atomic.h>
7#include <libplatform/thread.h>
10#include <ascii-chat/otel.h>
13#include <uv.h>
14#include <openssl/err.h>
15#include <openssl/ssl.h>
16#include <string.h>
17
18#define HTTP_READ_CAPACITY (1024u * 1024u)
19#define HTTP_HEADER_MAX_BYTES (64u * 1024u)
20#define HTTP_HEADER_CAPACITY 64u
21
22static const char *http_protocol_name(http_protocol_t protocol) {
23 switch (protocol) {
25 return "1.0";
27 return "1.1";
28 case HTTP_PROTOCOL_2:
29 return "2";
30 case HTTP_PROTOCOL_3:
31 return "3";
32 default:
33 return "unknown";
34 }
35}
36
45static void write_done(uv_write_t *req, int status);
46static void connection_cb(uv_stream_t *listener, int status);
47static asciichat_error_t http2_request_cb(http2_connection_t *connection, const http2_request_view_t *view,
48 void *user_data);
49static asciichat_error_t http2_flush(http_server_client_t *client);
50
107
131static size_t http_max_response_body(const http_server_t *server) {
134}
136 return server ? http_max_response_body(server) : 0;
137}
139 if (!server || !context)
140 return;
141 mutex_lock(&server->response_mutex);
142 for (http_response_t *response = server->retained_responses; response; response = response->next_retained) {
143 if (response->http3_context == context) {
144 response->http3_submit = NULL;
145 response->http3_context = NULL;
146 response->cancelled = true;
147 }
148 }
149 mutex_unlock(&server->response_mutex);
150}
151static void stop_async_cb(uv_async_t *async);
152static void http_server_detach_http3(http_server_t *s) {
153 if (s->http3_attached) {
155 s->http3_attached = false;
156 }
157}
158static void http_server_rollback_start(http_server_t *s) {
159 http_server_detach_http3(s);
160 s->running = false;
162 stop_async_cb(&s->stop_async);
163 while (uv_run(&s->loop, UV_RUN_NOWAIT) != 0)
164 ;
165}
166static asciichat_error_t http_server_start_listener(http_server_t *s) {
167 if (s->config.enable_http3) {
169 if (result != ASCIICHAT_OK)
170 return SET_ERRNO(ERROR_NETWORK, "Cannot attach shared HTTP/3 handler");
171 s->http3_attached = true;
172 }
173 bool tcp_enabled = s->config.enable_http1 || s->config.enable_http2;
174 if (tcp_enabled && !s->adopted_socket) {
175 const char *bind_address = s->config.bind_address ? s->config.bind_address : "0.0.0.0";
176 struct sockaddr_storage address;
177 int rc;
178 if (strchr(bind_address, ':')) {
179 struct sockaddr_in6 ipv6;
180 rc = uv_ip6_addr(bind_address, s->config.port, &ipv6);
181 memcpy(&address, &ipv6, sizeof(ipv6));
182 } else {
183 struct sockaddr_in ipv4;
184 rc = uv_ip4_addr(bind_address, s->config.port, &ipv4);
185 memcpy(&address, &ipv4, sizeof(ipv4));
186 }
187 if (rc || uv_tcp_bind(&s->listener, (const struct sockaddr *)&address, 0) != 0) {
188 http_server_rollback_start(s);
189 return SET_ERRNO(ERROR_NETWORK, "Cannot bind HTTP listener");
190 }
191 if (s->config.bind_address6 && *s->config.bind_address6 && !strchr(bind_address, ':')) {
192 struct sockaddr_in6 ipv6;
193 if (uv_ip6_addr(s->config.bind_address6, s->config.port, &ipv6) != 0 ||
194 uv_tcp_bind(&s->listener6, (const struct sockaddr *)&ipv6, UV_TCP_IPV6ONLY) != 0) {
195 http_server_rollback_start(s);
196 return SET_ERRNO(ERROR_NETWORK, "Cannot bind IPv6 HTTP listener");
197 }
198 }
199 }
200 if (tcp_enabled && uv_listen((uv_stream_t *)&s->listener, (int)s->config.max_connections, connection_cb) != 0) {
201 http_server_rollback_start(s);
202 return SET_ERRNO(ERROR_NETWORK, "Cannot listen for HTTP connections");
203 }
204 if (tcp_enabled && s->listener6_initialized && s->config.bind_address6 && *s->config.bind_address6 &&
205 !strchr(s->config.bind_address ? s->config.bind_address : "0.0.0.0", ':') &&
206 uv_listen((uv_stream_t *)&s->listener6, (int)s->config.max_connections, connection_cb) != 0) {
207 http_server_rollback_start(s);
208 return SET_ERRNO(ERROR_NETWORK, "Cannot listen on IPv6 HTTP listener");
209 }
210 return ASCIICHAT_OK;
211}
212static void wake_async_cb(uv_async_t *async) {
213 http_server_t *s = async->data;
214 if (s->config.on_wakeup)
216}
217static size_t http_max_inflight(const http_server_t *server) {
218 if (server->config.max_inflight_requests)
219 return server->config.max_inflight_requests;
220 return server->config.max_connections > SIZE_MAX / 4 ? SIZE_MAX : server->config.max_connections * 4;
221}
222static bool http_request_admit(http_server_t *server) {
223 int current = atomic_fetch_add_int(&server->inflight_requests, 1);
224 size_t max = http_max_inflight(server);
225 if (current < 0 || (size_t)current >= max) {
226 atomic_fetch_sub_int(&server->inflight_requests, 1);
227 return false;
228 }
229 return true;
230}
231static void http_request_release(http_server_t *server) {
232 if (server)
233 atomic_fetch_sub_int(&server->inflight_requests, 1);
234}
235
236static void client_freed(uv_handle_t *handle) {
237 http_server_client_t *c = handle->data;
238 mutex_lock(&c->server->response_mutex);
239 for (http_response_t *response = c->server->retained_responses; response; response = response->next_retained) {
240 if (response->client == c && !response->cancelled) {
241 response->cancelled = true;
243 }
244 }
245 mutex_unlock(&c->server->response_mutex);
246 if (c->previous)
247 c->previous->next = c->next;
248 else
249 c->server->clients = c->next;
250 if (c->next)
251 c->next->previous = c->previous;
252 if (c->admitted && c->server->connections)
253 c->server->connections--;
254 if (c->admitted)
256 if (c->admitted && c->server->config.on_connection)
259 SSL_free(c->tls);
260 SAFE_FREE(c->buffer);
261 SAFE_FREE(c);
262}
263
264static int http_select_alpn(SSL *ssl, const unsigned char **out, unsigned char *out_length,
265 const unsigned char *offered, unsigned int offered_length, void *arg) {
266 http_server_t *server = arg;
267 unsigned char protocols[sizeof("h2") + sizeof("http/1.1")];
268 unsigned int length = 0;
269 if (server->config.enable_http2) {
270 protocols[length++] = 2;
271 memcpy(protocols + length, "h2", 2);
272 length += 2;
273 }
274 if (server->config.enable_http1) {
275 protocols[length++] = 8;
276 memcpy(protocols + length, "http/1.1", 8);
277 length += 8;
278 }
279 if (!length || SSL_select_next_proto((unsigned char **)out, out_length, protocols, length, offered, offered_length) !=
280 OPENSSL_NPN_NEGOTIATED)
281 return SSL_TLSEXT_ERR_NOACK;
282 (void)ssl;
283 return SSL_TLSEXT_ERR_OK;
284}
285
286static int tls_password_cb(char *buf, int size, int rwflag, void *userdata) {
287 (void)rwflag;
288 const char *password = userdata;
289 if (!password || size <= 0)
290 return 0;
291 size_t length = strlen(password);
292 if (length >= (size_t)size)
293 return 0;
294 memcpy(buf, password, length);
295 return (int)length;
296}
297
298static bool tls_flush(http_server_client_t *c) {
299 char buffer[16 * 1024];
300 while (c->tls && BIO_pending(SSL_get_wbio(c->tls)) > 0) {
301 int n = BIO_read(SSL_get_wbio(c->tls), buffer, sizeof(buffer));
302 if (n <= 0)
303 return false;
304 char *copy = SAFE_MALLOC((size_t)n, char *);
305 uv_write_t *request = SAFE_CALLOC(1, sizeof(*request), uv_write_t *);
306 http_write_t *write = SAFE_CALLOC(1, sizeof(*write), http_write_t *);
307 if (!copy || !request || !write) {
308 SAFE_FREE(copy);
309 SAFE_FREE(request);
310 SAFE_FREE(write);
311 return false;
312 }
313 memcpy(copy, buffer, (size_t)n);
314 write->bytes = copy;
315 write->length = (size_t)n;
316 write->stream = (uv_stream_t *)&c->tcp;
317 write->client = c;
318 request->data = write;
319 uv_buf_t b = uv_buf_init(copy, n);
320 if (uv_write(request, write->stream, &b, 1, write_done) != 0) {
321 SAFE_FREE(copy);
322 SAFE_FREE(request);
323 SAFE_FREE(write);
324 return false;
325 }
326 c->pending_writes++;
327 }
328 return true;
329}
330
331static bool tls_handshake(http_server_client_t *c) {
332 int result = SSL_accept(c->tls);
333 if (result != 1) {
334 int error = SSL_get_error(c->tls, result);
335 if (error != SSL_ERROR_WANT_READ && error != SSL_ERROR_WANT_WRITE)
336 return false;
337 }
338 return tls_flush(c);
339}
340static void client_close(http_server_client_t *c) {
341 if (!uv_is_closing((uv_handle_t *)&c->tcp))
342 uv_close((uv_handle_t *)&c->tcp, client_freed);
343}
344static void client_peer_address(http_server_client_t *c) {
345 struct sockaddr_storage address;
346 int length = sizeof(address);
347 if (uv_tcp_getpeername(&c->tcp, (struct sockaddr *)&address, &length) != 0) {
348 safe_snprintf(c->peer_address, sizeof(c->peer_address), "unknown");
349 return;
350 }
351 if (address.ss_family == AF_INET)
352 uv_ip4_name((const struct sockaddr_in *)&address, c->peer_address, sizeof(c->peer_address));
353 else if (address.ss_family == AF_INET6)
354 uv_ip6_name((const struct sockaddr_in6 *)&address, c->peer_address, sizeof(c->peer_address));
355 else
356 safe_snprintf(c->peer_address, sizeof(c->peer_address), "unknown");
357}
358static void alloc_buffer(uv_handle_t *h, size_t n, uv_buf_t *b) {
359 (void)h;
360 b->base = SAFE_MALLOC(n, char *);
361 b->len = (unsigned)n;
362}
363static bool decode_chunked_body(http_server_client_t *c, size_t offset, size_t *body_length) {
364 size_t limit = c->server->config.max_request_body_bytes;
365 if (!c->chunked_initialized) {
366 c->chunked_initialized = true;
367 c->chunk_size = 0;
368 c->chunk_trailers = false;
371 c->chunk_decoded = NULL;
372 }
373 for (;;) {
374 if (c->chunk_size) {
375 size_t available = c->length - offset;
376 size_t take = available < c->chunk_size ? available : c->chunk_size;
377 if (take) {
378 if (c->chunk_decoded_length > limit - take) {
379 c->request_too_large = true;
380 goto invalid;
381 }
382 size_t required = c->chunk_decoded_length + take;
383 if (required > c->chunk_decoded_capacity) {
384 size_t capacity = c->chunk_decoded_capacity ? c->chunk_decoded_capacity : 4096;
385 while (capacity < required) {
386 if (capacity > limit / 2) {
387 capacity = limit;
388 break;
389 }
390 capacity *= 2;
391 }
392 uint8_t *decoded = SAFE_REALLOC(c->chunk_decoded, capacity, uint8_t *);
393 if (!decoded)
394 goto invalid;
395 c->chunk_decoded = decoded;
396 c->chunk_decoded_capacity = capacity;
397 }
398 memcpy(c->chunk_decoded + c->chunk_decoded_length, c->buffer + offset, take);
399 c->chunk_decoded_length += take;
400 c->chunk_size -= take;
401 memmove(c->buffer + offset, c->buffer + offset + take, available - take);
402 c->length -= take;
403 c->buffer[c->length] = 0;
404 }
405 if (c->chunk_size)
406 return false;
407 if (c->length - offset < sizeof("\r\n") - 1)
408 return false;
409 if (memcmp(c->buffer + offset, "\r\n", sizeof("\r\n") - 1) != 0)
410 goto invalid;
411 memmove(c->buffer + offset, c->buffer + offset + 2, c->length - offset - 2);
412 c->length -= 2;
413 c->buffer[c->length] = 0;
414 }
415 char *line_end = strstr(c->buffer + offset, "\r\n");
416 if (!line_end)
417 return false;
418 if (line_end == c->buffer + offset) {
419 *body_length = c->chunk_decoded_length;
420 if (*body_length)
421 memcpy(c->buffer + offset, c->chunk_decoded, *body_length);
422 c->length = offset + *body_length;
423 c->buffer[c->length] = 0;
425 c->chunked_initialized = false;
426 return true;
427 }
428 if (c->chunk_trailers) {
429 size_t line_length = (size_t)(line_end - (c->buffer + offset)) + 2;
430 memmove(c->buffer + offset, c->buffer + offset + line_length, c->length - offset - line_length);
431 c->length -= line_length;
432 c->buffer[c->length] = 0;
433 continue;
434 }
435 char *number_end = NULL;
436 unsigned long long chunk = strtoull(c->buffer + offset, &number_end, 16);
437 if (number_end == c->buffer + offset || chunk > SIZE_MAX || (number_end != line_end && *number_end != ';'))
438 goto invalid;
439 size_t line_length = (size_t)(line_end - (c->buffer + offset)) + 2;
440 memmove(c->buffer + offset, c->buffer + offset + line_length, c->length - offset - line_length);
441 c->length -= line_length;
442 c->buffer[c->length] = 0;
443 if (chunk > limit || c->chunk_decoded_length > limit - (size_t)chunk) {
444 c->request_too_large = true;
445 goto invalid;
446 }
447 if (chunk == 0) {
448 c->chunk_trailers = true;
449 continue;
450 }
451 c->chunk_size = (size_t)chunk;
452 }
453invalid:
455 c->chunked_initialized = false;
456 return false;
457}
458static bool parse_request(http_server_client_t *c, http_request_t *r) {
459 char *end = strstr(c->buffer, "\r\n\r\n");
460 if (!end)
461 return false;
462 size_t head = (size_t)(end - c->buffer) + 4;
463 size_t body = 0;
464 bool chunked = false;
465 bool transfer_encoding = false;
466 for (char *scan = strstr(c->buffer, "\r\n") + 2; scan < end;) {
467 char *next = strstr(scan, "\r\n");
468 if (!next)
469 return false;
470 if (platform_strncasecmp(scan, "Content-Length:", 15) == 0)
471 body = (size_t)strtoull(scan + 15, NULL, 10);
472 if (platform_strncasecmp(scan, "Transfer-Encoding:", 18) == 0) {
473 transfer_encoding = true;
474 const char *value = scan + 18;
475 while (*value && *value != '\r' && *value != '\n') {
476 while (*value == ' ' || *value == '\t' || *value == ',')
477 value++;
478 const char *token = value;
479 while (*value && *value != ',' && *value != ';' && *value != '\r' && *value != '\n' && *value != ' ' &&
480 *value != '\t')
481 value++;
482 size_t token_length = (size_t)(value - token);
483 if (token_length == sizeof("chunked") - 1 && platform_strncasecmp(token, "chunked", token_length) == 0)
484 chunked = true;
485 else if (token_length)
486 return false;
487 while (*value == ' ' || *value == '\t' || *value == ';')
488 value++;
489 }
490 }
491 scan = next + 2;
492 }
493 if (transfer_encoding && !chunked)
494 return false;
495 if (chunked) {
496 if (!decode_chunked_body(c, head, &body))
497 return false;
498 } else if (body > c->server->config.max_request_body_bytes || c->length < head + body)
499 return false;
500 char *line = c->buffer;
501 char *nl = strstr(line, "\r\n");
502 if (!nl)
503 return false;
504 *nl = 0;
505 char *a = strchr(line, ' '), *b = a ? strchr(a + 1, ' ') : NULL;
506 if (!a || !b)
507 return false;
508 *a = 0;
509 *b = 0;
510 r->storage = c->buffer;
511 r->storage_length = c->length;
512 r->info.method = line;
513 r->info.path = a + 1;
514 if (strcmp(b + 1, "HTTP/1.0") == 0)
516 else if (strcmp(b + 1, "HTTP/1.1") == 0)
518 else
519 return false;
520 char *q = strchr(r->info.path, '?');
521 if (q) {
522 *q = 0;
523 r->info.query = q + 1;
524 }
525 char *p = nl + 2;
526 r->info.header_count = 0;
527 while (p < end && *p) {
528 char *next = strstr(p, "\r\n");
529 if (!next)
530 return false;
531 *next = 0;
532 char *colon = strchr(p, ':');
533 if (colon && r->info.header_count < HTTP_HEADER_CAPACITY) {
534 *colon = 0;
535 while (colon[1] == ' ')
536 colon++;
537 r->headers[r->info.header_count] = (http_header_t){p, colon + 1};
538 r->info.header_count++;
539 }
540 p = next + 2;
541 }
542 r->body_offset = head;
543 r->body_length = body;
544 r->info.headers = r->headers;
548 return true;
549}
550
551static void write_done(uv_write_t *req, int status) {
552 http_write_t *write = req->data;
553 if (write->client && write->client->pending_writes)
554 write->client->pending_writes--;
555 if ((write->close_on_done || (write->client && write->client->close_after_write && !write->client->pending_writes)) &&
556 !uv_is_closing((uv_handle_t *)write->stream))
557 uv_close((uv_handle_t *)write->stream, client_freed);
558 if (!status && write->client && write->client->http2)
559 (void)http2_flush(write->client);
560 SAFE_FREE(write->bytes);
562 SAFE_FREE(write);
563 SAFE_FREE(req);
564 (void)status;
565}
566static asciichat_error_t client_write_plain(http_server_client_t *client, const char *data, size_t length,
567 bool close_on_done) {
568 if (!client->tls) {
569 uv_write_t *request = SAFE_CALLOC(1, sizeof(*request), uv_write_t *);
570 http_write_t *write = SAFE_CALLOC(1, sizeof(*write), http_write_t *);
571 char *copy = SAFE_MALLOC(length, char *);
572 if (!request || !write || !copy) {
573 SAFE_FREE(request);
574 SAFE_FREE(write);
575 SAFE_FREE(copy);
576 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate HTTP write state");
577 }
578 memcpy(copy, data, length);
579 write->bytes = copy;
580 write->length = length;
581 write->stream = (uv_stream_t *)&client->tcp;
582 write->client = client;
583 write->close_on_done = close_on_done;
584 request->data = write;
585 uv_buf_t b = uv_buf_init(copy, (unsigned)length);
586 if (uv_write(request, write->stream, &b, 1, write_done) != 0) {
587 SAFE_FREE(copy);
588 SAFE_FREE(write);
589 SAFE_FREE(request);
590 return SET_ERRNO(ERROR_NETWORK, "Cannot write HTTP response");
591 }
592 client->pending_writes++;
593 return ASCIICHAT_OK;
594 }
595 size_t offset = 0;
596 while (offset < length) {
597 int n = SSL_write(client->tls, data + offset, (int)(length - offset));
598 if (n <= 0) {
599 int error = SSL_get_error(client->tls, n);
600 if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) {
601 if (!tls_flush(client))
602 break;
603 continue;
604 }
605 return SET_ERRNO(ERROR_NETWORK, "Cannot encrypt HTTP response");
606 }
607 offset += (size_t)n;
608 if (!tls_flush(client))
609 return SET_ERRNO(ERROR_NETWORK, "Cannot write encrypted HTTP response");
610 }
611 if (close_on_done)
612 client->close_after_write = true;
613 return offset == length ? ASCIICHAT_OK : SET_ERRNO(ERROR_NETWORK, "Cannot write encrypted HTTP response");
614}
615static asciichat_error_t http2_flush(http_server_client_t *client) {
616 const uint8_t *data = NULL;
617 size_t length = 0;
618 asciichat_error_t result = http2_connection_next_output(client->http2, &data, &length);
619 if (result != ASCIICHAT_OK || !length)
620 return result;
621 return client_write_plain(client, (const char *)data, length, false);
622}
623static const char *reason(unsigned s) {
624 switch (s) {
625 case 200:
626 return "OK";
627 case 204:
628 return "No Content";
629 case 400:
630 return "Bad Request";
631 case 404:
632 return "Not Found";
633 case 413:
634 return "Payload Too Large";
635 case 429:
636 return "Too Many Requests";
637 case 500:
638 return "Internal Server Error";
639 default:
640 return "";
641 }
642}
644 if (!r || s < 100 || s > 599)
645 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid HTTP status");
646 r->status = s;
647 return ASCIICHAT_OK;
648}
650 if (!r || !retained || !r->server || r->finished || r->retained)
651 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP response cannot be retained");
652 http_response_t *copy = SAFE_CALLOC(1, sizeof(*copy), http_response_t *);
653 if (!copy)
654 return SET_ERRNO(ERROR_MEMORY, "Cannot retain HTTP response");
655 copy->stream = r->stream;
656 copy->client = r->client;
658 copy->protocol = r->protocol;
660 copy->http3_submit = r->http3_submit;
661 copy->http3_context = r->http3_context;
662 copy->server = r->server;
664 copy->status = r->status;
665 copy->header_count = r->header_count;
666 memcpy(copy->headers, r->headers, r->header_count * sizeof(r->headers[0]));
667 if (r->body_length) {
668 copy->body = SAFE_MALLOC(r->body_length, char *);
669 if (!copy->body) {
670 SAFE_FREE(copy);
671 return SET_ERRNO(ERROR_MEMORY, "Cannot retain HTTP response body");
672 }
673 memcpy(copy->body, r->body, r->body_length);
674 copy->body_length = r->body_length;
675 }
676 copy->retained = true;
677 mutex_lock(&r->server->response_mutex);
679 r->server->retained_responses = copy;
680 mutex_unlock(&r->server->response_mutex);
681 r->deferred = true;
682 r->retained = true;
683 r->inflight_accounted = false;
684 *retained = copy;
685 return ASCIICHAT_OK;
686}
688 if (!response || !*response)
689 return;
690 if ((*response)->retained) {
691 http_server_t *server = (*response)->server;
692 if ((*response)->inflight_accounted) {
693 http_request_release(server);
694 (*response)->inflight_accounted = false;
695 }
696 if (server) {
697 mutex_lock(&server->response_mutex);
698 http_response_t **cursor = &server->retained_responses;
699 while (*cursor && *cursor != *response)
700 cursor = &(*cursor)->next_retained;
701 if (*cursor == *response)
702 *cursor = (*response)->next_retained;
703 mutex_unlock(&server->response_mutex);
704 }
705 SAFE_FREE((*response)->body);
706 SAFE_FREE(*response);
707 }
708 *response = NULL;
709}
711 if (!r || !n || !v)
712 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid HTTP response header");
713 for (size_t i = 0; i < r->header_count; ++i) {
714 if (platform_strcasecmp(r->headers[i].name, n) == 0) {
715 r->headers[i] = (http_header_t){n, v};
716 return ASCIICHAT_OK;
717 }
718 }
720 return SET_ERRNO(ERROR_INVALID_PARAM, "HTTP response header limit exceeded");
721 r->headers[r->header_count++] = (http_header_t){n, v};
722 return ASCIICHAT_OK;
723}
725 if (!r || (!d && n) || r->finished)
726 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP response is not writable");
727 if (n > r->max_body_length || r->body_length > r->max_body_length - n)
728 return SET_ERRNO(ERROR_NETWORK_SIZE, "HTTP response body exceeds configured limit");
729 char *p = SAFE_REALLOC(r->body, r->body_length + n + 1, char *);
730 if (!p)
731 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate HTTP response body");
732 r->body = p;
733 memcpy(p + r->body_length, d, n);
734 r->body_length += n;
735 p[r->body_length] = 0;
736 return ASCIICHAT_OK;
737}
739 return r ? r->body_length : 0;
740}
742 if (!r || r->finished)
743 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP response already finished");
744 r->finished = true;
745 if (r->status >= 400)
747 if (r->cancelled) {
748 if (r->inflight_accounted && !r->deferred) {
749 http_request_release(r->server);
750 r->inflight_accounted = false;
751 }
752 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP response was cancelled");
753 }
754 asciichat_error_t result;
755 if (r->http3_submit)
756 result = r->http3_submit(r->http3_context, r->status, r->headers, r->header_count, r->body, r->body_length);
757 else if (r->client && r->client->http2_mode) {
759 r->header_count, r->body, r->body_length);
760 if (result == ASCIICHAT_OK)
761 result = http2_flush(r->client);
762 } else {
763 size_t cap = 256 + r->body_length;
764 for (size_t i = 0; i < r->header_count; i++)
765 cap += strlen(r->headers[i].name) + strlen(r->headers[i].value) + 4;
766 char *out = SAFE_MALLOC(cap, char *);
767 if (!out) {
768 if (r->inflight_accounted && !r->deferred) {
769 http_request_release(r->server);
770 r->inflight_accounted = false;
771 }
772 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate HTTP response");
773 }
774 const char *version = r->protocol == HTTP_PROTOCOL_10 ? "HTTP/1.0" : "HTTP/1.1";
775 int n = safe_snprintf(out, cap, "%s %u %s\r\nContent-Length: %zu\r\nConnection: close\r\n", version, r->status,
776 reason(r->status), r->body_length);
777 for (size_t i = 0; i < r->header_count; i++)
778 n += safe_snprintf(out + n, cap - (size_t)n, "%s: %s\r\n", r->headers[i].name, r->headers[i].value);
779 n += safe_snprintf(out + n, cap - (size_t)n, "\r\n");
780 if (r->body_length)
781 memcpy(out + n, r->body, r->body_length);
782 n += (int)r->body_length;
783 result = client_write_plain(r->client, out, (size_t)n, true);
784 SAFE_FREE(out);
785 }
786 if (r->inflight_accounted && !r->deferred) {
787 http_request_release(r->server);
788 r->inflight_accounted = false;
789 }
790 return result;
791}
792
793asciichat_error_t http_server_dispatch_http3(http_server_t *server, const char *method, const char *path,
794 const http_header_t *headers, size_t header_count, const void *body,
795 size_t body_length, const char *peer_address,
796 http3_submit_response_fn submit, void *submit_context) {
797 if (!server || !method || !path || (header_count && !headers) || (!body && body_length) || !submit ||
798 body_length > server->config.max_request_body_bytes)
799 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid HTTP/3 request");
800 char path_copy[2048];
801 if (strlen(path) >= sizeof(path_copy))
802 return SET_ERRNO(ERROR_NETWORK_SIZE, "HTTP/3 request path is too long");
803 platform_strncpy(path_copy, sizeof(path_copy), path, sizeof(path_copy) - 1);
804 char *query = strchr(path_copy, '?');
805 if (query)
806 *query++ = 0;
807 http_request_t request = {0};
808 request.info = (http_request_info_t){.method = method,
809 .path = path_copy,
810 .query = query,
811 .protocol = HTTP_PROTOCOL_3,
812 .headers = headers,
813 .header_count = header_count,
814 .peer_address = peer_address,
815 .user_data = server->config.user_data};
816 request.storage = (char *)body;
817 request.body_length = body_length;
819 const char *peer = peer_address ? peer_address : "unknown";
820 bool allowed = false;
821 if (rate_limiter_check(server->rate_limiter, peer, RATE_EVENT_HTTP_REQUEST, &server->request_rate_limit, &allowed) !=
822 ASCIICHAT_OK ||
823 !allowed) {
825 http_response_t limited = {.server = server,
826 .protocol = HTTP_PROTOCOL_3,
827 .status = 429,
828 .max_body_length = http_max_response_body(server),
829 .http3_submit = submit,
830 .http3_context = submit_context};
831 return http_response_finish(&limited);
832 }
836 uint64_t request_start = time_get_ns();
837 if (!http_request_admit(server)) {
838 http_response_t limited = {.server = server,
839 .protocol = HTTP_PROTOCOL_3,
840 .status = 429,
841 .max_body_length = http_max_response_body(server),
842 .http3_submit = submit,
843 .http3_context = submit_context};
844 return http_response_finish(&limited);
845 }
846 http_response_t response = {.server = server,
847 .protocol = HTTP_PROTOCOL_3,
848 .status = 200,
849 .max_body_length = http_max_response_body(server),
850 .inflight_accounted = true,
851 .http3_submit = submit,
852 .http3_context = submit_context};
853 ASCII_OTEL_SCOPE(HTTP);
854 ASCII_OTEL_ATTR_STRING("http.method", method);
855 ASCII_OTEL_ATTR_STRING("http.route", request.info.path);
856 ASCII_OTEL_ATTR_STRING("http.protocol", "3");
857 ASCII_OTEL_ATTR_STRING("http.backend", "quic-ngtcp2-nghttp3");
858 if (http_server_authorize(&server->config.basic_auth, &request.info, &response))
859 server->config.on_request(&request, &response, server->config.user_data);
860 if (!response.finished && !response.deferred) {
861 asciichat_error_t result = http_response_finish(&response);
862 ASCII_OTEL_ATTR_INT("http.status", response.status);
863 ASCII_OTEL_ATTR_INT("http.response_bytes", (int64_t)response.body_length);
866 if (result == ASCIICHAT_OK && response.protocol == HTTP_PROTOCOL_3)
868 SAFE_FREE(response.body);
869 return result;
870 }
873 SAFE_FREE(response.body);
874 return ASCIICHAT_OK;
875}
877 return r ? &r->info : NULL;
878}
879const void *http_request_body(const http_request_t *r, size_t *n) {
880 if (n)
881 *n = r ? r->body_length : 0;
882 return r && r->body_length ? r->storage + r->body_offset : NULL;
883}
884
886 return server ? server->config.max_request_body_bytes : 0;
887}
889 return !r || r->cancelled;
890}
892 return !r || r->cancelled;
893}
895 if (!r)
896 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing HTTP request");
897 if (!r->cancelled)
899 r->cancelled = true;
900 return ASCIICHAT_OK;
901}
902static asciichat_error_t http2_request_cb(http2_connection_t *connection, const http2_request_view_t *view,
903 void *user_data) {
904 http_server_client_t *client = user_data;
905 uint64_t request_start = time_get_ns();
908 http_request_t request = {0};
909 ASCII_OTEL_SCOPE(HTTP);
910 ASCII_OTEL_ATTR_STRING("http.method", view->method);
911 ASCII_OTEL_ATTR_STRING("http.route", view->path);
912 ASCII_OTEL_ATTR_STRING("http.protocol", "2");
913 ASCII_OTEL_ATTR_STRING("http.backend", "libuv-nghttp2");
914 request.info.method = view->method;
915 request.info.path = view->path;
916 request.info.query = view->query;
917 request.info.protocol = HTTP_PROTOCOL_2;
919 for (size_t i = 0; i < request.info.header_count; ++i)
920 request.headers[i] = (http_header_t){view->headers[i].name, view->headers[i].value};
921 request.info.headers = request.headers;
922 request.info.peer_address = client->peer_address;
923 request.info.user_data = client->server->config.user_data;
924 request.storage = (char *)view->body;
925 request.body_offset = 0;
926 request.body_length = view->body_length;
927 request.info.user_data = client->server->config.user_data;
928 bool allowed = false;
930 &client->server->request_rate_limit, &allowed) != ASCIICHAT_OK ||
931 !allowed) {
933 http_response_t limited = {.server = client->server,
934 .stream = (uv_stream_t *)&client->tcp,
935 .client = client,
936 .http2_stream_id = view->stream_id,
937 .protocol = HTTP_PROTOCOL_2,
938 .max_body_length = http_max_response_body(client->server),
939 .status = 429};
940 asciichat_error_t result = http_response_finish(&limited);
941 SAFE_FREE(limited.body);
944 return result;
945 }
947 if (!http_request_admit(client->server)) {
948 http_response_t limited = {.server = client->server,
949 .stream = (uv_stream_t *)&client->tcp,
950 .client = client,
951 .http2_stream_id = view->stream_id,
952 .protocol = HTTP_PROTOCOL_2,
953 .max_body_length = http_max_response_body(client->server),
954 .status = 429};
955 asciichat_error_t result = http_response_finish(&limited);
956 SAFE_FREE(limited.body);
958 return result;
959 }
960 http_response_t response = {.server = client->server,
961 .stream = (uv_stream_t *)&client->tcp,
962 .client = client,
963 .http2_stream_id = view->stream_id,
964 .protocol = HTTP_PROTOCOL_2,
965 .max_body_length = http_max_response_body(client->server),
966 .inflight_accounted = true,
967 .status = 200};
968 if (http_server_authorize(&client->server->config.basic_auth, &request.info, &response))
969 client->server->config.on_request(&request, &response, client->server->config.user_data);
970 if (!response.finished && !response.deferred) {
971 asciichat_error_t result = http_response_finish(&response);
972 ASCII_OTEL_ATTR_INT("http.status", response.status);
973 ASCII_OTEL_ATTR_INT("http.response_bytes", (int64_t)response.body_length);
974 SAFE_FREE(response.body);
977 return result;
978 }
981 (void)connection;
982 return ASCIICHAT_OK;
983}
988static void read_cb(uv_stream_t *s, ssize_t n, const uv_buf_t *b) {
989 http_server_client_t *c = s->data;
990 char *base = (char *)b->base;
991 if (n <= 0) {
992 SAFE_FREE(base);
993 client_close(c);
994 return;
995 }
996 if (c->tls) {
997 if (BIO_write(SSL_get_rbio(c->tls), base, (int)n) != n || !tls_handshake(c)) {
998 SAFE_FREE(base);
999 client_close(c);
1000 return;
1001 }
1002 if (!SSL_is_init_finished(c->tls)) {
1003 SAFE_FREE(base);
1004 return;
1005 }
1006 char *plain_buffer = SAFE_MALLOC(b->len, char *);
1007 if (!plain_buffer) {
1008 SAFE_FREE(base);
1009 client_close(c);
1010 return;
1011 }
1012 int plain = SSL_read(c->tls, plain_buffer, (int)b->len);
1013 SAFE_FREE(base);
1014 if (plain <= 0) {
1015 client_close(c);
1016 return;
1017 }
1018 base = plain_buffer;
1019 n = plain;
1020 }
1021 size_t required = c->length + (size_t)n + 1;
1022 if (required > c->capacity) {
1023 size_t limit = c->server->config.max_request_body_bytes > SIZE_MAX - HTTP_READ_CAPACITY
1024 ? SIZE_MAX
1026 if (required > limit) {
1027 SAFE_FREE(base);
1028 client_close(c);
1029 return;
1030 }
1031 size_t capacity = c->capacity;
1032 while (capacity < required) {
1033 if (capacity > limit / 2) {
1034 capacity = limit;
1035 break;
1036 }
1037 capacity *= 2;
1038 }
1039 char *expanded = SAFE_REALLOC(c->buffer, capacity, char *);
1040 if (!expanded) {
1041 SAFE_FREE(base);
1042 client_close(c);
1043 return;
1044 }
1045 c->buffer = expanded;
1046 c->capacity = capacity;
1047 }
1048 memcpy(c->buffer + c->length, base, (size_t)n);
1049 c->length += (size_t)n;
1050 c->buffer[c->length] = 0;
1052 SAFE_FREE(base);
1053 static const char h2_preface[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
1054 if (!c->http2_mode && c->server->config.enable_http2 && c->length >= sizeof(h2_preface) - 1 &&
1055 memcmp(c->buffer, h2_preface, sizeof(h2_preface) - 1) == 0) {
1056 c->http2_mode = true;
1057 if (http2_connection_create(&c->http2, http2_request_cb, c, c->server->config.max_request_body_bytes,
1058 http_max_response_body(c->server)) != ASCIICHAT_OK ||
1060 client_close(c);
1061 return;
1062 }
1063 c->length = 0;
1064 if (http2_flush(c) != ASCIICHAT_OK)
1065 client_close(c);
1066 return;
1067 }
1068 if (!c->http2_mode && !strstr(c->buffer, "\r\n\r\n") && c->length > HTTP_HEADER_MAX_BYTES) {
1069 client_close(c);
1070 return;
1071 }
1072 if (c->http2_mode) {
1073 if (http2_connection_receive(c->http2, c->buffer, c->length) != ASCIICHAT_OK || http2_flush(c) != ASCIICHAT_OK)
1074 client_close(c);
1075 c->length = 0;
1076 return;
1077 }
1078 http_request_t r = {0};
1079 c->request_too_large = false;
1080 if (!parse_request(c, &r)) {
1081 if (c->request_too_large)
1082 client_close(c);
1083 return;
1084 }
1085 if (!c->server->config.enable_http1) {
1086 client_close(c);
1087 return;
1088 }
1089 bool allowed = false;
1091 &c->server->request_rate_limit, &allowed) != ASCIICHAT_OK ||
1092 !allowed) {
1094 http_response_t limited = {.server = c->server,
1095 .stream = s,
1096 .protocol = r.info.protocol,
1097 .max_body_length = http_max_response_body(c->server),
1098 .status = 429};
1099 http_response_finish(&limited);
1100 uv_read_stop(s);
1101 return;
1102 }
1104 if (!http_request_admit(c->server)) {
1105 http_response_t limited = {.server = c->server,
1106 .stream = s,
1107 .protocol = r.info.protocol,
1108 .max_body_length = http_max_response_body(c->server),
1109 .status = 429};
1110 http_response_finish(&limited);
1111 uv_read_stop(s);
1112 return;
1113 }
1114 http_response_t resp = {.server = c->server,
1115 .stream = s,
1116 .client = c,
1117 .protocol = r.info.protocol,
1118 .max_body_length = http_max_response_body(c->server),
1119 .inflight_accounted = true,
1120 .status = 200};
1121 uint64_t request_start = time_get_ns();
1122 ASCII_OTEL_SCOPE(HTTP);
1123 ASCII_OTEL_ATTR_STRING("http.method", r.info.method);
1124 ASCII_OTEL_ATTR_STRING("http.route", r.info.path);
1125 ASCII_OTEL_ATTR_STRING("http.protocol", http_protocol_name(r.info.protocol));
1126 ASCII_OTEL_ATTR_STRING("http.backend", "libuv");
1129 if (http_server_authorize(&c->server->config.basic_auth, &r.info, &resp))
1130 c->server->config.on_request(&r, &resp, c->server->config.user_data);
1131 if (!resp.finished && !resp.deferred)
1132 http_response_finish(&resp);
1133 ASCII_OTEL_ATTR_INT("http.status", resp.status);
1134 ASCII_OTEL_ATTR_INT("http.response_bytes", (int64_t)resp.body_length);
1135 SAFE_FREE(resp.body);
1138 uv_read_stop(s);
1139}
1140static void connection_cb(uv_stream_t *l, int status) {
1141 if (status < 0)
1142 return;
1143 http_server_t *s = l->data;
1145 if (!c)
1146 return;
1147 c->server = s;
1148 if (uv_tcp_init(&s->loop, &c->tcp) != 0) {
1149 SAFE_FREE(c);
1150 return;
1151 }
1152 c->tcp.data = c;
1153 if (uv_accept(l, (uv_stream_t *)&c->tcp) == 0) {
1154 if (s->connections >= s->config.max_connections) {
1155 client_close(c);
1156 return;
1157 }
1158 client_peer_address(c);
1159 bool allowed = false;
1162 !allowed) {
1164 client_close(c);
1165 return;
1166 }
1169 c->buffer = SAFE_CALLOC(c->capacity, 1, char *);
1170 if (!c->buffer) {
1171 client_close(c);
1172 return;
1173 }
1174 c->next = s->clients;
1175 if (s->clients)
1176 s->clients->previous = c;
1177 s->clients = c;
1178 c->admitted = true;
1179 s->connections++;
1181 if (s->config.on_connection)
1183 if (s->tls_context) {
1184 c->tls = SSL_new(s->tls_context);
1185 if (!c->tls) {
1186 client_close(c);
1187 return;
1188 }
1189 BIO *read_bio = BIO_new(BIO_s_mem());
1190 BIO *write_bio = BIO_new(BIO_s_mem());
1191 if (!read_bio || !write_bio) {
1192 BIO_free(read_bio);
1193 BIO_free(write_bio);
1194 client_close(c);
1195 return;
1196 }
1197 SSL_set_bio(c->tls, read_bio, write_bio);
1198 SSL_set_accept_state(c->tls);
1199 if (!tls_handshake(c)) {
1200 client_close(c);
1201 return;
1202 }
1203 }
1204 if (uv_read_start((uv_stream_t *)&c->tcp, alloc_buffer, read_cb) != 0)
1205 client_close(c);
1206 } else
1207 client_close(c);
1208}
1209static void stop_async_cb(uv_async_t *async) {
1210 http_server_t *s = async->data;
1211 if (!uv_is_closing((uv_handle_t *)&s->listener))
1212 uv_close((uv_handle_t *)&s->listener, NULL);
1213 if (s->listener6_initialized && !uv_is_closing((uv_handle_t *)&s->listener6))
1214 uv_close((uv_handle_t *)&s->listener6, NULL);
1215 for (http_server_client_t *c = s->clients; c; c = c->next)
1216 client_close(c);
1217 uv_close((uv_handle_t *)async, NULL);
1218 if (!uv_is_closing((uv_handle_t *)&s->wake_async))
1219 uv_close((uv_handle_t *)&s->wake_async, NULL);
1220 uv_stop(&s->loop);
1221}
1222static void *http_loop_thread(void *arg) {
1223 http_server_t *s = arg;
1224 uv_run(&s->loop, UV_RUN_DEFAULT);
1225 return NULL;
1226}
1228 if (!out || !cfg || !cfg->on_request || (!cfg->enable_http1 && !cfg->enable_http2 && !cfg->enable_http3))
1229 return SET_ERRNO(ERROR_INVALID_PARAM, "At least one HTTP protocol and a request handler are required");
1230 if (cfg->max_connections == 0 || cfg->max_request_body_bytes == 0)
1231 return SET_ERRNO(ERROR_INVALID_PARAM, "HTTP limits must be non-zero");
1232 if (cfg->basic_auth.enabled && (!cfg->basic_auth.username || !cfg->basic_auth.password ||
1233 !cfg->basic_auth.username[0] || !cfg->basic_auth.password[0]))
1234 return SET_ERRNO(ERROR_INVALID_PARAM, "Basic authentication requires non-empty credentials");
1235 if ((cfg->enable_http3 && !cfg->http3_endpoint) || ((cfg->tls_cert_path == NULL) != (cfg->tls_key_path == NULL)))
1236 return SET_ERRNO(ERROR_INVALID_PARAM, "HTTP/3 requires a pre-opened endpoint and TLS requires certificate and key");
1237 http_server_t *s = SAFE_CALLOC(1, sizeof(*s), http_server_t *);
1238 if (mutex_init(&s->response_mutex, "http_responses") != 0) {
1239 SAFE_FREE(s);
1240 return SET_ERRNO(ERROR_NETWORK, "Cannot initialize HTTP response synchronization");
1241 }
1243 if (!s->rate_limiter) {
1244 mutex_destroy(&s->response_mutex);
1245 SAFE_FREE(s);
1246 return SET_ERRNO(ERROR_MEMORY, "Cannot initialize HTTP rate limiter");
1247 }
1248 s->config = *cfg;
1249 if (cfg->tls_cert_path) {
1250 s->tls_context = SSL_CTX_new(TLS_server_method());
1251 if (!s->tls_context) {
1253 mutex_destroy(&s->response_mutex);
1254 SAFE_FREE(s);
1255 return SET_ERRNO(ERROR_NETWORK, "Cannot initialize TLS context");
1256 }
1257 SSL_CTX_set_app_data(s->tls_context, s);
1258 SSL_CTX_set_alpn_select_cb(s->tls_context, http_select_alpn, s);
1259 if (cfg->tls_key_password) {
1260 SSL_CTX_set_default_passwd_cb_userdata(s->tls_context, (void *)cfg->tls_key_password);
1261 }
1262 if (cfg->tls_key_password)
1263 SSL_CTX_set_default_passwd_cb(s->tls_context, tls_password_cb);
1264 if (SSL_CTX_use_certificate_chain_file(s->tls_context, cfg->tls_cert_path) != 1 ||
1265 SSL_CTX_use_PrivateKey_file(s->tls_context, cfg->tls_key_path, SSL_FILETYPE_PEM) != 1 ||
1266 SSL_CTX_check_private_key(s->tls_context) != 1) {
1267 SSL_CTX_free(s->tls_context);
1268 s->tls_context = NULL;
1270 mutex_destroy(&s->response_mutex);
1271 SAFE_FREE(s);
1272 return SET_ERRNO(ERROR_NETWORK, "Cannot load HTTP TLS certificate or key");
1273 }
1274 }
1277 ? (uint32_t)(cfg->max_requests_per_peer > UINT32_MAX ? UINT32_MAX : cfg->max_requests_per_peer)
1282 if (uv_loop_init(&s->loop) != 0) {
1284 mutex_destroy(&s->response_mutex);
1285 SSL_CTX_free(s->tls_context);
1286 SAFE_FREE(s);
1287 return SET_ERRNO(ERROR_NETWORK, "Cannot initialize HTTP event loop");
1288 }
1289 if (uv_tcp_init(&s->loop, &s->listener) != 0) {
1290 uv_loop_close(&s->loop);
1292 mutex_destroy(&s->response_mutex);
1293 SSL_CTX_free(s->tls_context);
1294 SAFE_FREE(s);
1295 return SET_ERRNO(ERROR_NETWORK, "Cannot initialize HTTP listener");
1296 }
1297 s->listener_initialized = true;
1298 s->listener.data = s;
1299 if (uv_tcp_init(&s->loop, &s->listener6) != 0) {
1300 uv_close((uv_handle_t *)&s->listener, NULL);
1301 return SET_ERRNO(ERROR_NETWORK, "Cannot initialize IPv6 HTTP listener");
1302 }
1303 s->listener6_initialized = true;
1304 s->listener6.data = s;
1305 if (uv_async_init(&s->loop, &s->stop_async, stop_async_cb) != 0) {
1306 uv_close((uv_handle_t *)&s->listener, NULL);
1307 uv_run(&s->loop, UV_RUN_DEFAULT);
1308 uv_loop_close(&s->loop);
1310 mutex_destroy(&s->response_mutex);
1311 SSL_CTX_free(s->tls_context);
1312 SAFE_FREE(s);
1313 return SET_ERRNO(ERROR_NETWORK, "Cannot initialize HTTP stop handle");
1314 }
1315 s->stop_async_initialized = true;
1316 s->stop_async.data = s;
1317 if (uv_async_init(&s->loop, &s->wake_async, wake_async_cb) != 0) {
1318 uv_close((uv_handle_t *)&s->stop_async, NULL);
1319 uv_close((uv_handle_t *)&s->listener, NULL);
1320 uv_run(&s->loop, UV_RUN_DEFAULT);
1321 uv_loop_close(&s->loop);
1322 mutex_destroy(&s->response_mutex);
1324 SSL_CTX_free(s->tls_context);
1325 SAFE_FREE(s);
1326 return SET_ERRNO(ERROR_NETWORK, "Cannot initialize HTTP wakeup handle");
1327 }
1328 s->wake_async_initialized = true;
1329 s->wake_async.data = s;
1330 *out = s;
1331 return ASCIICHAT_OK;
1332}
1334 if (!s || !s->running || uv_is_closing((uv_handle_t *)&s->wake_async))
1335 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP server is not running");
1336 return uv_async_send(&s->wake_async) == 0 ? ASCIICHAT_OK : SET_ERRNO(ERROR_NETWORK, "Cannot wake HTTP event loop");
1337}
1339 if (!s || s->running)
1340 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP server is already running");
1341 asciichat_error_t result = http_server_start_listener(s);
1342 if (result != ASCIICHAT_OK)
1343 return result;
1344 s->running = true;
1345 if (platform_thread_create(&s->thread, "http_io", http_loop_thread, s) != 0) {
1346 http_server_rollback_start(s);
1347 return SET_ERRNO(ERROR_THREAD, "Cannot create HTTP event-loop thread");
1348 }
1349 s->thread_started = true;
1350 return ASCIICHAT_OK;
1351}
1353 if (!s || s->running || s->adopted_socket || socket_handle < 0)
1354 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP listener cannot adopt this socket");
1355 if (uv_tcp_open(&s->listener, (uv_os_sock_t)socket_handle) != 0)
1356 return SET_ERRNO(ERROR_NETWORK, "Cannot adopt HTTP listener socket");
1357 s->adopted_socket = true;
1358 return ASCIICHAT_OK;
1359}
1361 if (!s || s->running || s->thread_started)
1362 return SET_ERRNO(ERROR_INVALID_STATE, "HTTP server loop is already running in its worker thread");
1363 asciichat_error_t result = http_server_start_listener(s);
1364 if (result != ASCIICHAT_OK)
1365 return result;
1366 s->running = true;
1367 s->foreground_running = true;
1368 uv_run(&s->loop, UV_RUN_DEFAULT);
1369 s->foreground_running = false;
1370 s->running = false;
1371 http_server_detach_http3(s);
1372 return ASCIICHAT_OK;
1373}
1375 if (!s || !s->running)
1376 return;
1377 s->running = false;
1378 if (s->thread_started) {
1379 uv_async_send(&s->stop_async);
1380 thread_join(&s->thread, NULL);
1381 s->thread_started = false;
1382 http_server_detach_http3(s);
1383 } else if (s->foreground_running) {
1384 (void)uv_async_send(&s->stop_async);
1385 } else {
1386 stop_async_cb(&s->stop_async);
1387 while (uv_run(&s->loop, UV_RUN_NOWAIT) != 0)
1388 ;
1389 http_server_detach_http3(s);
1390 }
1391}
1393 if (!p || !*p)
1394 return;
1395 http_server_t *s = *p;
1397 while (uv_run(&s->loop, UV_RUN_NOWAIT) != 0)
1398 ;
1399 uv_loop_close(&s->loop);
1400 http_server_detach_http3(s);
1402 mutex_destroy(&s->response_mutex);
1403 SSL_CTX_free(s->tls_context);
1404 SAFE_FREE(s);
1405 *p = NULL;
1406}
#define SET_ERRNO(code,...)
bool http_server_authorize(const http_basic_auth_config_t *config, const struct http_request_info *request, struct http_response *response)
Definition basic_auth.c:119
⚙️ Common definitions, error codes, macros, and types shared throughout the application
asciichat_error_t quic_endpoint_set_http_server(quic_endpoint_t *endpoint, http_server_t *server)
Definition endpoint.c:206
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:285
unsigned int uint32_t
Definition common.h:62
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
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_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_INVALID_PARAM
@ ERROR_THREAD
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
void http2_connection_destroy(http2_connection_t **connection)
Definition http2.c:229
asciichat_error_t http2_connection_receive(http2_connection_t *connection, const void *data, size_t length)
Definition http2.c:237
asciichat_error_t http2_connection_create(http2_connection_t **out, http2_request_fn on_request, void *user_data, size_t max_request_body_length, size_t max_response_body_length)
Definition http2.c:189
asciichat_error_t http2_connection_submit_response(http2_connection_t *connection, int32_t stream_id, unsigned status, const http_header_t *headers, size_t header_count, const void *body, size_t body_length)
Definition http2.c:257
asciichat_error_t http2_connection_next_output(http2_connection_t *connection, const uint8_t **data, size_t *length)
Definition http2.c:245
struct http2_connection http2_connection_t
Definition http2.h:8
struct quic_endpoint quic_endpoint_t
Backend-neutral HTTP request/response contract.
struct http_request http_request_t
struct http_request_info http_request_info_t
struct http_response http_response_t
@ STATS_COUNTER_HTTP_BYTES_RECEIVED
@ STATS_GAUGE_HTTP_CONNECTIONS_ACTIVE
@ STATS_GAUGE_HTTP_STREAMS_ACTIVE
#define HTTP_READ_CAPACITY
#define HTTP_HEADER_CAPACITY
asciichat_error_t http_request_cancel(http_request_t *r)
const void * http_request_body(const http_request_t *r, size_t *n)
asciichat_error_t http_server_init(http_server_t **out, const http_server_config_t *cfg)
asciichat_error_t http_response_set_status(http_response_t *r, unsigned s)
bool http_request_is_cancelled(const http_request_t *r)
asciichat_error_t http_server_wakeup(http_server_t *s)
size_t http_server_max_request_body(const http_server_t *server)
void http_server_stop(http_server_t *s)
void http_server_destroy(http_server_t **p)
asciichat_error_t http_response_set_header(http_response_t *r, const char *n, const char *v)
asciichat_error_t http_response_retain(http_response_t *r, http_response_t **retained)
struct http_write http_write_t
size_t http_server_max_response_body(const http_server_t *server)
bool http_response_is_cancelled(const http_response_t *r)
asciichat_error_t http_server_start(http_server_t *s)
size_t http_response_body_length(const http_response_t *r)
asciichat_error_t http_response_write(http_response_t *r, const void *d, size_t n)
asciichat_error_t http_server_adopt_socket(http_server_t *s, intptr_t socket_handle)
struct http_server_client http_server_client_t
void http_server_invalidate_http3_context(http_server_t *server, void *context)
const http_request_info_t * http_request_info(const http_request_t *r)
asciichat_error_t http_server_dispatch_http3(http_server_t *server, const char *method, const char *path, const http_header_t *headers, size_t header_count, const void *body, size_t body_length, const char *peer_address, http3_submit_response_fn submit, void *submit_context)
asciichat_error_t http_server_run(http_server_t *s)
#define HTTP_HEADER_MAX_BYTES
void http_response_release(http_response_t **response)
asciichat_error_t http_response_finish(http_response_t *r)
void stats_counter_add(stats_scope_t *scope, stats_counter_id_t id, uint64_t amount)
void stats_duration_record(stats_scope_t *scope, stats_duration_id_t id, uint64_t elapsed_ns)
void stats_gauge_set(stats_scope_t *scope, stats_gauge_id_t id, uint64_t value)
#define ASCII_OTEL_SCOPE(id)
Definition otel.h:72
#define ASCII_OTEL_ATTR_INT(key, value)
Definition otel.h:67
#define ASCII_OTEL_ATTR_STRING(key, value)
Definition otel.h:66
static bool size_t gotel_query_t * out
Definition queries.h:8
asciichat_error_t rate_limiter_record(rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type)
Record a rate limit event.
Definition rate_limit.c:162
void rate_limiter_destroy(rate_limiter_t *limiter)
Destroy rate limiter and free resources.
Definition rate_limit.c:126
rate_limiter_t * rate_limiter_create_memory(void)
Create in-memory rate limiter.
Definition rate_limit.c:83
asciichat_error_t rate_limiter_check(rate_limiter_t *limiter, const char *ip_address, rate_event_type_t event_type, const rate_limit_config_t *config, bool *allowed)
Check if an event from an IP address should be rate limited.
Definition rate_limit.c:138
const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
Default rate limits for each event type.
Definition rate_limit.c:33
🚦 Rate limiting API with pluggable backends
@ RATE_EVENT_CONNECTION
New connection.
Definition rate_limit.h:55
@ RATE_EVENT_HTTP_REQUEST
HTTP requests from a client identity.
Definition rate_limit.h:61
struct rate_limiter_s rate_limiter_t
Opaque rate limiter handle.
Definition rate_limit.h:83
stats_scope_t * stats_runtime_scope(void)
Definition runtime.c:80
asciichat_error_t(* http3_submit_response_fn)(void *context, unsigned status, const http_header_t *headers, size_t header_count, const void *body, size_t body_length)
🔤 String Manipulation and Shell Escaping Utilities
const char * name
Definition http2.h:20
const char * value
Definition http2.h:21
size_t body_length
Definition http2.h:14
const char * query
Definition http2.h:12
const char * path
Definition http2.h:11
const void * body
Definition http2.h:13
size_t header_count
Definition http2.h:17
const char * method
Definition http2.h:10
int32_t stream_id
Definition http2.h:15
const struct http2_header * headers
Definition http2.h:16
const char * password
Definition basic_auth.h:14
const char * username
Definition basic_auth.h:13
http_server_client_t * client
http3_submit_response_fn http3_submit
http_server_config_t config
rate_limiter_t * rate_limiter
rate_limit_config_t request_rate_limit
http_server_client_t * clients
http_response_t * retained_responses
http_server_client_t * client
One sliding-window rate-limit policy.
Definition rate_limit.h:73
uint32_t max_events
Maximum events allowed.
Definition rate_limit.h:74
uint32_t window_secs
Time window in seconds.
Definition rate_limit.h:75
⏱️ High-precision timing utilities using sokol_time.h and uthash
struct http_server http_server_t
Definition tls.h:8