ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
server.c File Reference

Go to the source code of this file.

Data Structures

struct  http_write
struct  http_server
struct  http_request
struct  http_response
struct  http_server_client

Macros

#define HTTP_READ_CAPACITY   (1024u * 1024u)
#define HTTP_HEADER_MAX_BYTES   (64u * 1024u)
#define HTTP_HEADER_CAPACITY   64u

Typedefs

typedef struct http_server_client http_server_client_t
typedef struct http_write http_write_t

Functions

size_t http_server_max_response_body (const http_server_t *server)
void http_server_invalidate_http3_context (http_server_t *server, void *context)
asciichat_error_t http_response_set_status (http_response_t *r, unsigned s)
asciichat_error_t http_response_retain (http_response_t *r, http_response_t **retained)
void http_response_release (http_response_t **response)
asciichat_error_t http_response_set_header (http_response_t *r, const char *n, const char *v)
asciichat_error_t http_response_write (http_response_t *r, const void *d, size_t n)
size_t http_response_body_length (const http_response_t *r)
asciichat_error_t http_response_finish (http_response_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)
const http_request_info_t * http_request_info (const http_request_t *r)
const void * http_request_body (const http_request_t *r, size_t *n)
size_t http_server_max_request_body (const http_server_t *server)
bool http_request_is_cancelled (const http_request_t *r)
bool http_response_is_cancelled (const http_response_t *r)
asciichat_error_t http_request_cancel (http_request_t *r)
asciichat_error_t http_server_init (http_server_t **out, const http_server_config_t *cfg)
asciichat_error_t http_server_wakeup (http_server_t *s)
asciichat_error_t http_server_start (http_server_t *s)
asciichat_error_t http_server_adopt_socket (http_server_t *s, intptr_t socket_handle)
asciichat_error_t http_server_run (http_server_t *s)
void http_server_stop (http_server_t *s)
void http_server_destroy (http_server_t **p)

Macro Definition Documentation

◆ HTTP_HEADER_CAPACITY

#define HTTP_HEADER_CAPACITY   64u

Definition at line 20 of file lib/network/http/server/server.c.

Referenced by http_response_set_header().

◆ HTTP_HEADER_MAX_BYTES

#define HTTP_HEADER_MAX_BYTES   (64u * 1024u)

Definition at line 19 of file lib/network/http/server/server.c.

◆ HTTP_READ_CAPACITY

#define HTTP_READ_CAPACITY   (1024u * 1024u)

Definition at line 18 of file lib/network/http/server/server.c.

Typedef Documentation

◆ http_server_client_t

Definition at line 37 of file lib/network/http/server/server.c.

◆ http_write_t

typedef struct http_write http_write_t

Function Documentation

◆ http_request_body()

const void * http_request_body ( const http_request_t * r,
size_t * n )

Definition at line 879 of file lib/network/http/server/server.c.

879 {
880 if (n)
881 *n = r ? r->body_length : 0;
882 return r && r->body_length ? r->storage + r->body_offset : NULL;
883}

References http_request::body_length, http_request::body_offset, and http_request::storage.

◆ http_request_cancel()

asciichat_error_t http_request_cancel ( http_request_t * r)

Definition at line 894 of file lib/network/http/server/server.c.

894 {
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}
#define SET_ERRNO(code,...)
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
void stats_counter_add(stats_scope_t *scope, stats_counter_id_t id, uint64_t amount)
stats_scope_t * stats_runtime_scope(void)
Definition runtime.c:80

References ASCIICHAT_OK, http_request::cancelled, ERROR_INVALID_PARAM, SET_ERRNO, stats_counter_add(), STATS_COUNTER_HTTP_CANCELLED, and stats_runtime_scope().

◆ http_request_info()

const http_request_info_t * http_request_info ( const http_request_t * r)

Definition at line 876 of file lib/network/http/server/server.c.

876 {
877 return r ? &r->info : NULL;
878}

References http_request::info.

◆ http_request_is_cancelled()

bool http_request_is_cancelled ( const http_request_t * r)

Definition at line 888 of file lib/network/http/server/server.c.

888 {
889 return !r || r->cancelled;
890}

References http_request::cancelled.

◆ http_response_body_length()

size_t http_response_body_length ( const http_response_t * response)

Return the number of bytes buffered for the response body.

Definition at line 738 of file lib/network/http/server/server.c.

738 {
739 return r ? r->body_length : 0;
740}

References http_response::body_length.

◆ http_response_finish()

asciichat_error_t http_response_finish ( http_response_t * r)

Definition at line 741 of file lib/network/http/server/server.c.

741 {
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}
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:51
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Platform-safe snprintf wrapper.
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
static bool size_t gotel_query_t * out
Definition queries.h:8
http_server_client_t * client
http3_submit_response_fn http3_submit

References ASCIICHAT_OK, http_response::body, http_response::body_length, http_response::cancelled, http_response::client, http_response::deferred, ERROR_INVALID_STATE, ERROR_MEMORY, http_response::finished, http_response::header_count, http_response::headers, http_server_client::http2, http2_connection_submit_response(), http_server_client::http2_mode, http_response::http2_stream_id, http_response::http3_context, http_response::http3_submit, HTTP_PROTOCOL_10, http_response::inflight_accounted, http_header_t::name, out, http_response::protocol, SAFE_FREE, SAFE_MALLOC, safe_snprintf(), http_response::server, SET_ERRNO, stats_counter_add(), STATS_COUNTER_HTTP_ERRORS, stats_runtime_scope(), http_response::status, and http_header_t::value.

Referenced by http_server_authorize(), and http_server_dispatch_http3().

◆ http_response_is_cancelled()

bool http_response_is_cancelled ( const http_response_t * r)

Definition at line 891 of file lib/network/http/server/server.c.

891 {
892 return !r || r->cancelled;
893}

References http_response::cancelled.

◆ http_response_release()

void http_response_release ( http_response_t ** response)

Release a response retained with http_response_retain().

Definition at line 687 of file lib/network/http/server/server.c.

687 {
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}
struct http_response http_response_t
http_response_t * retained_responses
struct http_server http_server_t
Definition tls.h:8

References http_response::next_retained, http_server::response_mutex, http_server::retained_responses, and SAFE_FREE.

◆ http_response_retain()

asciichat_error_t http_response_retain ( http_response_t * response,
http_response_t ** retained )

Retain a response for completion after the request callback returns.

Definition at line 649 of file lib/network/http/server/server.c.

649 {
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}
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275

References ASCIICHAT_OK, http_response::body, http_response::body_length, http_response::client, http_response::deferred, ERROR_INVALID_STATE, ERROR_MEMORY, http_response::finished, http_response::header_count, http_response::headers, http_response::http2_stream_id, http_response::http3_context, http_response::http3_submit, http_response::inflight_accounted, http_response::max_body_length, http_response::next_retained, http_response::protocol, http_server::response_mutex, http_response::retained, http_server::retained_responses, SAFE_CALLOC, SAFE_FREE, SAFE_MALLOC, http_response::server, SET_ERRNO, http_response::status, and http_response::stream.

◆ http_response_set_header()

asciichat_error_t http_response_set_header ( http_response_t * r,
const char * n,
const char * v )

Definition at line 710 of file lib/network/http/server/server.c.

710 {
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}
#define HTTP_HEADER_CAPACITY

References ASCIICHAT_OK, ERROR_INVALID_PARAM, http_response::header_count, http_response::headers, HTTP_HEADER_CAPACITY, http_header_t::name, and SET_ERRNO.

Referenced by http_server_authorize().

◆ http_response_set_status()

asciichat_error_t http_response_set_status ( http_response_t * r,
unsigned s )

Definition at line 643 of file lib/network/http/server/server.c.

643 {
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}

References ASCIICHAT_OK, ERROR_INVALID_PARAM, SET_ERRNO, and http_response::status.

Referenced by http_server_authorize().

◆ http_response_write()

asciichat_error_t http_response_write ( http_response_t * r,
const void * d,
size_t n )

Definition at line 724 of file lib/network/http/server/server.c.

724 {
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}
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:285
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77

References ASCIICHAT_OK, http_response::body, http_response::body_length, ERROR_INVALID_STATE, ERROR_MEMORY, ERROR_NETWORK_SIZE, http_response::finished, http_response::max_body_length, SAFE_REALLOC, and SET_ERRNO.

◆ http_server_adopt_socket()

asciichat_error_t http_server_adopt_socket ( http_server_t * server,
intptr_t socket_handle )

Adopt an already-listening native socket before starting the server.

Definition at line 1352 of file lib/network/http/server/server.c.

1352 {
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}
@ ERROR_NETWORK
Definition error_codes.h:72

References http_server::adopted_socket, ASCIICHAT_OK, ERROR_INVALID_STATE, ERROR_NETWORK, http_server::listener, http_server::running, and SET_ERRNO.

◆ http_server_destroy()

void http_server_destroy ( http_server_t ** p)

Definition at line 1392 of file lib/network/http/server/server.c.

1392 {
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}
void http_server_stop(http_server_t *s)
void rate_limiter_destroy(rate_limiter_t *limiter)
Destroy rate limiter and free resources.
Definition rate_limit.c:126
rate_limiter_t * rate_limiter

References http_server_stop(), http_server::loop, http_server::rate_limiter, rate_limiter_destroy(), http_server::response_mutex, SAFE_FREE, and http_server::tls_context.

Referenced by api_server_main(), and mcp_http_run().

◆ http_server_dispatch_http3()

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 )

Definition at line 793 of file lib/network/http/server/server.c.

796 {
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}
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
unsigned long long uint64_t
Definition common.h:63
struct http_request http_request_t
struct http_request_info http_request_info_t
@ STATS_COUNTER_HTTP_BYTES_RECEIVED
@ STATS_GAUGE_HTTP_STREAMS_ACTIVE
asciichat_error_t http_response_finish(http_response_t *r)
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
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
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
@ RATE_EVENT_HTTP_REQUEST
HTTP requests from a client identity.
Definition rate_limit.h:61
http_server_config_t config
rate_limit_config_t request_rate_limit

References ASCII_OTEL_ATTR_INT, ASCII_OTEL_ATTR_STRING, ASCII_OTEL_SCOPE, ASCIICHAT_OK, http_server_config_t::basic_auth, http_response::body, http_request::body_length, http_response::body_length, http_server::config, http_response::deferred, ERROR_INVALID_PARAM, ERROR_NETWORK_SIZE, http_response::finished, HTTP_PROTOCOL_3, http_response_finish(), http_server_authorize(), http_request::info, http_server_config_t::max_request_body_bytes, http_server_config_t::on_request, http_request_info::path, http_response::protocol, RATE_EVENT_HTTP_REQUEST, http_server::rate_limiter, rate_limiter_check(), rate_limiter_record(), http_server::request_rate_limit, SAFE_FREE, SET_ERRNO, stats_counter_add(), STATS_COUNTER_HTTP_BYTES_RECEIVED, STATS_COUNTER_HTTP_BYTES_SENT, STATS_COUNTER_HTTP_REJECTED, STATS_COUNTER_HTTP_REQUESTS, STATS_DURATION_HTTP_REQUEST, stats_duration_record(), STATS_GAUGE_HTTP_STREAMS_ACTIVE, stats_gauge_set(), stats_runtime_scope(), http_response::status, http_request::storage, and http_server_config_t::user_data.

◆ http_server_init()

asciichat_error_t http_server_init ( http_server_t ** out,
const http_server_config_t * cfg )

Definition at line 1227 of file lib/network/http/server/server.c.

1227 {
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}
unsigned int uint32_t
Definition common.h:62
rate_limiter_t * rate_limiter_create_memory(void)
Create in-memory rate limiter.
Definition rate_limit.c:83
const rate_limit_config_t DEFAULT_RATE_LIMITS[RATE_EVENT_MAX]
Default rate limits for each event type.
Definition rate_limit.c:33
const char * password
Definition basic_auth.h:14
const char * username
Definition basic_auth.h:13
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

References ASCIICHAT_OK, http_server_config_t::basic_auth, http_server::config, DEFAULT_RATE_LIMITS, http_server_config_t::enable_http1, http_server_config_t::enable_http2, http_server_config_t::enable_http3, http_basic_auth_config::enabled, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_NETWORK, http_server_config_t::http3_endpoint, http_server::listener, http_server::listener6, http_server::listener6_initialized, http_server::listener_initialized, http_server::loop, http_server_config_t::max_connections, rate_limit_config_t::max_events, http_server_config_t::max_request_body_bytes, http_server_config_t::max_requests_per_peer, http_server_config_t::on_request, out, http_basic_auth_config::password, RATE_EVENT_HTTP_REQUEST, http_server::rate_limiter, rate_limiter_create_memory(), rate_limiter_destroy(), http_server::request_rate_limit, http_server_config_t::request_rate_window_seconds, http_server::response_mutex, SAFE_CALLOC, SAFE_FREE, SET_ERRNO, http_server::stop_async, http_server::stop_async_initialized, http_server_config_t::tls_cert_path, http_server::tls_context, http_server_config_t::tls_key_password, http_server_config_t::tls_key_path, http_basic_auth_config::username, http_server::wake_async, http_server::wake_async_initialized, and rate_limit_config_t::window_secs.

Referenced by api_server_main(), and mcp_http_run().

◆ http_server_invalidate_http3_context()

void http_server_invalidate_http3_context ( http_server_t * server,
void * context )

Definition at line 138 of file lib/network/http/server/server.c.

138 {
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}

References http_response::next_retained, http_server::response_mutex, and http_server::retained_responses.

Referenced by http_server_http3_destroy().

◆ http_server_max_request_body()

size_t http_server_max_request_body ( const http_server_t * server)

Definition at line 885 of file lib/network/http/server/server.c.

885 {
886 return server ? server->config.max_request_body_bytes : 0;
887}

References http_server::config, and http_server_config_t::max_request_body_bytes.

◆ http_server_max_response_body()

size_t http_server_max_response_body ( const http_server_t * server)

Definition at line 135 of file lib/network/http/server/server.c.

135 {
136 return server ? http_max_response_body(server) : 0;
137}

◆ http_server_run()

asciichat_error_t http_server_run ( http_server_t * server)

Run the server loop when embedding the server without a background thread.

Definition at line 1360 of file lib/network/http/server/server.c.

1360 {
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}

References ASCIICHAT_OK, ERROR_INVALID_STATE, http_server::foreground_running, http_server::loop, http_server::running, SET_ERRNO, and http_server::thread_started.

◆ http_server_start()

asciichat_error_t http_server_start ( http_server_t * s)

Definition at line 1338 of file lib/network/http/server/server.c.

1338 {
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}
@ ERROR_THREAD

References ASCIICHAT_OK, ERROR_INVALID_STATE, ERROR_THREAD, http_server::running, SET_ERRNO, http_server::thread, and http_server::thread_started.

Referenced by api_server_main(), and mcp_http_run().

◆ http_server_stop()

void http_server_stop ( http_server_t * s)

Definition at line 1374 of file lib/network/http/server/server.c.

1374 {
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}

References http_server::foreground_running, http_server::loop, http_server::running, http_server::stop_async, http_server::thread, and http_server::thread_started.

Referenced by api_server_main(), http_server_destroy(), and mcp_http_run().

◆ http_server_wakeup()

asciichat_error_t http_server_wakeup ( http_server_t * server)

Wake the private event loop from a worker thread.

Definition at line 1333 of file lib/network/http/server/server.c.

1333 {
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}

References ASCIICHAT_OK, ERROR_INVALID_STATE, ERROR_NETWORK, http_server::running, SET_ERRNO, and http_server::wake_async.

Referenced by mcp_http_wake().