|
Csilk 0.2.1
A lightweight, high-performance C HTTP web framework
|
Core event-driven HTTP server implementation. More...
#include <limits.h>#include <llhttp.h>#include <openssl/err.h>#include <openssl/ssl.h>#include <stdatomic.h>#include <stdbool.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <uv.h>#include "context_internal.h"#include "csilk/core/internal.h"#include "csilk/csilk.h"#include "server_internal.h"#include "h2.h"#include "csilk/reflection/reflect.h"#include <netinet/in.h>#include <sys/socket.h>#include <unistd.h>
Data Structures | |
| struct | worker_data_t |
| Per-worker thread initialization data for SO_REUSEPORT multi-loop mode. More... | |
| struct | worker_stop_data_t |
Macros | |
| #define | UV_HANDLE_BOUND 0x00002000 |
Functions | |
| static void | alloc_buffer (uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) |
| libuv buffer allocation callback — allocates a receive buffer. | |
| static void | on_read (uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) |
| libuv read callback — processes incoming data from a client connection. | |
| static void | on_idle_timeout (uv_timer_t *handle) |
| libuv timer callback: fired when the connection has been idle (no active request) beyond keepalive_idle_ms. | |
| static void | on_write_timeout (uv_timer_t *handle) |
| libuv timer callback: fired when the response write has not completed within write_timeout_ms. | |
| static void | on_server_handle_close (uv_handle_t *handle) |
| libuv close callback for server-level handles during shutdown. | |
| static void | init_tls (csilk_server_t *s) |
| Initialize the server's TLS/SSL context using OpenSSL. | |
| static void | cleanup_tls (csilk_server_t *s) |
| Clean up the server's TLS/SSL context and global SSL state. | |
| static int | setup_client_tls (csilk_client_t *client) |
| Set up TLS for an individual client connection. | |
| static void | process_tls_read (csilk_client_t *client) |
| Process incoming TLS data — complete the handshake or decrypt application data. | |
| static void | flush_tls_write (csilk_client_t *client) |
| Flush buffered TLS encrypted data to the client socket. | |
| static void | trigger_hooks (csilk_server_t *s, csilk_ctx_t *c, csilk_hook_type_t type) |
| Internal: invoke all registered handlers for a given hook type. | |
| static csilk_client_t * | pool_get (csilk_server_t *server) |
| Get a client connection object from the server's free pool or allocate a new one. | |
| static void | pool_put (csilk_server_t *server, csilk_client_t *client) |
| Return a client connection to the server's free pool for reuse. | |
| static void | client_list_add (csilk_server_t *server, csilk_client_t *client) |
| Insert a client at the head of the server's active client list. | |
| static void | client_list_remove_internal (csilk_server_t *server, csilk_client_t *client) |
| Remove a client from the active list (no locking). | |
| static void | client_list_remove (csilk_server_t *server, csilk_client_t *client) |
| Remove a client from the active list (thread-safe). | |
| static void | on_timer_close (uv_handle_t *handle) |
| libuv close callback for client timer handles. | |
| static void | on_close (uv_handle_t *handle) |
| libuv close callback for client TCP handles — performs full cleanup. | |
| static void | on_signal (uv_signal_t *handle, int signum) |
| libuv signal handler for SIGINT — initiates graceful server shutdown. | |
| static void | on_stop_async (uv_async_t *handle) |
| libuv async callback to stop the server gracefully. | |
| static void | on_sendfile_complete (uv_fs_t *req) |
| libuv sendfile completion callback — continues with keep-alive or close. | |
| static void | on_write (uv_write_t *req, int status) |
| libuv write completion callback — handles post-write pipeline. | |
| static void | on_read_timeout (uv_timer_t *handle) |
| libuv timer callback: fired when no request data has been received within read_timeout_ms. | |
| static int | on_message_begin (llhttp_t *p) |
| llhttp callback: a new HTTP message begins. | |
| static int | on_url (llhttp_t *p, const char *at, size_t length) |
| llhttp callback: URL data received. | |
| static int | on_header_field (llhttp_t *p, const char *at, size_t length) |
| llhttp callback: header field name received. | |
| static char * | buf_grow (char *buf, size_t *cap, size_t needed) |
Grow a heap-allocated buffer to at least needed bytes. | |
| static int | on_header_value (llhttp_t *p, const char *at, size_t length) |
| llhttp callback: header value data received. | |
| static int | on_headers_complete (llhttp_t *p) |
| llhttp callback: all HTTP headers have been received. | |
| static int | on_body (llhttp_t *p, const char *at, size_t length) |
| llhttp callback: body data received. | |
| static const char * | get_status_text (int status) |
| Map an HTTP status code to its standard reason phrase. | |
| void | csilk_client_write (csilk_client_t *client, const uint8_t *data, size_t len) |
| Send raw data to the client (TLS-aware). | |
| void | _csilk_send_data (csilk_ctx_t *c, const uint8_t *data, size_t len) |
| Internal: Send data through the appropriate I/O path. | |
| void | _csilk_send_response (csilk_ctx_t *c) |
| Send the assembled HTTP response to the client. | |
| static void | finalize_request (csilk_client_t *client, llhttp_t *p) |
| Finalize the parsed request data before routing. | |
| static int | on_message_complete (llhttp_t *p) |
| llhttp callback: the full HTTP request message has been parsed. | |
| static void | on_rejected_close (uv_handle_t *handle) |
| static void | on_new_connection (uv_stream_t *server_stream, int status) |
| libuv connection callback — accept a new incoming TCP connection. | |
| const char * | csilk_get_client_ip (csilk_ctx_t *c) |
| Get the remote client's IP address as a string. | |
| csilk_server_t * | csilk_server_new (csilk_router_t *router) |
| Create a new server instance associated with a router. | |
| static void | spa_fallback_handler (csilk_ctx_t *c) |
| Built-in SPA (Single Page Application) fallback handler. | |
| void | csilk_server_set_not_found_handler (csilk_server_t *server, csilk_handler_t handler) |
| Set a custom handler for unmatched routes (404 Not Found). | |
| void | csilk_server_set_spa_fallback (csilk_server_t *server, const char *doc_root) |
| Enable SPA fallback: all unmatched GET requests serve index.html from the given directory. | |
| int | csilk_server_use (csilk_server_t *server, csilk_handler_t handler) |
| Register a global middleware handler that runs before every request. | |
| void | csilk_server_free (csilk_server_t *server) |
| Free a server instance and all associated resources. | |
| void | csilk_server_stop (csilk_server_t *server) |
| Signal the server to stop gracefully (thread-safe). | |
| void | csilk_server_get_stats (csilk_server_t *server, int *active_conn, int *pooled_conn) |
| void | csilk_server_set_config (csilk_server_t *server, const csilk_server_config_t *config) |
| Apply a server configuration struct, overwriting the current settings. | |
| int | csilk_server_set_max_connections (csilk_server_t *server, int max) |
| Set the maximum number of concurrent client connections. | |
| void | csilk_server_set_storage_driver (csilk_server_t *server, csilk_storage_driver_t *driver) |
| Set the pluggable storage driver for context key-value operations. | |
| void | csilk_server_set_crypto_driver (csilk_server_t *server, csilk_crypto_driver_t *driver) |
| Set the pluggable cryptographic driver for the server. | |
| void | csilk_server_set_cipher_driver (csilk_server_t *server, csilk_cipher_driver_t *driver) |
| Set the global cipher algorithm driver for the server. | |
| void | csilk_server_add_hook (csilk_server_t *s, csilk_hook_type_t type, void *handler) |
| Register a lifecycle hook on the server. | |
| static int | bind_and_listen (uv_loop_t *loop, uv_tcp_t *out_handle, int port, int backlog, bool reuseport) |
| Create, bind, and listen on a TCP socket with optional SO_REUSEPORT. | |
| static void | on_worker_stop_async (uv_async_t *handle) |
| Async callback for stopping a worker's event loop gracefully. | |
| static void | worker_thread (void *arg) |
| Worker thread entry point for multi-threaded SO_REUSEPORT mode. | |
| int | csilk_server_run (csilk_server_t *server, int port) |
| Start the server, bind to the given port, and enter the main event loop (blocking). | |
| static int | alpn_select_cb (SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) |
| csilk_mq_t * | csilk_server_get_mq (csilk_server_t *server) |
| Get the internal message queue instance for the server. | |
| csilk_router_t * | csilk_server_get_router (csilk_server_t *server) |
| Get the router instance attached to a server. | |
Core event-driven HTTP server implementation.
Architecture overview:
This file implements the full lifecycle of a csilk HTTP/TLS server. The design is built on three layers:
Key design decisions:
| struct worker_data_t |
Per-worker thread initialization data for SO_REUSEPORT multi-loop mode.
Passed to worker_thread() when spawning multiple accept loops.
| struct worker_stop_data_t |
| #define UV_HANDLE_BOUND 0x00002000 |
| void _csilk_send_data | ( | csilk_ctx_t * | c, |
| const uint8_t * | data, | ||
| size_t | len | ||
| ) |
Internal: Send data through the appropriate I/O path.
Routes data through the TLS wrapper if TLS is enabled, or writes directly to the TCP socket otherwise.
| c | Request context. |
| data | Bytes to send. |
| len | Number of bytes to send. |
| void _csilk_send_response | ( | csilk_ctx_t * | c | ) |
Send the assembled HTTP response to the client.
Internal: Trigger the response send path.
This is the central response-serialization function. It constructs the HTTP response bytes in memory and sends them via _csilk_send_data(). The response format is:
HTTP/1.1 <status> <reason>\r
[Transfer-Encoding: chunked\r
] Content-Length: <len>\r
Connection: keep-alive|close\r
<custom headers...>\r
\r
<body>
Response mode selection:
After the response is sent:
| c | Request context (must have _internal_client set). |
|
static |
libuv buffer allocation callback — allocates a receive buffer.
Allocates a buffer of the suggested size using malloc. The buffer is freed by libuv after the read callback is invoked.
| handle | The libuv handle that will read into the buffer. |
| suggested_size | Recommended buffer size from libuv. |
| buf | [out] Pointer to the uv_buf_t to populate. |
|
static |
|
static |
Create, bind, and listen on a TCP socket with optional SO_REUSEPORT.
Two code paths:
SO_REUSEPORT path (reuseport=true, non-Windows): Creates a raw socket with socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK), sets SO_REUSEADDR and SO_REUSEPORT, binds, and listens. The socket fd is then handed to libuv via uv_tcp_open(). This is used in multi-worker mode so each worker thread has its own accept loop sharing the same port. The kernel distributes incoming connections across the workers in a round-robin fashion.
IMPORTANT: uv_tcp_open() does not set the internal UV_HANDLE_BOUND flag. Since uv_listen() checks for this flag, we must set it manually (out_handle->flags |= UV_HANDLE_BOUND) before calling uv_listen().
Standard path (reuseport=false or Windows): Uses libuv's standard uv_tcp_bind() + uv_listen() sequence. This works on all platforms but does not support SO_REUSEPORT.
| loop | Event loop to attach the TCP handle to. |
| out_handle | [out] Initialized TCP handle (managed by libuv, do not free by caller). |
| port | TCP port number. |
| backlog | Maximum length of the pending connections queue. |
| reuseport | Enable SO_REUSEPORT for multi-process/thread socket sharing. |
|
static |
Grow a heap-allocated buffer to at least needed bytes.
Uses realloc with capacity doubling for amortized O(1) growth. If buf is NULL and *cap is 0, this acts as a malloc. On realloc failure the original buffer is NOT freed (caller must free it).
| buf | Existing allocation (may be NULL). |
| cap | [in,out] Current capacity — updated on success. |
| needed | Minimum required size in bytes. |
|
static |
Clean up the server's TLS/SSL context and global SSL state.
Frees the SSL_CTX and calls EVP_cleanup() for OpenSSL global cleanup.
| s | The server instance (may have ssl_ctx == NULL). |
|
static |
Insert a client at the head of the server's active client list.
Thread-safe: acquires the clients_mutex before modification.
| server | The server instance. |
| client | The client to add (must not already be in a list). |
|
static |
Remove a client from the active list (thread-safe).
Acquires clients_mutex, then delegates to client_list_remove_internal().
| server | The server instance. |
| client | The client to remove. |
|
static |
Remove a client from the active list (no locking).
Unlinks the client from the doubly-linked list and clears its prev/next pointers. Caller must hold clients_mutex.
| server | The server instance. |
| client | The client to remove. |
| void csilk_client_write | ( | csilk_client_t * | client, |
| const uint8_t * | data, | ||
| size_t | len | ||
| ) |
Send raw data to the client (TLS-aware).
Write data to the client's TCP socket, handling TLS encryption if necessary.
If TLS is active, writes through the SSL session and flushes the write BIO. Otherwise, allocates a write request, copies the data, and queues the write via libuv. The data buffer is freed by the write completion callback.
| c | The request context. |
| data | Data buffer to send. |
| len | Length of data in bytes. |
| const char * csilk_get_client_ip | ( | csilk_ctx_t * | c | ) |
Get the remote client's IP address as a string.
Get the client's IP address.
Resolves the client's IP address (IPv4 or IPv6) from the underlying TCP socket using libuv's getpeername. The result is allocated in arena memory so it is valid for the duration of the request.
| c | The request context. |
| void csilk_server_add_hook | ( | csilk_server_t * | s, |
| csilk_hook_type_t | type, | ||
| void * | handler | ||
| ) |
Register a lifecycle hook on the server.
Register a lifecycle hook callback.
Hooks are invoked at specific points in the request lifecycle (conn_open, conn_close, request_begin, request_end, server_start, server_stop). Multiple handlers can be registered for the same hook type; they are called in reverse order of registration (LIFO).
| s | The server instance. |
| type | Hook type (CSILK_HOOK_CONN_OPEN, CSILK_HOOK_REQUEST_BEGIN, CSILK_HOOK_CONN_CLOSE, CSILK_HOOK_REQUEST_END, CSILK_HOOK_SERVER_START, CSILK_HOOK_SERVER_STOP). |
| handler | Function pointer. For server hooks (start/stop), the signature is void(*)(csilk_server_t*). For context hooks, the signature is void(*)(csilk_ctx_t*). |
| void csilk_server_free | ( | csilk_server_t * | server | ) |
Free a server instance and all associated resources.
Destroy the server and release all resources.
Should only be called after the event loop has stopped. Joins any worker threads, frees the SPA doc root, drains the client pool, cleans up TLS, frees the message queue, frees all registered hooks, destroys the clients mutex, and frees the server struct.
| server | The server to free (may be NULL). |
| csilk_mq_t * csilk_server_get_mq | ( | csilk_server_t * | server | ) |
Get the internal message queue instance for the server.
Get the Message Queue instance attached to a server.
The MQ is created automatically during csilk_server_new(). It can be used to register topics, subscribers, and publish messages.
| server | The server instance. |
| csilk_router_t * csilk_server_get_router | ( | csilk_server_t * | server | ) |
Get the router instance attached to a server.
| server | The server instance. |
| void csilk_server_get_stats | ( | csilk_server_t * | server, |
| int * | active_conn, | ||
| int * | pooled_conn | ||
| ) |
| csilk_server_t * csilk_server_new | ( | csilk_router_t * | router | ) |
Create a new server instance associated with a router.
Create a new server instance.
Initializes the reflection system, allocates the server struct, sets up the libuv default loop, configures the llhttp parser callbacks, applies default server configuration (timeouts, buffer limits, backlog), creates a clients mutex, and creates the internal message queue (MQ) instance.
| router | The router instance to use for request matching. |
| int csilk_server_run | ( | csilk_server_t * | server, |
| int | port | ||
| ) |
Start the server, bind to the given port, and enter the main event loop (blocking).
Start the server and enter the libuv event loop.
This is the final step in server startup. The full bootstrap sequence:
| server | The server instance. |
| port | TCP port to bind to. |
| void csilk_server_set_cipher_driver | ( | csilk_server_t * | server, |
| csilk_cipher_driver_t * | driver | ||
| ) |
Set the global cipher algorithm driver for the server.
Set the cipher driver for symmetric/asymmetric encryption.
Replaces the built-in AES/RSA routines with a user-provided implementation.
| server | The server instance. |
| driver | Pointer to a csilk_cipher_driver_t. |
| void csilk_server_set_config | ( | csilk_server_t * | server, |
| const csilk_server_config_t * | config | ||
| ) |
Apply a server configuration struct, overwriting the current settings.
Apply server configuration options.
Copies the provided configuration into the server instance. This should be called before csilk_server_run().
| server | The server instance. |
| config | Pointer to the configuration to apply (copied by value). |
| void csilk_server_set_crypto_driver | ( | csilk_server_t * | server, |
| csilk_crypto_driver_t * | driver | ||
| ) |
Set the pluggable cryptographic driver for the server.
Set the global crypto driver for the server.
When set, HMAC and UUID operations on request contexts will delegate to the driver instead of using the built-in software implementations.
| server | The server instance. |
| driver | Pointer to the crypto driver vtable (may be NULL to reset). |
| int csilk_server_set_max_connections | ( | csilk_server_t * | server, |
| int | max | ||
| ) |
Set the maximum number of concurrent client connections.
Set the maximum number of concurrent connections and return the previous limit.
When this limit is reached, new connections are accepted and immediately closed to drain the listen backlog. A value of 0 means unlimited.
| server | The server instance. |
| max | Maximum concurrent connections (0 for unlimited). |
| void csilk_server_set_not_found_handler | ( | csilk_server_t * | server, |
| csilk_handler_t | handler | ||
| ) |
Set a custom handler for unmatched routes (404 Not Found).
Set a custom handler for 404 (route-not-found) responses.
Replaces the default "Not Found" plain-text response with a custom handler. Overridden by csilk_server_set_spa_fallback().
| server | The server instance. |
| handler | Handler function invoked for unmatched routes. |
| void csilk_server_set_spa_fallback | ( | csilk_server_t * | server, |
| const char * | doc_root | ||
| ) |
Enable SPA fallback: all unmatched GET requests serve index.html from the given directory.
Enable single-page application (SPA) fallback mode.
Sets the SPA document root and replaces the 404 handler with the built-in spa_fallback_handler. Overrides any custom 404 handler set via csilk_server_set_not_found_handler().
| server | The server instance. |
| doc_root | Absolute or relative filesystem path to the directory containing index.html. |
| void csilk_server_set_storage_driver | ( | csilk_server_t * | server, |
| csilk_storage_driver_t * | driver | ||
| ) |
Set the pluggable storage driver for context key-value operations.
Replace the context key-value storage driver.
When set, calls to csilk_set()/csilk_get() on request contexts belonging to this server will delegate to the driver instead of using the default arena-backed linked list.
| server | The server instance. |
| driver | Pointer to the storage driver vtable (may be NULL to reset). |
| void csilk_server_stop | ( | csilk_server_t * | server | ) |
Signal the server to stop gracefully (thread-safe).
Request a graceful server shutdown.
Sends an async signal to the event loop which triggers on_stop_async() on the main loop thread. The function returns immediately; the server shuts down asynchronously.
| server | The server instance. |
| int csilk_server_use | ( | csilk_server_t * | server, |
| csilk_handler_t | handler | ||
| ) |
Register a global middleware handler that runs before every request.
Register global middleware.
Global middlewares are prepended to the matched route's handler chain. They run before route-specific middleware and the final handler. There is a hard limit of 32 global middlewares.
| server | The server instance. |
| handler | Middleware handler function. |
|
static |
Finalize the parsed request data before routing.
Stores any remaining header field+value pair, splits the URL into path and query string, URL-decodes the path, parses query parameters into the context's query_params map, and sets the HTTP method on the context.
| client | The client connection. |
| p | The llhttp parser instance. |
|
static |
Flush buffered TLS encrypted data to the client socket.
Reads encrypted data from the write BIO and sends it via libuv write requests. Must be called after SSL_write() or SSL_do_handshake() to ensure the encrypted output is actually transmitted.
| client | The client connection whose write BIO should be drained. |
|
static |
Map an HTTP status code to its standard reason phrase.
Supports common codes: 101, 200, 201, 204, 400, 401, 403, 404, 500. Unrecognized codes default to "OK".
| status | HTTP status code. |
|
static |
Initialize the server's TLS/SSL context using OpenSSL.
Loads error strings, initializes SSL algorithms, creates a TLS server method context, loads the certificate chain and private key from the configured file paths, optionally loads a CA file, and optionally enables peer verification. On any failure, the SSL context is freed and set to NULL (TLS is effectively disabled).
| s | The server instance (config must have tls_cert_file and tls_key_file set if enable_tls is true). |
|
static |
llhttp callback: body data received.
Appends body data to the request body buffer (realloc as needed). Enforces max_body_size limit (returns HPE_USER if exceeded). On realloc failure, the existing body is freed and HPE_USER is returned.
| p | The llhttp parser instance. |
| at | Pointer to body data. |
| length | Length of body data in bytes. |
|
static |
libuv close callback for client TCP handles — performs full cleanup.
Triggers the CSILK_HOOK_CONN_CLOSE hook, removes the client from the active connections list, stops all four timers, and initiates their close via on_timer_close. When all timers are closed, the client's request context, arena, and temporary buffers are freed and the client is returned to the pool.
| handle | The TCP handle being closed (data points to csilk_client_t). |
|
static |
llhttp callback: header field name received.
Accumulates header field names. When a previous field+value pair is complete, stores it in the request context. Enforces max_header_size and max_headers_count limits (returns HPE_USER on violation).
| p | The llhttp parser instance. |
| at | Pointer to header field data. |
| length | Length of the header field data in bytes. |
|
static |
llhttp callback: header value data received.
Appends to the current header value buffer. Enforces max_header_size limit (returns HPE_USER if exceeded). On allocation failure, frees the partial value and returns HPE_USER.
| p | The llhttp parser instance. |
| at | Pointer to header value data. |
| length | Length of header value data. |
|
static |
llhttp callback: all HTTP headers have been received.
Flushes any remaining header field+value pair into the request context.
| p | The llhttp parser instance. |
|
static |
libuv timer callback: fired when the connection has been idle (no active request) beyond keepalive_idle_ms.
Closes the client connection immediately.
| handle | The timer handle (castable to client via handle->data). |
|
static |
llhttp callback: a new HTTP message begins.
Resets per-request state (total_header_size, header_count, etc. are reset elsewhere). Clears the thread-local request ID so each request starts fresh. Stops and restarts the request timeout timer.
| p | The llhttp parser instance (data points to csilk_client_t). |
|
static |
llhttp callback: the full HTTP request message has been parsed.
This is the main request dispatch point. It executes the following pipeline for every incoming HTTP request:
| p | The llhttp parser instance. |
|
static |
libuv connection callback — accept a new incoming TCP connection.
This is the entry point for every new TCP connection. The sequence is:
If any step fails (allocation, accept, init), the client is cleaned up via close callbacks and returned to the pool.
| server_stream | The listening server stream. |
| status | Connection status (negative on error). |
|
static |
libuv read callback — processes incoming data from a client connection.
This is the heart of the event-driven I/O model. Every byte from every connection arrives here. The dispatch logic has three paths:
TLS path (client->ssl is set): Data is written to the read BIO, then process_tls_read() drives the TLS handshake (if not yet complete) or decrypts and feeds the result to the llhttp parser (or WebSocket frame parser). Encrypted output from the write BIO is flushed via flush_tls_write().
WebSocket path (client->ctx.is_websocket): Data is parsed directly as WebSocket frames by csilk_ws_parse_frame(). No HTTP parsing occurs on this connection after the upgrade.
HTTP path (default): Data is fed directly to llhttp_execute(). The callbacks in server->settings (on_url, on_header_field, on_body, etc.) incrementally build the request struct. When the request is complete, on_message_complete fires to dispatch routing.
On positive nread: feed data to the appropriate handler. On nread == UV_EOF: peer closed the connection; close the client. On nread < 0 (error): log and close.
The idle timer is always stopped when data arrives (keep-alive wait is reset). The read timeout is restarted.
| stream | The client TCP stream. |
| nread | Number of bytes read (negative for error/EOF). |
| buf | The buffer that was read into (freed by this callback). |
|
static |
libuv timer callback: fired when no request data has been received within read_timeout_ms.
Closes the connection immediately.
| handle | The timer handle (castable to client via handle->data). |
|
static |
|
static |
libuv sendfile completion callback — continues with keep-alive or close.
sendfile() is used for efficient zero-copy file serving (kernel copies file data directly to the socket without userspace buffering). After sendfile completes, this callback:
sendfile is only used for non-TLS connections; TLS connections must use the buffered write path (SSL_write) because file data must be encrypted before transmission.
| req | The completed uv_fs_t sendfile request (freed by this callback). |
|
static |
libuv close callback for server-level handles during shutdown.
Currently a no-op placeholder. Called when the server, signal, or async handles finish closing.
| handle | The handle being closed (unused). |
|
static |
libuv signal handler for SIGINT — initiates graceful server shutdown.
Delegates to csilk_server_stop() which sends an async signal to the event loop to trigger cleanup on the main loop thread.
| handle | libuv signal handle (data points to csilk_server_t). |
| signum | Received signal number (e.g., SIGINT). |
|
static |
libuv async callback to stop the server gracefully.
This function performs the full shutdown sequence:
The actual client struct cleanup happens asynchronously in on_close() when each TCP handle finishes closing. This avoids blocking the event loop for connection draining.
| handle | libuv async handle (data points to csilk_server_t). |
|
static |
libuv close callback for client timer handles.
Decrements the close_pending counter. When all four timers are closed (close_pending reaches 0), the client is fully cleaned up: the arena is freed, temporary fields are freed, and the client is returned to the pool.
| handle | The timer handle being closed (data points to csilk_client_t). |
|
static |
llhttp callback: URL data received.
Stores the raw URL string. Checks against max_url_size and returns HPE_USER if exceeded (aborts parsing).
| p | The llhttp parser instance. |
| at | Pointer to the URL data. |
| length | Length of the URL data in bytes. |
|
static |
Async callback for stopping a worker's event loop gracefully.
This is the worker-thread equivalent of on_stop_async. It:
The loop will then naturally exit when all close callbacks finish.
| handle | The per-worker async handle (data points to worker_stop_data_t). |
|
static |
libuv write completion callback — handles post-write pipeline.
After a response body (or TLS-encrypted data) has been written to the socket, this callback orchestrates the next action:
The write request's data buffer (buf_copy) is freed here because it was allocated by _csilk_send_data / flush_tls_write.
| req | The completed uv_write_t request. |
| status | 0 on success, negative on error. |
|
static |
libuv timer callback: fired when the response write has not completed within write_timeout_ms.
Closes the connection immediately.
| handle | The timer handle (castable to client via handle->data). |
|
static |
Get a client connection object from the server's free pool or allocate a new one.
Reuses a previously freed client if available (up to 32 pooled entries), otherwise allocates a new zero-initialized csilk_client_t. The returned client's file_fd is initialized to -1.
| server | The server instance. |
|
static |
Return a client connection to the server's free pool for reuse.
If the client has an SSL session, it is freed first. The client struct is zeroed. If the pool has fewer than 32 entries, the client is saved for reuse; otherwise it is freed.
| server | The server instance. |
| client | The client to return (must not be used after this call). |
|
static |
Process incoming TLS data — complete the handshake or decrypt application data.
If the TLS handshake is not yet complete, performs SSL_do_handshake() and flushes the write BIO. If the handshake is complete, calls SSL_read() in a loop to decrypt application data and feeds the decrypted data to the llhttp parser (or WebSocket frame parser).
| client | The client connection with pending TLS data in the read BIO. |
|
static |
Set up TLS for an individual client connection.
Creates a new SSL session from the server's SSL_CTX, initializes memory BIOs for reading and writing encrypted data, and starts the TLS handshake by calling process_tls_read().
| client | The client connection to set up TLS on. |
|
static |
Built-in SPA (Single Page Application) fallback handler.
For unmatched GET requests, attempts to serve "index.html" from the configured SPA doc root. This enables client-side routing for SPAs like React, Vue, or Angular that handle their own URL routing in the browser.
| c | The request context. |
|
static |
Internal: invoke all registered handlers for a given hook type.
Walks the hook's linked list and calls each handler. Server-level hooks (start/stop) receive the server pointer. Context-level hooks receive the request context pointer.
| s | The server instance. |
| c | The request context (may be NULL for server-level hooks). |
| type | Hook type to trigger. |
|
static |
Worker thread entry point for multi-threaded SO_REUSEPORT mode.
Each worker runs its own libuv event loop and accept loop, sharing the same port via SO_REUSEPORT. The kernel distributes incoming connections across worker threads. The worker_data_t argument is freed by this function.
The worker registers a per-worker uv_async_t in server->worker_stop_async[idx] so the main thread can signal it to stop gracefully. After uv_run returns, the async handle and the server_handle are closed synchronously, then the loop is closed.
| arg | Pointer to worker_data_t (freed when the function exits). |