From a38df769226dcc74db39ac81a5e6b337970611c6 Mon Sep 17 00:00:00 2001 From: Azreyo <58790873+Azreyo@users.noreply.github.com> Date: Wed, 17 Dec 2025 15:26:36 +0100 Subject: [PATCH] Refactor code for consistency and readability - Updated function parameter formatting to use consistent pointer notation. - Removed unnecessary whitespace and adjusted comments for clarity. - Improved variable declarations and initializations in websocket.c for better readability. - Ensured consistent use of types and formatting across server_config and websocket headers. - Enhanced overall code style to align with best practices. --- src/config_parser.c | 63 +-- src/http2.c | 174 ++++--- src/http2.h | 32 +- src/logging.c | 375 ++++++++------ src/logging.h | 54 +- src/performance.c | 87 ++-- src/performance.h | 54 +- src/server.c | 1143 +++++++++++++++++++++---------------------- src/server_config.c | 7 +- src/server_config.h | 13 +- src/websocket.c | 100 ++-- src/websocket.h | 22 +- 12 files changed, 1100 insertions(+), 1024 deletions(-) diff --git a/src/config_parser.c b/src/config_parser.c index 009d6ee..3e8186f 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -15,7 +15,7 @@ typedef enum CONFIG_RUNNING, CONFIG_SERVER_NAME, CONFIG_LOG_MODE, - CONFIG_VERBOSE, // Keep for backwards compatibility + CONFIG_VERBOSE, // Keep for backwards compatibility CONFIG_ENABLE_HTTP2, CONFIG_ENABLE_WEBSOCKET, CONFIG_WWW_PATH, @@ -23,14 +23,11 @@ typedef enum CONFIG_SSL_CERT_PATH, CONFIG_SSL_KEY_PATH, CONFIG_UNKNOWN - } ConfigKey; // Trim whitespace from both ends of a string -static char *trim_whitespace(char *str) +static char* trim_whitespace(char* str) { - char *end; - // Trim leading space while (isspace((unsigned char)*str)) str++; @@ -39,7 +36,7 @@ static char *trim_whitespace(char *str) return str; // Trim trailing space - end = str + strlen(str) - 1; + char* end = str + strlen(str) - 1; while (end > str && isspace((unsigned char)*end)) end--; @@ -48,7 +45,7 @@ static char *trim_whitespace(char *str) } // Parse a boolean value (true/false, yes/no, on/off, 1/0) -static bool parse_bool(const char *value) +static bool parse_bool(const char* value) { if (strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0 || @@ -59,8 +56,9 @@ static bool parse_bool(const char *value) } return false; } + // Parse log mode value (off/classic/debug/advanced) -static LogMode parse_log_mode(const char *value) +static LogMode parse_log_mode(const char* value) { if (strcasecmp(value, "off") == 0 || strcmp(value, "0") == 0) return LOG_MODE_OFF; @@ -71,15 +69,15 @@ static LogMode parse_log_mode(const char *value) return LOG_MODE_DEBUG; if (strcasecmp(value, "advanced") == 0) return LOG_MODE_ADVANCED; - return LOG_MODE_CLASSIC; // Default + return LOG_MODE_CLASSIC; // Default } // Map string to enum -static ConfigKey get_config_key(const char *key) +static ConfigKey get_config_key(const char* key) { static const struct { - const char *name; + const char* name; ConfigKey key; } key_map[] = { {"port", CONFIG_PORT}, @@ -89,7 +87,7 @@ static ConfigKey get_config_key(const char *key) {"running", CONFIG_RUNNING}, {"server_name", CONFIG_SERVER_NAME}, {"log_mode", CONFIG_LOG_MODE}, - {"verbose", CONFIG_VERBOSE}, // Keep for backwards compatibility + {"verbose", CONFIG_VERBOSE}, // Keep for backwards compatibility {"enable_http2", CONFIG_ENABLE_HTTP2}, {"enable_websocket", CONFIG_ENABLE_WEBSOCKET}, {"www_path", CONFIG_WWW_PATH}, @@ -109,15 +107,15 @@ static ConfigKey get_config_key(const char *key) return CONFIG_UNKNOWN; } -int load_config(const char *filename, ServerConfig *config) +int load_config(const char* filename, ServerConfig* config) { if (!filename || strlen(filename) > 4096) { fprintf(stderr, "Invalid config filename\n"); return 1; } - - FILE *fp = fopen(filename, "r"); + + FILE* fp = fopen(filename, "r"); if (!fp) { perror("Error opening config file"); @@ -135,7 +133,7 @@ int load_config(const char *filename, ServerConfig *config) line[strcspn(line, "\r\n")] = 0; // Trim whitespace - char *trimmed = trim_whitespace(line); + char* trimmed = trim_whitespace(line); // Skip empty lines and comments if (trimmed[0] == '\0' || trimmed[0] == '#' || trimmed[0] == ';') @@ -144,7 +142,7 @@ int load_config(const char *filename, ServerConfig *config) } // Find the delimiter (= or space) - char *delim = strchr(trimmed, '='); + char* delim = strchr(trimmed, '='); if (!delim) { // Try space as delimiter @@ -159,8 +157,8 @@ int load_config(const char *filename, ServerConfig *config) // Split into key and value *delim = '\0'; - char *key = trim_whitespace(trimmed); - char *value = trim_whitespace(delim + 1); + char* key = trim_whitespace(trimmed); + char* value = trim_whitespace(delim + 1); // Remove quotes from value if present if ((value[0] == '"' || value[0] == '\'') && @@ -173,7 +171,7 @@ int load_config(const char *filename, ServerConfig *config) switch (get_config_key(key)) { case CONFIG_PORT: - config->port = atoi(value); + config->port = strcoll(value, value); printf("load_config: port = %d\n", config->port); break; @@ -189,7 +187,7 @@ int load_config(const char *filename, ServerConfig *config) break; case CONFIG_MAX_THREADS: - config->max_threads = atoi(value); + config->max_threads = strcoll(value, value); printf("load_config: max_threads = %d\n", config->max_threads); break; @@ -210,21 +208,28 @@ int load_config(const char *filename, ServerConfig *config) if (strcmp(config->server_name, "Your_domain/IP") == 0) { fprintf(stderr, "WARNING: server_name is set to default\n" - "Please set server_name in server.conf to the server's IP address or domain name for proper operation.\n"); + "Please set server_name in server.conf to the server's IP address or domain name for proper operation.\n"); } break; case CONFIG_LOG_MODE: config->log_mode = parse_log_mode(value); - printf("load_config: log_mode = %s\n", - config->log_mode == LOG_MODE_OFF ? "off" : - config->log_mode == LOG_MODE_DEBUG ? "debug" : - config->log_mode == LOG_MODE_ADVANCED ? "advanced" : "classic"); + printf("load_config: log_mode = %s\n", + config->log_mode == LOG_MODE_OFF + ? "off" + : config->log_mode == LOG_MODE_DEBUG + ? "debug" + : config->log_mode == LOG_MODE_ADVANCED + ? "advanced" + : "classic"); break; case CONFIG_VERBOSE: // Backwards compatibility: map verbose boolean to log_mode - if (parse_bool(value)) { + if (parse_bool(value)) + { config->log_mode = LOG_MODE_CLASSIC; - } else { + } + else + { config->log_mode = LOG_MODE_OFF; } printf("load_config: verbose (legacy) -> log_mode = %s\n", @@ -256,7 +261,7 @@ int load_config(const char *filename, ServerConfig *config) break; case CONFIG_MAX_CONNECTIONS: - config->max_connections = atoi(value); + config->max_connections = strcoll(value, value); printf("load_config: max_connections: = %d\n", config->max_connections); break; diff --git a/src/http2.c b/src/http2.c index 37a8526..0c00594 100644 --- a/src/http2.c +++ b/src/http2.c @@ -10,19 +10,19 @@ #include extern ServerConfig config; -extern void log_event(const char *message); -extern char *get_mime_type(const char *filepath); -extern char *sanitize_url(const char *url); +extern void log_event(const char* message); +extern char* get_mime_type(const char* filepath); +extern char* sanitize_url(const char* url); // ALPN callback - select HTTP/2 protocol -int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg) +int alpn_select_proto_cb(SSL* ssl, const unsigned char** out, + unsigned char* outlen, const unsigned char* in, + unsigned int inlen, void* arg) { (void)ssl; (void)arg; - int ret = nghttp2_select_next_protocol((unsigned char **)out, outlen, + int ret = nghttp2_select_next_protocol((unsigned char**)out, outlen, in, inlen); if (ret == 1) @@ -37,17 +37,17 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, } // No match, use HTTP/1.1 as fallback - *out = (const unsigned char *)"http/1.1"; + *out = (const unsigned char*)"http/1.1"; *outlen = 8; return SSL_TLSEXT_ERR_OK; } // Data read callback for nghttp2 -static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, - uint8_t *buf, size_t length, - uint32_t *data_flags, - nghttp2_data_source *source, - void *user_data) +static ssize_t file_read_callback(nghttp2_session* session, int32_t stream_id, + uint8_t* buf, size_t length, + uint32_t* data_flags, + nghttp2_data_source* source, + void* user_data) { (void)session; (void)stream_id; @@ -56,8 +56,7 @@ static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, int fd = source->fd; ssize_t nread; - while ((nread = read(fd, buf, length)) == -1 && errno == EINTR) - ; + while ((nread = read(fd, buf, length)) == -1 && errno == EINTR); if (nread == -1) { @@ -73,13 +72,13 @@ static ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id, } // Send callback for nghttp2 -static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, - size_t length, int flags, void *user_data) +static ssize_t send_callback(nghttp2_session* session, const uint8_t* data, + size_t length, int flags, void* user_data) { (void)session; (void)flags; - http2_session_t *h2_session = (http2_session_t *)user_data; + http2_session_t* h2_session = (http2_session_t*)user_data; ssize_t rv; if (h2_session->ssl) @@ -112,13 +111,13 @@ static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, } // Receive callback for nghttp2 -static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf, - size_t length, int flags, void *user_data) +static ssize_t recv_callback(nghttp2_session* session, uint8_t* buf, + size_t length, int flags, void* user_data) { (void)session; (void)flags; - http2_session_t *h2_session = (http2_session_t *)user_data; + http2_session_t* h2_session = (http2_session_t*)user_data; ssize_t rv; if (h2_session->ssl) @@ -159,9 +158,9 @@ static ssize_t recv_callback(nghttp2_session *session, uint8_t *buf, } // Frame receive callback -static int on_frame_recv_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) +static int on_frame_recv_callback(nghttp2_session* session, + const nghttp2_frame* frame, + void* user_data) { (void)user_data; @@ -173,28 +172,29 @@ static int on_frame_recv_callback(nghttp2_session *session, log_event("HTTP/2: Received HEADERS frame (request)"); // Get stream data - http2_stream_data_t *stream_data = - (http2_stream_data_t *)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + http2_stream_data_t* stream_data = + (http2_stream_data_t*)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if (stream_data && (frame->hd.flags & NGHTTP2_FLAG_END_STREAM)) { // Request is complete, send response - char *path = stream_data->request_path; + char* path = stream_data->request_path; if (strlen(path) == 0) { strcpy(path, "/"); } // Sanitize URL - char *sanitized = sanitize_url(path); + char* sanitized = sanitize_url(path); if (!sanitized) { log_event("HTTP/2: Blocked malicious URL"); // Send 403 error nghttp2_nv hdrs[] = { - {(uint8_t *)":status", (uint8_t *)"403", 7, 3, NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}}; + {(uint8_t*)":status", (uint8_t*)"403", 7, 3, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE} + }; nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL); break; } @@ -213,8 +213,9 @@ static int on_frame_recv_callback(nghttp2_session *session, // Send 404 error nghttp2_nv hdrs[] = { - {(uint8_t *)":status", (uint8_t *)"404", 7, 3, NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}}; + {(uint8_t*)":status", (uint8_t*)"404", 7, 3, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE} + }; nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL); break; } @@ -228,8 +229,9 @@ static int on_frame_recv_callback(nghttp2_session *session, // Send 500 error nghttp2_nv hdrs[] = { - {(uint8_t *)":status", (uint8_t *)"500", 7, 3, NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}}; + {(uint8_t*)":status", (uint8_t*)"500", 7, 3, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE} + }; nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL); break; } @@ -238,17 +240,18 @@ static int on_frame_recv_callback(nghttp2_session *session, { close(fd); log_event("HTTP/2: File size out of bounds"); - + // Send 500 error nghttp2_nv hdrs[] = { - {(uint8_t *)":status", (uint8_t *)"500", 7, 3, NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}}; + {(uint8_t*)":status", (uint8_t*)"500", 7, 3, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE} + }; nghttp2_submit_response(session, frame->hd.stream_id, hdrs, 2, NULL); break; } // Get MIME type - char *mime_type = get_mime_type(filepath); + char* mime_type = get_mime_type(filepath); if (!mime_type) { mime_type = strdup("application/octet-stream"); @@ -260,7 +263,7 @@ static int on_frame_recv_callback(nghttp2_session *session, stream_data->mime_type = mime_type; // Build response headers - allocate content length string - char *content_length = malloc(32); + char* content_length = malloc(32); if (!content_length) { close(fd); @@ -274,10 +277,14 @@ static int on_frame_recv_callback(nghttp2_session *session, stream_data->content_length = content_length; nghttp2_nv hdrs[] = { - {(uint8_t *)":status", (uint8_t *)"200", 7, 3, NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-type", (uint8_t *)mime_type, 12, strlen(mime_type), NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-length", (uint8_t *)content_length, 14, strlen(content_length), NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"server", (uint8_t *)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE}}; + {(uint8_t*)":status", (uint8_t*)"200", 7, 3, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"content-type", (uint8_t*)mime_type, 12, strlen(mime_type), NGHTTP2_NV_FLAG_NONE}, + { + (uint8_t*)"content-length", (uint8_t*)content_length, 14, strlen(content_length), + NGHTTP2_NV_FLAG_NONE + }, + {(uint8_t*)"server", (uint8_t*)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE} + }; // Submit response with file data provider nghttp2_data_provider data_prd; @@ -307,15 +314,15 @@ static int on_frame_recv_callback(nghttp2_session *session, } // Stream close callback -static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, - uint32_t error_code, void *user_data) +static int on_stream_close_callback(nghttp2_session* session, int32_t stream_id, + uint32_t error_code, void* user_data) { (void)error_code; (void)user_data; // Get stream data and clean up - http2_stream_data_t *stream_data = - (http2_stream_data_t *)nghttp2_session_get_stream_user_data(session, stream_id); + http2_stream_data_t* stream_data = + (http2_stream_data_t*)nghttp2_session_get_stream_user_data(session, stream_id); if (stream_data) { @@ -340,11 +347,11 @@ static int on_stream_close_callback(nghttp2_session *session, int32_t stream_id, } // Header callback -static int on_header_callback(nghttp2_session *session, - const nghttp2_frame *frame, - const uint8_t *name, size_t namelen, - const uint8_t *value, size_t valuelen, - uint8_t flags, void *user_data) +static int on_header_callback(nghttp2_session* session, + const nghttp2_frame* frame, + const uint8_t* name, size_t namelen, + const uint8_t* value, size_t valuelen, + uint8_t flags, void* user_data) { (void)flags; (void)user_data; @@ -356,8 +363,8 @@ static int on_header_callback(nghttp2_session *session, } // Get stream data - http2_stream_data_t *stream_data = - (http2_stream_data_t *)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); + http2_stream_data_t* stream_data = + (http2_stream_data_t*)nghttp2_session_get_stream_user_data(session, frame->hd.stream_id); if (!stream_data) { @@ -367,7 +374,9 @@ static int on_header_callback(nghttp2_session *session, // Process request headers if (namelen == 5 && memcmp(name, ":path", 5) == 0) { - size_t copy_len = valuelen < sizeof(stream_data->request_path) - 1 ? valuelen : sizeof(stream_data->request_path) - 1; + size_t copy_len = valuelen < sizeof(stream_data->request_path) - 1 + ? valuelen + : sizeof(stream_data->request_path) - 1; memcpy(stream_data->request_path, value, copy_len); stream_data->request_path[copy_len] = '\0'; @@ -380,9 +389,9 @@ static int on_header_callback(nghttp2_session *session, } // Begin headers callback -static int on_begin_headers_callback(nghttp2_session *session, - const nghttp2_frame *frame, - void *user_data) +static int on_begin_headers_callback(nghttp2_session* session, + const nghttp2_frame* frame, + void* user_data) { (void)session; (void)user_data; @@ -394,7 +403,7 @@ static int on_begin_headers_callback(nghttp2_session *session, } // Allocate stream data - http2_stream_data_t *stream_data = calloc(1, sizeof(http2_stream_data_t)); + http2_stream_data_t* stream_data = calloc(1, sizeof(http2_stream_data_t)); if (!stream_data) { return NGHTTP2_ERR_CALLBACK_FAILURE; @@ -404,14 +413,14 @@ static int on_begin_headers_callback(nghttp2_session *session, stream_data->fd = -1; nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream_data); - + free(stream_data); return 0; } // Data chunk receive callback -static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, - int32_t stream_id, const uint8_t *data, - size_t len, void *user_data) +static int on_data_chunk_recv_callback(nghttp2_session* session, uint8_t flags, + int32_t stream_id, const uint8_t* data, + size_t len, void* user_data) { (void)session; (void)flags; @@ -425,14 +434,14 @@ static int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, } // Initialize HTTP/2 session -int http2_session_init(http2_session_t *h2_session, int client_socket, SSL *ssl) +int http2_session_init(http2_session_t* h2_session, int client_socket, SSL* ssl) { h2_session->client_socket = client_socket; h2_session->ssl = ssl; h2_session->handshake_complete = false; // Setup callbacks - nghttp2_session_callbacks *callbacks; + nghttp2_session_callbacks* callbacks; nghttp2_session_callbacks_new(&callbacks); nghttp2_session_callbacks_set_send_callback(callbacks, send_callback); @@ -456,7 +465,8 @@ int http2_session_init(http2_session_t *h2_session, int client_socket, SSL *ssl) // Send initial SETTINGS frame nghttp2_settings_entry settings[] = { {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}, - {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 65535}}; + {NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 65535} + }; rv = nghttp2_submit_settings(h2_session->session, NGHTTP2_FLAG_NONE, settings, sizeof(settings) / sizeof(settings[0])); @@ -474,7 +484,7 @@ int http2_session_init(http2_session_t *h2_session, int client_socket, SSL *ssl) } // Cleanup HTTP/2 session -void http2_session_cleanup(http2_session_t *h2_session) +void http2_session_cleanup(http2_session_t* h2_session) { if (h2_session->session) { @@ -484,18 +494,19 @@ void http2_session_cleanup(http2_session_t *h2_session) } // Send HTTP/2 response -int http2_send_response(http2_session_t *h2_session, int32_t stream_id, - const char *data, size_t len, bool end_stream) +int http2_send_response(http2_session_t* h2_session, int32_t stream_id, + const char* data, size_t len, bool end_stream) { - (void)data; // Unused in current implementation - (void)len; // Unused in current implementation + (void)data; // Unused in current implementation + (void)len; // Unused in current implementation (void)end_stream; // Unused in current implementation // Send response headers nghttp2_nv hdrs[] = { - {(uint8_t *)":status", (uint8_t *)"200", 7, 3, NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-type", (uint8_t *)"text/html", 12, 9, NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"server", (uint8_t *)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE}}; + {(uint8_t*)":status", (uint8_t*)"200", 7, 3, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"content-type", (uint8_t*)"text/html", 12, 9, NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"server", (uint8_t*)"Carbon/2.0", 6, 10, NGHTTP2_NV_FLAG_NONE} + }; int rv = nghttp2_submit_response(h2_session->session, stream_id, hdrs, 3, NULL); if (rv != 0) @@ -507,15 +518,16 @@ int http2_send_response(http2_session_t *h2_session, int32_t stream_id, } // Send HTTP/2 error response -int http2_send_error(http2_session_t *h2_session, int32_t stream_id, - int status_code, const char *message) +int http2_send_error(http2_session_t* h2_session, int32_t stream_id, + int status_code, const char* message) { char status_str[4]; snprintf(status_str, sizeof(status_str), "%d", status_code); nghttp2_nv hdrs[] = { - {(uint8_t *)":status", (uint8_t *)status_str, 7, strlen(status_str), NGHTTP2_NV_FLAG_NONE}, - {(uint8_t *)"content-type", (uint8_t *)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE}}; + {(uint8_t*)":status", (uint8_t*)status_str, 7, strlen(status_str), NGHTTP2_NV_FLAG_NONE}, + {(uint8_t*)"content-type", (uint8_t*)"text/plain", 12, 10, NGHTTP2_NV_FLAG_NONE} + }; int rv = nghttp2_submit_response(h2_session->session, stream_id, hdrs, 2, NULL); if (rv != 0) @@ -526,7 +538,7 @@ int http2_send_error(http2_session_t *h2_session, int32_t stream_id, if (message) { nghttp2_data_provider prd; - prd.source.ptr = (void *)message; + prd.source.ptr = (void*)message; prd.read_callback = NULL; nghttp2_submit_data(h2_session->session, NGHTTP2_FLAG_END_STREAM, @@ -537,7 +549,7 @@ int http2_send_error(http2_session_t *h2_session, int32_t stream_id, } // Handle HTTP/2 connection -int http2_handle_connection(http2_session_t *h2_session) +int http2_handle_connection(http2_session_t* h2_session) { // Receive and process frames first int rv = nghttp2_session_recv(h2_session->session); @@ -573,4 +585,4 @@ int http2_handle_connection(http2_session_t *h2_session) } return 1; -} +} \ No newline at end of file diff --git a/src/http2.h b/src/http2.h index 56753f4..35dcfc3 100644 --- a/src/http2.h +++ b/src/http2.h @@ -8,8 +8,8 @@ // HTTP/2 session context typedef struct { - nghttp2_session *session; - SSL *ssl; + nghttp2_session* session; + SSL* ssl; int client_socket; bool handshake_complete; } http2_session_t; @@ -19,25 +19,25 @@ typedef struct { int32_t stream_id; char request_path[256]; - char *request_method; + char* request_method; int fd; // File descriptor for response size_t file_size; - char *mime_type; - char *content_length; + char* mime_type; + char* content_length; } http2_stream_data_t; // Function prototypes -int http2_session_init(http2_session_t *session, int client_socket, SSL *ssl); -void http2_session_cleanup(http2_session_t *session); -int http2_handle_connection(http2_session_t *session); -int http2_send_response(http2_session_t *session, int32_t stream_id, - const char *data, size_t len, bool end_stream); -int http2_send_error(http2_session_t *session, int32_t stream_id, - int status_code, const char *message); +int http2_session_init(http2_session_t* session, int client_socket, SSL* ssl); +void http2_session_cleanup(http2_session_t* session); +int http2_handle_connection(http2_session_t* session); +int http2_send_response(http2_session_t* session, int32_t stream_id, + const char* data, size_t len, bool end_stream); +int http2_send_error(http2_session_t* session, int32_t stream_id, + int status_code, const char* message); // ALPN callback for protocol selection -int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, - unsigned char *outlen, const unsigned char *in, - unsigned int inlen, void *arg); +int alpn_select_proto_cb(SSL* ssl, const unsigned char** out, + unsigned char* outlen, const unsigned char* in, + unsigned int inlen, void* arg); -#endif +#endif \ No newline at end of file diff --git a/src/logging.c b/src/logging.c index 7b61c34..1c6c0f1 100644 --- a/src/logging.c +++ b/src/logging.c @@ -1,6 +1,5 @@ #include "logging.h" #include -#include #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include // ANSI color codes #define COLOR_RESET "\x1b[0m" @@ -33,7 +33,7 @@ static LogConfig g_log_config = { .include_source_location = false, .colorize_console = true, .log_file = "log/server.log", - .max_file_size = 100 * 1024 * 1024, // 100MB + .max_file_size = 100 * 1024 * 1024, // 100MB .max_backup_files = 5 }; @@ -41,7 +41,8 @@ static pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER; static bool g_log_initialized = false; // Performance tracking -typedef struct { +typedef struct +{ char operation[64]; struct timeval start_time; bool active; @@ -52,96 +53,101 @@ static PerfTracker g_perf_trackers[MAX_PERF_TRACKERS]; static pthread_mutex_t g_perf_mutex = PTHREAD_MUTEX_INITIALIZER; // Get color for log level -static const char *get_level_color(LogLevel level) +static const char* get_level_color(LogLevel level) { - switch (level) { - case LOG_LEVEL_ERROR: return COLOR_RED; - case LOG_LEVEL_WARN: return COLOR_YELLOW; - case LOG_LEVEL_INFO: return COLOR_GREEN; - case LOG_LEVEL_DEBUG: return COLOR_CYAN; - case LOG_LEVEL_TRACE: return COLOR_MAGENTA; - default: return COLOR_WHITE; + switch (level) + { + case LOG_LEVEL_ERROR: return COLOR_RED; + case LOG_LEVEL_WARN: return COLOR_YELLOW; + case LOG_LEVEL_INFO: return COLOR_GREEN; + case LOG_LEVEL_DEBUG: return COLOR_CYAN; + case LOG_LEVEL_TRACE: return COLOR_MAGENTA; + default: return COLOR_WHITE; } } // Get level prefix -static const char *get_level_prefix(LogLevel level) +static const char* get_level_prefix(LogLevel level) { - switch (level) { - case LOG_LEVEL_ERROR: return "ERROR"; - case LOG_LEVEL_WARN: return "WARN "; - case LOG_LEVEL_INFO: return "INFO "; - case LOG_LEVEL_DEBUG: return "DEBUG"; - case LOG_LEVEL_TRACE: return "TRACE"; - default: return "?????"; + switch (level) + { + case LOG_LEVEL_ERROR: return "ERROR"; + case LOG_LEVEL_WARN: return "WARN "; + case LOG_LEVEL_INFO: return "INFO "; + case LOG_LEVEL_DEBUG: return "DEBUG"; + case LOG_LEVEL_TRACE: return "TRACE"; + default: return "?????"; } } // Get category name -static const char *get_category_name(LogCategory cat) +static const char* get_category_name(LogCategory cat) { - switch (cat) { - case LOG_CAT_GENERAL: return "GENERAL"; - case LOG_CAT_SECURITY: return "SECURITY"; - case LOG_CAT_NETWORK: return "NETWORK"; - case LOG_CAT_HTTP: return "HTTP"; - case LOG_CAT_SSL: return "SSL"; - case LOG_CAT_WEBSOCKET: return "WEBSOCKET"; - case LOG_CAT_CACHE: return "CACHE"; - case LOG_CAT_PERFORMANCE: return "PERF"; - default: return "UNKNOWN"; + switch (cat) + { + case LOG_CAT_GENERAL: return "GENERAL"; + case LOG_CAT_SECURITY: return "SECURITY"; + case LOG_CAT_NETWORK: return "NETWORK"; + case LOG_CAT_HTTP: return "HTTP"; + case LOG_CAT_SSL: return "SSL"; + case LOG_CAT_WEBSOCKET: return "WEBSOCKET"; + case LOG_CAT_CACHE: return "CACHE"; + case LOG_CAT_PERFORMANCE: return "PERF"; + default: return "UNKNOWN"; } } -const char *log_level_to_string(LogLevel level) +const char* log_level_to_string(LogLevel level) { - switch (level) { - case LOG_LEVEL_OFF: return "off"; - case LOG_LEVEL_ERROR: return "error"; - case LOG_LEVEL_WARN: return "warn"; - case LOG_LEVEL_INFO: return "info"; - case LOG_LEVEL_DEBUG: return "debug"; - case LOG_LEVEL_TRACE: return "trace"; - default: return "unknown"; + switch (level) + { + case LOG_LEVEL_OFF: return "off"; + case LOG_LEVEL_ERROR: return "error"; + case LOG_LEVEL_WARN: return "warn"; + case LOG_LEVEL_INFO: return "info"; + case LOG_LEVEL_DEBUG: return "debug"; + case LOG_LEVEL_TRACE: return "trace"; + default: return "unknown"; } } -const char *log_mode_to_string(LogLevel level) +const char* log_mode_to_string(LogLevel level) { - switch (level) { - case LOG_LEVEL_OFF: return "off"; - case LOG_LEVEL_ERROR: - case LOG_LEVEL_WARN: - case LOG_LEVEL_INFO: return "classic"; - case LOG_LEVEL_DEBUG: return "debug"; - case LOG_LEVEL_TRACE: return "advanced"; - default: return "classic"; + switch (level) + { + case LOG_LEVEL_OFF: return "off"; + case LOG_LEVEL_ERROR: + case LOG_LEVEL_WARN: + case LOG_LEVEL_INFO: return "classic"; + case LOG_LEVEL_DEBUG: return "debug"; + case LOG_LEVEL_TRACE: return "advanced"; + default: return "classic"; } } -LogLevel log_level_from_string(const char *str) +LogLevel log_level_from_string(const char* str) { if (!str) return LOG_LEVEL_INFO; - + // Handle mode names if (strcasecmp(str, "off") == 0) return LOG_LEVEL_OFF; if (strcasecmp(str, "classic") == 0) return LOG_LEVEL_INFO; if (strcasecmp(str, "debug") == 0) return LOG_LEVEL_DEBUG; if (strcasecmp(str, "advanced") == 0) return LOG_LEVEL_TRACE; - + // Handle level names if (strcasecmp(str, "error") == 0) return LOG_LEVEL_ERROR; if (strcasecmp(str, "warn") == 0) return LOG_LEVEL_WARN; if (strcasecmp(str, "warning") == 0) return LOG_LEVEL_WARN; if (strcasecmp(str, "info") == 0) return LOG_LEVEL_INFO; if (strcasecmp(str, "trace") == 0) return LOG_LEVEL_TRACE; - + // Handle boolean-like values for backwards compatibility - if (strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) + if (strcasecmp(str, "true") == 0 || strcmp(str, "1") == 0) return LOG_LEVEL_INFO; - if (strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0) + if (strcasecmp(str, "false") == 0 || strcmp(str, "0") == 0) return LOG_LEVEL_OFF; - + return LOG_LEVEL_INFO; } @@ -151,23 +157,30 @@ static void rotate_logs(void) struct stat st; if (stat(g_log_config.log_file, &st) != 0) return; - + if (st.st_size < (off_t)g_log_config.max_file_size) return; - + // Rotate existing backup files char old_path[512], new_path[512]; - for (int i = g_log_config.max_backup_files - 1; i >= 0; i--) { - if (i == 0) { + for (int i = g_log_config.max_backup_files - 1; i >= 0; i--) + { + if (i == 0) + { snprintf(old_path, sizeof(old_path), "%s", g_log_config.log_file); - } else { + } + else + { snprintf(old_path, sizeof(old_path), "%s.%d", g_log_config.log_file, i); } snprintf(new_path, sizeof(new_path), "%s.%d", g_log_config.log_file, i + 1); - - if (i + 1 >= g_log_config.max_backup_files) { + + if (i + 1 >= g_log_config.max_backup_files) + { unlink(old_path); - } else { + } + else + { rename(old_path, new_path); } } @@ -179,32 +192,35 @@ static void ensure_log_directory(void) char log_dir[512]; strncpy(log_dir, g_log_config.log_file, sizeof(log_dir) - 1); log_dir[sizeof(log_dir) - 1] = '\0'; - - char *dir_path = dirname(log_dir); + + char* dir_path = dirname(log_dir); if (!dir_path || strcmp(dir_path, ".") == 0) return; - + struct stat st; - if (stat(dir_path, &st) != 0) { - if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) { + if (stat(dir_path, &st) != 0) + { + if (mkdir(dir_path, 0755) != 0 && errno != EEXIST) + { fprintf(stderr, "Failed to create log directory: %s\n", strerror(errno)); } } } -void log_init(LogConfig *config) +void log_init(LogConfig* config) { pthread_mutex_lock(&g_log_mutex); - - if (config) { + + if (config) + { memcpy(&g_log_config, config, sizeof(LogConfig)); } - + ensure_log_directory(); g_log_initialized = true; - + pthread_mutex_unlock(&g_log_mutex); - + LOG_INFO(LOG_CAT_GENERAL, "Logging system initialized [mode=%s, level=%s]", log_mode_to_string(g_log_config.level), log_level_to_string(g_log_config.level)); @@ -231,168 +247,193 @@ void log_set_categories(LogCategory categories) pthread_mutex_unlock(&g_log_mutex); } -void log_write(LogLevel level, LogCategory category, const char *file, - int line, const char *func, const char *fmt, ...) +void log_write(LogLevel level, LogCategory category, const char* file, + int line, const char* func, const char* fmt, ...) { // Quick check without lock if (level == LOG_LEVEL_OFF || g_log_config.level == LOG_LEVEL_OFF) return; - + if (level > g_log_config.level) return; - + if (!(category & g_log_config.categories)) return; - + pthread_mutex_lock(&g_log_mutex); - + // Get timestamp struct timeval tv; gettimeofday(&tv, NULL); struct tm tm; localtime_r(&tv.tv_sec, &tm); - + char timestamp[64] = ""; - if (g_log_config.include_timestamp) { + if (g_log_config.include_timestamp) + { snprintf(timestamp, sizeof(timestamp), "%04d-%02d-%02d %02d:%02d:%02d.%03ld", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec / 1000); } - + // Get thread ID char thread_id[32] = ""; - if (g_log_config.include_thread_id) { + if (g_log_config.include_thread_id) + { snprintf(thread_id, sizeof(thread_id), "%lu", (unsigned long)pthread_self()); } - + // Get source location char source_loc[256] = ""; - if (g_log_config.include_source_location && file && func) { - const char *filename = strrchr(file, '/'); + if (g_log_config.include_source_location && file && func) + { + const char* filename = strrchr(file, '/'); filename = filename ? filename + 1 : file; snprintf(source_loc, sizeof(source_loc), "%s:%d:%s", filename, line, func); } - + // Format the message char message[4096]; va_list args; va_start(args, fmt); vsnprintf(message, sizeof(message), fmt, args); va_end(args); - + // Build log entry based on format char log_entry[8192]; - - if (g_log_config.format == LOG_FORMAT_JSON) { + + if (g_log_config.format == LOG_FORMAT_JSON) + { snprintf(log_entry, sizeof(log_entry), "{\"timestamp\":\"%s\",\"level\":\"%s\",\"category\":\"%s\"," "\"pid\":%d,\"tid\":\"%s\",\"source\":\"%s\",\"message\":\"%s\"}\n", timestamp, get_level_prefix(level), get_category_name(category), getpid(), thread_id, source_loc, message); - } else if (g_log_config.format == LOG_FORMAT_SYSLOG) { + } + else if (g_log_config.format == LOG_FORMAT_SYSLOG) + { // Syslog-compatible format snprintf(log_entry, sizeof(log_entry), "<%d>%s %s[%d]: [%s] %s\n", level, timestamp, "carbon", getpid(), get_category_name(category), message); - } else { + } + else + { // Plain text format - if (g_log_config.include_source_location && source_loc[0]) { + if (g_log_config.include_source_location && source_loc[0]) + { snprintf(log_entry, sizeof(log_entry), "[%s] [%s] [PID:%d] [TID:%s] [%s] [%s] %s\n", timestamp, get_level_prefix(level), getpid(), thread_id, get_category_name(category), source_loc, message); - } else { + } + else + { snprintf(log_entry, sizeof(log_entry), "[%s] [%s] [PID:%d] [TID:%s] [%s] %s\n", timestamp, get_level_prefix(level), getpid(), thread_id, get_category_name(category), message); } } - + // Write to console - if (g_log_config.console_output) { - if (g_log_config.colorize_console && isatty(STDOUT_FILENO)) { + if (g_log_config.console_output) + { + if (g_log_config.colorize_console && isatty(STDOUT_FILENO)) + { fprintf(stdout, "%s%s%s", get_level_color(level), log_entry, COLOR_RESET); - } else { + } + else + { fputs(log_entry, stdout); } fflush(stdout); } - + // Write to file - if (g_log_config.file_output && g_log_config.log_file[0]) { + if (g_log_config.file_output && g_log_config.log_file[0]) + { rotate_logs(); - - FILE *fp = fopen(g_log_config.log_file, "a"); - if (fp) { + + FILE* fp = fopen(g_log_config.log_file, "a"); + if (fp) + { fputs(log_entry, fp); fflush(fp); fclose(fp); } } - + pthread_mutex_unlock(&g_log_mutex); } // Backwards compatible log_event function -void log_event(const char *message) +void log_event(const char* message) { if (!message) return; log_write(LOG_LEVEL_INFO, LOG_CAT_GENERAL, NULL, 0, NULL, "%s", message); } // Secure logging - sanitizes potentially sensitive data -void log_secure(LogLevel level, LogCategory category, const char *fmt, ...) +void log_secure(LogLevel level, LogCategory category, const char* fmt, ...) { char message[4096]; va_list args; va_start(args, fmt); vsnprintf(message, sizeof(message), fmt, args); va_end(args); - + // Sanitize common sensitive patterns - char *patterns[] = { + char* patterns[] = { "password", "passwd", "pwd", "secret", "token", "key", "auth", "credential", "credit", "ssn", "api_key", "apikey", NULL }; - + char sanitized[4096]; strncpy(sanitized, message, sizeof(sanitized) - 1); sanitized[sizeof(sanitized) - 1] = '\0'; - + // Convert to lowercase for pattern matching char lower[4096]; - for (size_t i = 0; i < strlen(sanitized) && i < sizeof(lower) - 1; i++) { + for (size_t i = 0; i < strlen(sanitized) && i < sizeof(lower) - 1; i++) + { lower[i] = tolower((unsigned char)sanitized[i]); } lower[strlen(sanitized)] = '\0'; - + // Check for sensitive patterns bool has_sensitive = false; - for (int i = 0; patterns[i]; i++) { - if (strstr(lower, patterns[i])) { + for (int i = 0; patterns[i]; i++) + { + if (strstr(lower, patterns[i])) + { has_sensitive = true; break; } } - - if (has_sensitive) { + + if (has_sensitive) + { log_write(level, category, NULL, 0, NULL, "[REDACTED] Message contained sensitive data"); - } else { + } + else + { log_write(level, category, NULL, 0, NULL, "%s", sanitized); } } -void log_perf_start(const char *operation) +void log_perf_start(const char* operation) { if (g_log_config.level < LOG_LEVEL_DEBUG) return; - + pthread_mutex_lock(&g_perf_mutex); - - for (int i = 0; i < MAX_PERF_TRACKERS; i++) { - if (!g_perf_trackers[i].active) { + + for (int i = 0; i < MAX_PERF_TRACKERS; i++) + { + if (!g_perf_trackers[i].active) + { strncpy(g_perf_trackers[i].operation, operation, sizeof(g_perf_trackers[i].operation) - 1); g_perf_trackers[i].operation[sizeof(g_perf_trackers[i].operation) - 1] = '\0'; gettimeofday(&g_perf_trackers[i].start_time, NULL); @@ -400,88 +441,106 @@ void log_perf_start(const char *operation) break; } } - + pthread_mutex_unlock(&g_perf_mutex); } -void log_perf_end(const char *operation) +void log_perf_end(const char* operation) { if (g_log_config.level < LOG_LEVEL_DEBUG) return; - + struct timeval end_time; gettimeofday(&end_time, NULL); - + pthread_mutex_lock(&g_perf_mutex); - - for (int i = 0; i < MAX_PERF_TRACKERS; i++) { - if (g_perf_trackers[i].active && - strcmp(g_perf_trackers[i].operation, operation) == 0) { - + + for (int i = 0; i < MAX_PERF_TRACKERS; i++) + { + if (g_perf_trackers[i].active && + strcmp(g_perf_trackers[i].operation, operation) == 0) + { long elapsed_us = (end_time.tv_sec - g_perf_trackers[i].start_time.tv_sec) * 1000000 + - (end_time.tv_usec - g_perf_trackers[i].start_time.tv_usec); - + (end_time.tv_usec - g_perf_trackers[i].start_time.tv_usec); + g_perf_trackers[i].active = false; - + pthread_mutex_unlock(&g_perf_mutex); - - if (elapsed_us > 1000000) { + + if (elapsed_us > 1000000) + { LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %.2f s", operation, elapsed_us / 1000000.0); - } else if (elapsed_us > 1000) { + } + else if (elapsed_us > 1000) + { LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %.2f ms", operation, elapsed_us / 1000.0); - } else { + } + else + { LOG_DEBUG(LOG_CAT_PERFORMANCE, "%s completed in %ld µs", operation, elapsed_us); } return; } } - + pthread_mutex_unlock(&g_perf_mutex); } -void log_hexdump(const char *label, const void *data, size_t len) +void log_hexdump(const char* label, const void* data, size_t len) { if (g_log_config.level < LOG_LEVEL_TRACE) return; - + if (!data || len == 0) return; - + // Limit output size - if (len > 256) { + if (len > 256) + { LOG_TRACE(LOG_CAT_GENERAL, "%s: [%zu bytes, showing first 256]", label, len); len = 256; } - - const unsigned char *bytes = (const unsigned char *)data; + + const unsigned char* bytes = (const unsigned char*)data; char line[80]; char ascii[17]; - - for (size_t i = 0; i < len; i += 16) { + + for (size_t i = 0; i < len; i += 16) + { int pos = snprintf(line, sizeof(line), "%04zx: ", i); if (pos < 0) pos = 0; - if ((size_t) pos>= sizeof(line)) pos = sizeof(line) -1; + if ((size_t)pos >= sizeof(line)) pos = sizeof(line) - 1; - for (size_t j = 0; j < 16; j++) { - if (i + j < len) { + for (size_t j = 0; j < 16; j++) + { + if (i + j < len) + { int written = snprintf(line + pos, sizeof(line) - pos, "%02x ", bytes[i + j]); - if (written > 0 && (size_t)(pos + written) < sizeof(line)) { + if (written > 0 && (size_t)(pos + written) < sizeof(line)) + { pos += written; - } else { - pos = sizeof(line) -1; + } + else + { + pos = sizeof(line) - 1; } ascii[j] = isprint(bytes[i + j]) ? bytes[i + j] : '.'; - } else { + } + else + { int written = snprintf(line + pos, sizeof(line) - pos, " "); - if (written > 0 && (size_t)(pos + written)) { + if (written > 0 && (size_t)(pos + written)) + { pos += written; - } else { + } + else + { ascii[j] = ' '; } } } ascii[16] = '\0'; - + LOG_TRACE(LOG_CAT_GENERAL, "%s: %s |%s|", label, line, ascii); } } diff --git a/src/logging.h b/src/logging.h index 52a8016..e4d3f4f 100644 --- a/src/logging.h +++ b/src/logging.h @@ -2,22 +2,22 @@ #define LOGGING_H #include -#include #include -#include // Log levels -typedef enum { - LOG_LEVEL_OFF = 0, // No logging - LOG_LEVEL_ERROR = 1, // Only errors - LOG_LEVEL_WARN = 2, // Errors + warnings - LOG_LEVEL_INFO = 3, // Classic mode: errors + warnings + info - LOG_LEVEL_DEBUG = 4, // Debug mode: all above + debug messages - LOG_LEVEL_TRACE = 5 // Advanced mode: everything including traces +typedef enum +{ + LOG_LEVEL_OFF = 0, // No logging + LOG_LEVEL_ERROR = 1, // Only errors + LOG_LEVEL_WARN = 2, // Errors + warnings + LOG_LEVEL_INFO = 3, // Classic mode: errors + warnings + info + LOG_LEVEL_DEBUG = 4, // Debug mode: all above + debug messages + LOG_LEVEL_TRACE = 5 // Advanced mode: everything including traces } LogLevel; // Log categories for filtering -typedef enum { +typedef enum +{ LOG_CAT_GENERAL = 0x01, LOG_CAT_SECURITY = 0x02, LOG_CAT_NETWORK = 0x04, @@ -30,14 +30,16 @@ typedef enum { } LogCategory; // Log output formats -typedef enum { - LOG_FORMAT_PLAIN = 0, // Simple text format - LOG_FORMAT_JSON = 1, // JSON structured format - LOG_FORMAT_SYSLOG = 2 // Syslog compatible format +typedef enum +{ + LOG_FORMAT_PLAIN = 0, // Simple text format + LOG_FORMAT_JSON = 1, // JSON structured format + LOG_FORMAT_SYSLOG = 2 // Syslog compatible format } LogFormat; // Logger configuration -typedef struct { +typedef struct +{ LogLevel level; LogCategory categories; LogFormat format; @@ -53,7 +55,7 @@ typedef struct { } LogConfig; // Initialize the logging system -void log_init(LogConfig *config); +void log_init(LogConfig* config); // Cleanup logging system void log_cleanup(void); @@ -65,8 +67,8 @@ void log_set_level(LogLevel level); void log_set_categories(LogCategory categories); // Core logging functions -void log_write(LogLevel level, LogCategory category, const char *file, - int line, const char *func, const char *fmt, ...); +void log_write(LogLevel level, LogCategory category, const char* file, + int line, const char* func, const char* fmt, ...); // Convenience macros with source location #define LOG_ERROR(cat, ...) \ @@ -89,21 +91,21 @@ void log_write(LogLevel level, LogCategory category, const char *file, log_write(level, LOG_CAT_SECURITY, __FILE__, __LINE__, __func__, __VA_ARGS__) // Simple backwards-compatible log function -void log_event(const char *message); +void log_event(const char* message); // Log mode string conversion -const char *log_level_to_string(LogLevel level); -LogLevel log_level_from_string(const char *str); -const char *log_mode_to_string(LogLevel level); +const char* log_level_to_string(LogLevel level); +LogLevel log_level_from_string(const char* str); +const char* log_mode_to_string(LogLevel level); // Secure logging (sanitizes sensitive data) -void log_secure(LogLevel level, LogCategory category, const char *fmt, ...); +void log_secure(LogLevel level, LogCategory category, const char* fmt, ...); // Performance logging with timing -void log_perf_start(const char *operation); -void log_perf_end(const char *operation); +void log_perf_start(const char* operation); +void log_perf_end(const char* operation); // Hex dump for debugging binary data (only in TRACE level) -void log_hexdump(const char *label, const void *data, size_t len); +void log_hexdump(const char* label, const void* data, size_t len); #endif \ No newline at end of file diff --git a/src/performance.c b/src/performance.c index f7c082c..e989dac 100644 --- a/src/performance.c +++ b/src/performance.c @@ -5,33 +5,33 @@ #include #include #include -#include #define MAX_MMAP_CACHE_SIZE 100 #define MAX_MMAP_FILE_SIZE (10 * 1024 * 1024) // 10MB #define BUFFER_POOL_SIZE 64 #define DEFAULT_BUFFER_SIZE 32768 - + // Global cache structures -static mmap_cache_entry_t *mmap_cache = NULL; +static mmap_cache_entry_t* mmap_cache = NULL; static int mmap_cache_size = 0; static pthread_mutex_t mmap_cache_mutex = PTHREAD_MUTEX_INITIALIZER; -static buffer_pool_t *buffer_pool = NULL; +static buffer_pool_t* buffer_pool = NULL; static pthread_mutex_t buffer_pool_mutex = PTHREAD_MUTEX_INITIALIZER; - + // Pre-allocated response headers -const char *response_200_header = "HTTP/1.1 200 OK\r\n"; -const char *response_404_header = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"; -const char *response_403_header = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied"; -const char *response_429_header = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded"; -const char *response_500_header = "HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error"; -const char *response_503_header = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded"; +const char* response_200_header = "HTTP/1.1 200 OK\r\n"; +const char* response_404_header = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"; +const char* response_403_header = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied"; +const char* response_429_header = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded"; +const char* response_500_header = "HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error"; +const char* response_503_header = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded"; // Common MIME types cache -typedef struct { - const char *ext; - const char *mime; +typedef struct +{ + const char* ext; + const char* mime; } mime_cache_t; static const mime_cache_t mime_cache[] = { @@ -55,9 +55,12 @@ static const mime_cache_t mime_cache[] = { {NULL, NULL} }; -const char *get_mime_from_cache(const char *ext) { - for (int i = 0; mime_cache[i].ext != NULL; i++) { - if (strcasecmp(ext, mime_cache[i].ext) == 0) { +const char* get_mime_from_cache(const char* ext) +{ + for (int i = 0; mime_cache[i].ext != NULL; i++) + { + if (strcasecmp(ext, mime_cache[i].ext) == 0) + { return mime_cache[i].mime; } } @@ -65,7 +68,7 @@ const char *get_mime_from_cache(const char *ext) { } // Task queue implementation -void init_task_queue(task_queue_t *queue) +void init_task_queue(task_queue_t* queue) { queue->head = NULL; queue->tail = NULL; @@ -74,14 +77,14 @@ void init_task_queue(task_queue_t *queue) pthread_cond_init(&queue->cond, NULL); } -void enqueue_task(task_queue_t *queue, int socket_fd, SSL *ssl, bool is_https) +void enqueue_task(task_queue_t* queue, int socket_fd, SSL* ssl, bool is_https) { if (queue->count >= WORKER_QUEUE_SIZE - 1) { return; } - - connection_task_t *task = malloc(sizeof(connection_task_t)); + + connection_task_t* task = malloc(sizeof(connection_task_t)); if (!task) return; @@ -107,7 +110,7 @@ void enqueue_task(task_queue_t *queue, int socket_fd, SSL *ssl, bool is_https) pthread_mutex_unlock(&queue->mutex); } -connection_task_t *dequeue_task(task_queue_t *queue) +connection_task_t* dequeue_task(task_queue_t* queue) { pthread_mutex_lock(&queue->mutex); @@ -116,7 +119,7 @@ connection_task_t *dequeue_task(task_queue_t *queue) pthread_cond_wait(&queue->cond, &queue->mutex); } - connection_task_t *task = queue->head; + connection_task_t* task = queue->head; queue->head = task->next; if (queue->head == NULL) @@ -129,14 +132,14 @@ connection_task_t *dequeue_task(task_queue_t *queue) return task; } -void destroy_task_queue(task_queue_t *queue) +void destroy_task_queue(task_queue_t* queue) { pthread_mutex_lock(&queue->mutex); - connection_task_t *current = queue->head; + connection_task_t* current = queue->head; while (current) { - connection_task_t *next = current->next; + connection_task_t* next = current->next; free(current); current = next; } @@ -152,7 +155,7 @@ void init_mmap_cache(void) mmap_cache = calloc(MAX_MMAP_CACHE_SIZE, sizeof(mmap_cache_entry_t)); } -mmap_cache_entry_t *get_cached_file(const char *path) +mmap_cache_entry_t* get_cached_file(const char* path) { pthread_mutex_lock(&mmap_cache_mutex); @@ -165,7 +168,7 @@ mmap_cache_entry_t *get_cached_file(const char *path) pthread_mutex_unlock(&mmap_cache_mutex); return NULL; } - + mmap_cache[i].last_access = time(NULL); mmap_cache[i].ref_count++; pthread_mutex_unlock(&mmap_cache_mutex); @@ -177,7 +180,7 @@ mmap_cache_entry_t *get_cached_file(const char *path) return NULL; } -void cache_file_mmap(const char *path, size_t size, const char *mime_type) +void cache_file_mmap(const char* path, size_t size, const char* mime_type) { if (size > MAX_MMAP_FILE_SIZE) return; @@ -239,7 +242,7 @@ void cache_file_mmap(const char *path, size_t size, const char *mime_type) return; } - void *mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + void* mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); if (mapped == MAP_FAILED) @@ -263,7 +266,7 @@ void cache_file_mmap(const char *path, size_t size, const char *mime_type) pthread_mutex_unlock(&mmap_cache_mutex); } -void release_cached_file(mmap_cache_entry_t *entry) +void release_cached_file(mmap_cache_entry_t* entry) { pthread_mutex_lock(&mmap_cache_mutex); entry->ref_count--; @@ -298,7 +301,7 @@ void init_buffer_pool(void) for (int i = 0; i < BUFFER_POOL_SIZE; i++) { - buffer_pool_t *buf = malloc(sizeof(buffer_pool_t)); + buffer_pool_t* buf = malloc(sizeof(buffer_pool_t)); if (buf) { buf->buffer = malloc(DEFAULT_BUFFER_SIZE); @@ -312,19 +315,19 @@ void init_buffer_pool(void) pthread_mutex_unlock(&buffer_pool_mutex); } -char *get_buffer_from_pool(size_t min_size) +char* get_buffer_from_pool(size_t min_size) { if (min_size > DEFAULT_BUFFER_SIZE * 4) { // For very large requests, allocate directly return malloc(min_size); } - + pthread_mutex_lock(&buffer_pool_mutex); - buffer_pool_t *current = buffer_pool; - buffer_pool_t *best_fit = NULL; - + buffer_pool_t* current = buffer_pool; + buffer_pool_t* best_fit = NULL; + // Find best fit buffer (smallest that fits) while (current) { @@ -337,7 +340,7 @@ char *get_buffer_from_pool(size_t min_size) } current = current->next; } - + if (best_fit) { best_fit->in_use = true; @@ -350,11 +353,11 @@ char *get_buffer_from_pool(size_t min_size) return malloc(min_size); } -void return_buffer_to_pool(char *buffer) +void return_buffer_to_pool(char* buffer) { pthread_mutex_lock(&buffer_pool_mutex); - buffer_pool_t *current = buffer_pool; + buffer_pool_t* current = buffer_pool; while (current) { if (current->buffer == buffer) @@ -376,10 +379,10 @@ void cleanup_buffer_pool(void) { pthread_mutex_lock(&buffer_pool_mutex); - buffer_pool_t *current = buffer_pool; + buffer_pool_t* current = buffer_pool; while (current) { - buffer_pool_t *next = current->next; + buffer_pool_t* next = current->next; free(current->buffer); free(current); current = next; diff --git a/src/performance.h b/src/performance.h index a3df270..d3a7f5e 100644 --- a/src/performance.h +++ b/src/performance.h @@ -14,15 +14,15 @@ typedef struct connection_task_t { int socket_fd; - SSL *ssl; + SSL* ssl; bool is_https; - struct connection_task_t *next; + struct connection_task_t* next; } connection_task_t; typedef struct { - connection_task_t *head; - connection_task_t *tail; + connection_task_t* head; + connection_task_t* tail; pthread_mutex_t mutex; pthread_cond_t cond; int count; @@ -31,51 +31,51 @@ typedef struct // Memory-mapped file cache typedef struct { - char *path; - void *mmap_data; + char* path; + void* mmap_data; size_t size; - void *compressed_data; + void* compressed_data; size_t compressed_size; time_t last_access; - char *mime_type; + char* mime_type; int ref_count; } mmap_cache_entry_t; // Response buffer pool typedef struct buffer_pool_t { - char *buffer; + char* buffer; size_t size; bool in_use; - struct buffer_pool_t *next; + struct buffer_pool_t* next; } buffer_pool_t; // Function declarations -void init_task_queue(task_queue_t *queue); -void enqueue_task(task_queue_t *queue, int socket_fd, SSL *ssl, bool is_https); -connection_task_t *dequeue_task(task_queue_t *queue); -void destroy_task_queue(task_queue_t *queue); +void init_task_queue(task_queue_t* queue); +void enqueue_task(task_queue_t* queue, int socket_fd, SSL* ssl, bool is_https); +connection_task_t* dequeue_task(task_queue_t* queue); +void destroy_task_queue(task_queue_t* queue); void init_mmap_cache(void); -mmap_cache_entry_t *get_cached_file(const char *path); -void cache_file_mmap(const char *path, size_t size, const char *mime_type); -void release_cached_file(mmap_cache_entry_t *entry); +mmap_cache_entry_t* get_cached_file(const char* path); +void cache_file_mmap(const char* path, size_t size, const char* mime_type); +void release_cached_file(mmap_cache_entry_t* entry); void cleanup_mmap_cache(void); void init_buffer_pool(void); -char *get_buffer_from_pool(size_t min_size); -void return_buffer_to_pool(char *buffer); +char* get_buffer_from_pool(size_t min_size); +void return_buffer_to_pool(char* buffer); void cleanup_buffer_pool(void); // Pre-allocated response headers -extern const char *response_200_header; -extern const char *response_404_header; -extern const char *response_403_header; -extern const char *response_429_header; -extern const char *response_500_header; -extern const char *response_503_header; +extern const char* response_200_header; +extern const char* response_404_header; +extern const char* response_403_header; +extern const char* response_429_header; +extern const char* response_500_header; +extern const char* response_503_header; // MIME type cache -const char *get_mime_from_cache(const char *ext); +const char* get_mime_from_cache(const char* ext); -#endif +#endif \ No newline at end of file diff --git a/src/server.c b/src/server.c index 855feff..2c6adae 100644 --- a/src/server.c +++ b/src/server.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -19,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -72,7 +70,7 @@ static int MAX_REQUESTS_DYNAMIC = 500; // Will be calculated dynamically #define SOCKET_SEND_BUFFER_SIZE (512 * 1024) // 512KB for faster throughput #define SOCKET_RECV_BUFFER_SIZE (512 * 1024) // 512KB -#define SOCKET_BACKLOG 256 +#define SOCKET_BACKLOG 256 #define EPOLL_TIMEOUT 50 // 50ms timeout for faster polling #define MAX_THREAD_POOL_SIZE 64 @@ -89,14 +87,14 @@ typedef struct int cpu_core; } ThreadInfo; -ThreadInfo *thread_pool; +ThreadInfo* thread_pool; int thread_pool_size = 0; pthread_mutex_t thread_pool_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t thread_pool_cond = PTHREAD_COND_INITIALIZER; // Worker thread queue task_queue_t worker_queue; -pthread_t *worker_threads = NULL; +pthread_t* worker_threads = NULL; int num_worker_threads = 0; volatile int workers_running = 1; @@ -109,55 +107,55 @@ typedef struct typedef struct { - char *path; - char *data; + char* path; + char* data; size_t size; time_t last_access; - char *mime_type; + char* mime_type; } CacheEntry; -CacheEntry *file_cache = NULL; +CacheEntry* file_cache = NULL; int cache_size = 0; pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER; ServerConfig config; char server_log[MAX_LOG_SIZE]; pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; -pthread_t *client_threads = NULL; +pthread_t* client_threads = NULL; int num_client_threads = 0; pthread_mutex_t thread_count_mutex = PTHREAD_MUTEX_INITIALIZER; -SSL_CTX *ssl_ctx = NULL; +SSL_CTX* ssl_ctx = NULL; volatile sig_atomic_t server_running = 1; int http_socket = -1; int https_socket = -1; int epoll_fd; -RateLimit *rate_limits = NULL; +RateLimit* rate_limits = NULL; int rate_limit_count = 0; pthread_mutex_t rate_limit_mutex = PTHREAD_MUTEX_INITIALIZER; void cleanup_thread_pool(void); -void *handle_http_client(void *arg); -void *handle_https_client(void *arg); -void *worker_thread(void *arg); +void* handle_http_client(void* arg); +void* handle_https_client(void* arg); +void* worker_thread(void* arg); void set_cpu_affinity(int thread_id); void optimize_socket_for_send(int socket_fd); -void log_event(const char *message); +void log_event(const char* message); void initialize_openssl(); void cleanup_openssl(); -SSL_CTX *create_ssl_context(); -void configure_ssl_context(SSL_CTX *ctx); -void *start_http_server(void *arg); -void *start_https_server(void *arg); +SSL_CTX* create_ssl_context(); +void configure_ssl_context(SSL_CTX * ctx); +void* start_http_server(void* arg); +void* start_https_server(void* arg); void shutdown_server(); -int parse_request_line(char *request_buffer, char *method, char *url, char *protocol); -char *get_mime_type(const char *filepath); -char *sanitize_url(const char *url); -int check_rate_limit(const char *ip); -int should_compress(const char *mime_type); -unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *compressed_size); -char *stristr(const char *haystack, const char *needle); -char *extract_header_value(const char *request, const char *header_name); +int parse_request_line(char* request_buffer, char* method, char* url, char* protocol); +char* get_mime_type(const char* filepath); +char* sanitize_url(const char* url); +int check_rate_limit(const char* ip); +int should_compress(const char* mime_type); +unsigned char* gzip_compress(const unsigned char* data, size_t size, size_t* compressed_size); +char* stristr(const char* haystack, const char* needle); +char* extract_header_value(const char* request, const char* header_name); int calculate_dynamic_rate_limit(void); void initialize_openssl() @@ -183,10 +181,10 @@ void cleanup_openssl() #endif } -SSL_CTX *create_ssl_context() +SSL_CTX* create_ssl_context() { - const SSL_METHOD *method = TLS_server_method(); - SSL_CTX *ctx = SSL_CTX_new(method); + const SSL_METHOD* method = TLS_server_method(); + SSL_CTX* ctx = SSL_CTX_new(method); if (!ctx) { perror(BOLD RED "Unable to create SSL context" RESET); @@ -195,7 +193,7 @@ SSL_CTX *create_ssl_context() return ctx; } -void configure_ssl_context(SSL_CTX *ctx) +void configure_ssl_context(SSL_CTX* ctx) { if (SSL_CTX_use_certificate_file(ctx, config.ssl_cert_path, SSL_FILETYPE_PEM) <= 0) { @@ -207,7 +205,7 @@ void configure_ssl_context(SSL_CTX *ctx) ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } - + // Verify private key matches certificate if (SSL_CTX_check_private_key(ctx) != 1) { @@ -215,44 +213,44 @@ void configure_ssl_context(SSL_CTX *ctx) ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } - + // Security hardening - enforce TLS 1.2 minimum (TLS 1.3 preferred) SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); - + // Disable all insecure protocols and options - SSL_CTX_set_options(ctx, - SSL_OP_NO_SSLv2 | // Disable SSLv2 (CVE-2016-0800) - SSL_OP_NO_SSLv3 | // Disable SSLv3 (POODLE - CVE-2014-3566) - SSL_OP_NO_TLSv1 | // Disable TLS 1.0 (BEAST - CVE-2011-3389) - SSL_OP_NO_TLSv1_1 | // Disable TLS 1.1 (deprecated) - SSL_OP_NO_COMPRESSION | // Disable compression (CRIME - CVE-2012-4929) - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | - SSL_OP_NO_TICKET | // Disable session tickets for forward secrecy - SSL_OP_CIPHER_SERVER_PREFERENCE | // Server chooses cipher order - SSL_OP_SINGLE_DH_USE | // Generate new DH key for each handshake - SSL_OP_SINGLE_ECDH_USE // Generate new ECDH key for each handshake + SSL_CTX_set_options(ctx, + SSL_OP_NO_SSLv2 | // Disable SSLv2 (CVE-2016-0800) + SSL_OP_NO_SSLv3 | // Disable SSLv3 (POODLE - CVE-2014-3566) + SSL_OP_NO_TLSv1 | // Disable TLS 1.0 (BEAST - CVE-2011-3389) + SSL_OP_NO_TLSv1_1 | // Disable TLS 1.1 (deprecated) + SSL_OP_NO_COMPRESSION | // Disable compression (CRIME - CVE-2012-4929) + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | + SSL_OP_NO_TICKET | // Disable session tickets for forward secrecy + SSL_OP_CIPHER_SERVER_PREFERENCE | // Server chooses cipher order + SSL_OP_SINGLE_DH_USE | // Generate new DH key for each handshake + SSL_OP_SINGLE_ECDH_USE // Generate new ECDH key for each handshake ); - + // Use secure ciphers only - TLS 1.3 and strong TLS 1.2 AEAD ciphers // Prioritize ChaCha20 for mobile devices, AES-GCM for servers with AES-NI - const char *cipher_list = + const char* cipher_list = "TLS_AES_256_GCM_SHA384:" "TLS_CHACHA20_POLY1305_SHA256:" - "TLS_AES_128_GCM_SHA256:" // TLS 1.3 ciphers + "TLS_AES_128_GCM_SHA256:" // TLS 1.3 ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:" "ECDHE-RSA-AES256-GCM-SHA384:" "ECDHE-ECDSA-CHACHA20-POLY1305:" "ECDHE-RSA-CHACHA20-POLY1305:" "ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES128-GCM-SHA256:" // TLS 1.2 AEAD ciphers + "ECDHE-RSA-AES128-GCM-SHA256:" // TLS 1.2 AEAD ciphers "!aNULL:!eNULL:!EXPORT:!DES:!3DES:!RC4:!MD5:!PSK:!SRP:!DSS:!CBC"; - + if (SSL_CTX_set_cipher_list(ctx, cipher_list) != 1) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); } - + // Set ECDH curve for key exchange (prefer X25519, fall back to P-256) #if OPENSSL_VERSION_NUMBER >= 0x10101000L if (SSL_CTX_set1_groups_list(ctx, "X25519:P-256:P-384") != 1) @@ -260,7 +258,7 @@ void configure_ssl_context(SSL_CTX *ctx) LOG_WARN(LOG_CAT_SSL, "Failed to set ECDH groups, using defaults"); } #endif - + // Enable OCSP stapling if available #ifdef SSL_CTX_set_tlsext_status_type SSL_CTX_set_tlsext_status_type(ctx, TLSEXT_STATUSTYPE_ocsp); @@ -272,7 +270,7 @@ void configure_ssl_context(SSL_CTX *ctx) SSL_CTX_set_alpn_select_cb(ctx, alpn_select_proto_cb, NULL); LOG_INFO(LOG_CAT_SSL, "HTTP/2 ALPN enabled"); } - + LOG_INFO(LOG_CAT_SSL, "SSL/TLS context configured with secure settings"); } @@ -281,7 +279,7 @@ void optimize_socket_for_send(int socket_fd) int flag = 1; // Enable TCP_NODELAY to disable Nagle's algorithm for low latency setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)); - + #ifdef TCP_QUICKACK // Enable quick ACK for faster response setsockopt(socket_fd, IPPROTO_TCP, TCP_QUICKACK, &flag, sizeof(flag)); @@ -335,7 +333,7 @@ void set_socket_options(int socket_fd) setsockopt(socket_fd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf)); } -void *start_http_server(void *arg) +void* start_http_server(void* arg) { (void)arg; http_socket = socket(AF_INET, SOCK_STREAM, 0); @@ -352,7 +350,7 @@ void *start_http_server(void *arg) http_address.sin_addr.s_addr = INADDR_ANY; http_address.sin_port = htons(config.port); - if (bind(http_socket, (struct sockaddr *)&http_address, sizeof(http_address)) < 0) + if (bind(http_socket, (struct sockaddr*)&http_address, sizeof(http_address)) < 0) { perror(BOLD RED "Error binding HTTP socket" RESET); close(http_socket); @@ -394,7 +392,8 @@ void *start_http_server(void *arg) if (nfds == -1) { if (errno != EINTR) - { // Ignore interrupts for shutdown + { + // Ignore interrupts for shutdown perror("epoll_wait"); break; // Exit loop on error } @@ -408,7 +407,7 @@ void *start_http_server(void *arg) // New connection struct sockaddr_in client_addr; socklen_t addr_size = sizeof(client_addr); - int client_socket = accept(http_socket, (struct sockaddr *)&client_addr, &addr_size); + int client_socket = accept(http_socket, (struct sockaddr*)&client_addr, &addr_size); if (client_socket < 0) { perror("accept"); @@ -423,7 +422,7 @@ void *start_http_server(void *arg) else { log_event("Worker queue full, rejecting connection."); - const char *overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded"; + const char* overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded"; send(client_socket, overload_response, strlen(overload_response), 0); close(client_socket); } @@ -438,7 +437,7 @@ void *start_http_server(void *arg) pthread_exit(NULL); } -void *start_https_server(void *arg) +void* start_https_server(void* arg) { (void)arg; https_socket = socket(AF_INET, SOCK_STREAM, 0); @@ -456,7 +455,7 @@ void *start_https_server(void *arg) https_address.sin_addr.s_addr = INADDR_ANY; https_address.sin_port = htons(443); - if (bind(https_socket, (struct sockaddr *)&https_address, sizeof(https_address)) < 0) + if (bind(https_socket, (struct sockaddr*)&https_address, sizeof(https_address)) < 0) { perror(BOLD RED "Error binding HTTPS socket" RESET); close(https_socket); @@ -494,7 +493,7 @@ void *start_https_server(void *arg) else { log_event("Worker queue full (HTTPS), rejecting connection."); - const char *overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded"; + const char* overload_response = "HTTP/1.1 503 Service Unavailable\r\n\r\nServer overloaded"; send(client_socket, overload_response, strlen(overload_response), 0); close(client_socket); } @@ -505,14 +504,14 @@ void *start_https_server(void *arg) } // Check if request is a WebSocket upgrade request -static int is_websocket_upgrade(const char *request) +static int is_websocket_upgrade(const char* request) { // Make a lowercase copy for case-insensitive comparison - char *request_lower = strdup(request); + char* request_lower = strdup(request); if (!request_lower) return 0; - for (char *p = request_lower; *p; p++) + for (char* p = request_lower; *p; p++) { *p = tolower((unsigned char)*p); } @@ -520,25 +519,21 @@ static int is_websocket_upgrade(const char *request) // Check for "upgrade: websocket" and "connection:" containing "upgrade" int has_upgrade = strstr(request_lower, "upgrade: websocket") != NULL; int has_connection = strstr(request_lower, "connection:") != NULL && - strstr(request_lower, "upgrade") != NULL; + strstr(request_lower, "upgrade") != NULL; free(request_lower); return has_upgrade && has_connection; } // Handle WebSocket connection -static void *handle_websocket(void *arg) +static void* handle_websocket(void* arg) { - ws_connection_t *conn = (ws_connection_t *)arg; - - if (!conn) - { - pthread_exit(NULL); - } + ws_connection_t* conn = (ws_connection_t*)arg; + // Remove socket timeout for WebSocket (connections should stay open) struct timeval ws_timeout; - ws_timeout.tv_sec = 0; // No timeout - wait indefinitely + ws_timeout.tv_sec = 0; // No timeout - wait indefinitely ws_timeout.tv_usec = 0; setsockopt(conn->socket_fd, SOL_SOCKET, SO_RCVTIMEO, &ws_timeout, sizeof(ws_timeout)); @@ -570,7 +565,7 @@ static void *handle_websocket(void *arg) } ws_frame_header_t header; - uint8_t *payload = NULL; + uint8_t* payload = NULL; int parsed = ws_parse_frame(buffer, bytes_received, &header, &payload); if (parsed < 0) @@ -627,9 +622,9 @@ static void *handle_websocket(void *arg) pthread_exit(NULL); } -void *handle_http_client(void *arg) +void* handle_http_client(void* arg) { - int client_socket = *((int *)arg); + int client_socket = *((int*)arg); free(arg); if (!server_running) @@ -656,415 +651,415 @@ void *handle_http_client(void *arg) memset(request_buffer, 0, MAX_REQUEST_SIZE); ssize_t bytes_received = recv(client_socket, request_buffer, MAX_REQUEST_SIZE - 1, 0); - if (bytes_received > 0) - { - request_buffer[bytes_received] = '\0'; - log_event("Received HTTP request"); - - // Check if client accepts gzip BEFORE parsing (parse modifies buffer!) - int accepts_gzip = (stristr(request_buffer, "accept-encoding:") && - stristr(request_buffer, "gzip")) ? 1 : 0; - - // Check for WebSocket upgrade request - if (config.enable_websocket && is_websocket_upgrade(request_buffer)) + if (bytes_received > 0) { - log_event("WebSocket upgrade request detected"); + request_buffer[bytes_received] = '\0'; + log_event("Received HTTP request"); - char response[512]; - if (ws_handle_handshake(client_socket, request_buffer, response, sizeof(response)) == 0) + // Check if client accepts gzip BEFORE parsing (parse modifies buffer!) + int accepts_gzip = (stristr(request_buffer, "accept-encoding:") && + stristr(request_buffer, "gzip")) + ? 1 + : 0; + + // Check for WebSocket upgrade request + if (config.enable_websocket && is_websocket_upgrade(request_buffer)) { - send(client_socket, response, strlen(response), 0); + log_event("WebSocket upgrade request detected"); - // Create WebSocket connection context - ws_connection_t *ws_conn = malloc(sizeof(ws_connection_t)); - if (ws_conn) + char response[512]; + if (ws_handle_handshake(client_socket, request_buffer, response, sizeof(response)) == 0) { - ws_conn->socket_fd = client_socket; - ws_conn->ssl = NULL; - ws_conn->is_ssl = false; - ws_conn->handshake_complete = true; + send(client_socket, response, strlen(response), 0); - // Handle WebSocket connection in this thread - handle_websocket(ws_conn); + // Create WebSocket connection context + ws_connection_t* ws_conn = malloc(sizeof(ws_connection_t)); + if (ws_conn) + { + ws_conn->socket_fd = client_socket; + ws_conn->ssl = NULL; + ws_conn->is_ssl = false; + ws_conn->handshake_complete = true; + + // Handle WebSocket connection in this thread + handle_websocket(ws_conn); + } + else + { + close(client_socket); + } } else { + log_event("WebSocket handshake failed"); close(client_socket); } + pthread_exit(NULL); } - else + + char method[8], url[256], protocol[16]; + if (parse_request_line(request_buffer, method, url, protocol) != 0) { - log_event("WebSocket handshake failed"); + log_event("Invalid request line."); + const char* bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request"; + send(client_socket, bad_request_response, strlen(bad_request_response), 0); close(client_socket); + pthread_exit(NULL); } - pthread_exit(NULL); - } - char method[8], url[256], protocol[16]; - if (parse_request_line(request_buffer, method, url, protocol) != 0) - { - log_event("Invalid request line."); - const char *bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request"; - send(client_socket, bad_request_response, strlen(bad_request_response), 0); - close(client_socket); - pthread_exit(NULL); - } - - if (config.use_https) - { // Check if HTTPS is enabled - size_t needed = snprintf(NULL, 0, - "HTTP/1.1 301 Moved Permanently\r\n" - "Location: https://%s%s\r\n\r\n", - config.server_name, url) + - 1; - - char *redirect_response = malloc(needed); - if (redirect_response) + if (config.use_https) { - snprintf(redirect_response, needed, - "HTTP/1.1 301 Moved Permanently\r\n" - "Location: https://%s%s\r\n\r\n", - config.server_name, url); - send(client_socket, redirect_response, strlen(redirect_response), 0); - free(redirect_response); - } - log_event("Redirecting to HTTPS"); - close(client_socket); - return NULL; - } + // Check if HTTPS is enabled + size_t needed = snprintf(NULL, 0, + "HTTP/1.1 301 Moved Permanently\r\n" + "Location: https://%s%s\r\n\r\n", + config.server_name, url) + + 1; - char *sanitized_url = sanitize_url(url); - if (!sanitized_url) - { - log_event("Blocked malicious URL"); - const char *forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied"; - send(client_socket, forbidden_response, strlen(forbidden_response), 0); - close(client_socket); - pthread_exit(NULL); - } - - char client_ip[INET_ADDRSTRLEN]; - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - getpeername(client_socket, (struct sockaddr *)&addr, &addr_len); - inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip)); - - if (!check_rate_limit(client_ip)) - { - log_event("Rate limit exceeded for IP:"); - log_event(client_ip); - const char *rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded"; - send(client_socket, rate_limit_response, strlen(rate_limit_response), 0); - close(client_socket); - return NULL; - } - - char filepath[512]; - int written = snprintf(filepath, sizeof(filepath), "%s%s", config.www_path, - (*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url); - free(sanitized_url); - - if (written < 0 || written >= (int)sizeof(filepath)) - { - log_event("Path too long, potential buffer overflow attempt"); - const char *error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n"; - send(client_socket, error_response, strlen(error_response), 0); - close(client_socket); - pthread_exit(NULL); - } - - // Get MIME type - char *mime_type = get_mime_type(filepath); - - // Check for conditional request headers - char *if_none_match = extract_header_value(request_buffer, "If-None-Match"); - char *if_modified_since = extract_header_value(request_buffer, "If-Modified-Since"); - - // Try cache first - mmap_cache_entry_t *cached = get_cached_file(filepath); - - if (cached) - { - // Generate current ETag - char current_etag[128]; - snprintf(current_etag, sizeof(current_etag), "\"%zu-%ld\"", - cached->size, cached->last_access); - - // Check If-None-Match (ETag validation) - if (if_none_match) - { - // Strip quotes if present in the header value - char *etag_value = if_none_match; - if (*etag_value == '"') - etag_value++; - char *end_quote = strchr(etag_value, '"'); - if (end_quote) - *end_quote = '\0'; - - // Compare ETags (without quotes in stored ETag) - char stored_etag[128]; - snprintf(stored_etag, sizeof(stored_etag), "%zu-%ld", - cached->size, cached->last_access); - - if (strstr(if_none_match, stored_etag)) + char* redirect_response = malloc(needed); + if (redirect_response) { - // ETag matches - return 304 Not Modified - char response_304[512]; - int header_len = snprintf(response_304, sizeof(response_304), - "HTTP/1.1 304 Not Modified\\r\\n" - "ETag: %s\\r\\n" - "Cache-Control: public, max-age=86400, immutable\\r\\n" - "Keep-Alive: timeout=5, max=100\\r\\n" - "Connection: Keep-Alive\\r\\n" - "\\r\\n", - current_etag); - send(client_socket, response_304, header_len, 0); - release_cached_file(cached); - free(mime_type); - free(if_none_match); - if (if_modified_since) - free(if_modified_since); - log_event("Served 304 Not Modified (ETag match)"); - goto done_serving; + snprintf(redirect_response, needed, + "HTTP/1.1 301 Moved Permanently\r\n" + "Location: https://%s%s\r\n\r\n", + config.server_name, url); + send(client_socket, redirect_response, strlen(redirect_response), 0); + free(redirect_response); } + log_event("Redirecting to HTTPS"); + close(client_socket); + return NULL; } - - free(if_none_match); - free(if_modified_since); - - // Check if we should compress - unsigned char *compressed_data = NULL; - size_t compressed_size = 0; - int using_compression = 0; - int needs_free = 0; - - if (accepts_gzip && should_compress(cached->mime_type) && cached->size > 1024) + + char* sanitized_url = sanitize_url(url); + if (!sanitized_url) { - // Check if we have cached compressed version - if (cached->compressed_data && cached->compressed_size > 0) + log_event("Blocked malicious URL"); + const char* forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied"; + send(client_socket, forbidden_response, strlen(forbidden_response), 0); + close(client_socket); + pthread_exit(NULL); + } + + char client_ip[INET_ADDRSTRLEN]; + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + getpeername(client_socket, (struct sockaddr*)&addr, &addr_len); + inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip)); + + if (!check_rate_limit(client_ip)) + { + log_event("Rate limit exceeded for IP:"); + log_event(client_ip); + const char* rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded"; + send(client_socket, rate_limit_response, strlen(rate_limit_response), 0); + close(client_socket); + return NULL; + } + + char filepath[512]; + int written = snprintf(filepath, sizeof(filepath), "%s%s", config.www_path, + (*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url); + free(sanitized_url); + + if (written < 0 || written >= (int)sizeof(filepath)) + { + log_event("Path too long, potential buffer overflow attempt"); + const char* error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n"; + send(client_socket, error_response, strlen(error_response), 0); + close(client_socket); + pthread_exit(NULL); + } + + // Get MIME type + char* mime_type = get_mime_type(filepath); + + // Check for conditional request headers + char* if_none_match = extract_header_value(request_buffer, "If-None-Match"); + char* if_modified_since = extract_header_value(request_buffer, "If-Modified-Since"); + + // Try cache first + mmap_cache_entry_t* cached = get_cached_file(filepath); + + if (cached) + { + // Generate current ETag + char current_etag[128]; + snprintf(current_etag, sizeof(current_etag), "\"%zu-%ld\"", + cached->size, cached->last_access); + + // Check If-None-Match (ETag validation) + if (if_none_match) { - // Use pre-compressed cached data - compressed_data = cached->compressed_data; - compressed_size = cached->compressed_size; - using_compression = 1; - needs_free = 0; + // Strip quotes if present in the header value + char* etag_value = if_none_match; + if (*etag_value == '"') + etag_value++; + char* end_quote = strchr(etag_value, '"'); + if (end_quote) + *end_quote = '\0'; + + // Compare ETags (without quotes in stored ETag) + char stored_etag[128]; + snprintf(stored_etag, sizeof(stored_etag), "%zu-%ld", + cached->size, cached->last_access); + + if (strstr(if_none_match, stored_etag)) + { + // ETag matches - return 304 Not Modified + char response_304[512]; + int header_len = snprintf(response_304, sizeof(response_304), + "HTTP/1.1 304 Not Modified\\r\\n" + "ETag: %s\\r\\n" + "Cache-Control: public, max-age=86400, immutable\\r\\n" + "Keep-Alive: timeout=5, max=100\\r\\n" + "Connection: Keep-Alive\\r\\n" + "\\r\\n", + current_etag); + send(client_socket, response_304, header_len, 0); + release_cached_file(cached); + free(mime_type); + free(if_none_match); + if (if_modified_since) + free(if_modified_since); + log_event("Served 304 Not Modified (ETag match)"); + goto done_serving; + } + } + + free(if_none_match); + free(if_modified_since); + + // Check if we should compress + unsigned char* compressed_data = NULL; + size_t compressed_size = 0; + int using_compression = 0; + int needs_free = 0; + + if (accepts_gzip && should_compress(cached->mime_type) && cached->size > 1024) + { + // Check if we have cached compressed version + if (cached->compressed_data && cached->compressed_size > 0) + { + // Use pre-compressed cached data + compressed_data = cached->compressed_data; + compressed_size = cached->compressed_size; + using_compression = 1; + needs_free = 0; + } + else + { + // Compress on-the-fly and cache it + compressed_data = gzip_compress((unsigned char*)cached->mmap_data, cached->size, + &compressed_size); + if (compressed_data && compressed_size < cached->size * 0.9) + { + using_compression = 1; + needs_free = 1; + + // Cache the compressed version for future requests + cached->compressed_data = malloc(compressed_size); + if (cached->compressed_data) + { + memcpy(cached->compressed_data, compressed_data, compressed_size); + cached->compressed_size = compressed_size; + needs_free = 0; + } + } + else if (compressed_data) + { + free(compressed_data); + compressed_data = NULL; + } + } + } + + // Serve from cache with optional compression + char response_header[2048]; + + // Format Last-Modified time (RFC 7231 format) + char last_modified[64]; + struct tm* tm_info = gmtime(&cached->last_access); + strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); + + int header_len = snprintf(response_header, sizeof(response_header), + "HTTP/1.1 200 OK\r\n" + "Content-Length: %zu\r\n" + "Content-Type: %s\r\n" + "Cache-Control: public, max-age=86400, immutable\r\n" + "ETag: \"%zu-%ld\"\r\n" + "Last-Modified: %s\r\n" + "%s" + "%s" + "Keep-Alive: timeout=5, max=100\r\n" + "Connection: Keep-Alive\r\n" + "\r\n", + using_compression ? compressed_size : cached->size, + cached->mime_type, + cached->size, + cached->last_access, + last_modified, + using_compression ? "Content-Encoding: gzip\r\n" : "", + SECURITY_HEADERS); + + void* data_to_send = using_compression ? compressed_data : cached->mmap_data; + size_t size_to_send = using_compression ? compressed_size : cached->size; + + // Use writev to send header + content in one syscall (for small files) + if (size_to_send < 65536) // Files < 64KB + { + struct iovec iov[2]; + iov[0].iov_base = response_header; + iov[0].iov_len = header_len; + iov[1].iov_base = data_to_send; + iov[1].iov_len = size_to_send; + ssize_t written = writev(client_socket, iov, 2); + (void)written; } else { - // Compress on-the-fly and cache it - compressed_data = gzip_compress((unsigned char *)cached->mmap_data, cached->size, &compressed_size); - if (compressed_data && compressed_size < cached->size * 0.9) + send(client_socket, response_header, header_len, 0); + + size_t total_sent = 0; + while (total_sent < size_to_send) { - using_compression = 1; - needs_free = 1; - - // Cache the compressed version for future requests - cached->compressed_data = malloc(compressed_size); - if (cached->compressed_data) - { - memcpy(cached->compressed_data, compressed_data, compressed_size); - cached->compressed_size = compressed_size; - needs_free = 0; - } - } - else if (compressed_data) - { - free(compressed_data); - compressed_data = NULL; + ssize_t sent = send(client_socket, (char*)data_to_send + total_sent, + size_to_send - total_sent, 0); + if (sent <= 0) + break; + total_sent += sent; } } + + if (needs_free && compressed_data) + free(compressed_data); + release_cached_file(cached); + free(mime_type); + log_event(using_compression ? "Served file from cache (gzip)" : "Served file from cache"); + goto done_serving; } - // Serve from cache with optional compression - char response_header[2048]; - - // Format Last-Modified time (RFC 7231 format) - char last_modified[64]; - struct tm *tm_info = gmtime(&cached->last_access); - strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); - - int header_len = snprintf(response_header, sizeof(response_header), - "HTTP/1.1 200 OK\r\n" - "Content-Length: %zu\r\n" - "Content-Type: %s\r\n" - "Cache-Control: public, max-age=86400, immutable\r\n" - "ETag: \"%zu-%ld\"\r\n" - "Last-Modified: %s\r\n" - "%s" - "%s" - "Keep-Alive: timeout=5, max=100\r\n" - "Connection: Keep-Alive\r\n" - "\r\n", - using_compression ? compressed_size : cached->size, - cached->mime_type, - cached->size, - cached->last_access, - last_modified, - using_compression ? "Content-Encoding: gzip\r\n" : "", - SECURITY_HEADERS); - - void *data_to_send = using_compression ? compressed_data : cached->mmap_data; - size_t size_to_send = using_compression ? compressed_size : cached->size; - - // Use writev to send header + content in one syscall (for small files) - if (size_to_send < 65536) // Files < 64KB + int fd = open(filepath, O_RDONLY); + if (fd == -1) { - struct iovec iov[2]; - iov[0].iov_base = response_header; - iov[0].iov_len = header_len; - iov[1].iov_base = data_to_send; - iov[1].iov_len = size_to_send; - ssize_t written = writev(client_socket, iov, 2); - (void)written; - } - else - { - send(client_socket, response_header, header_len, 0); - - size_t total_sent = 0; - while (total_sent < size_to_send) - { - ssize_t sent = send(client_socket, (char *)data_to_send + total_sent, - size_to_send - total_sent, 0); - if (sent <= 0) - break; - total_sent += sent; - } - } - - if (needs_free && compressed_data) - free(compressed_data); - release_cached_file(cached); - free(mime_type); - log_event(using_compression ? "Served file from cache (gzip)" : "Served file from cache"); - goto done_serving; - } - - int fd = open(filepath, O_RDONLY); - if (fd == -1) - { - const char *not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"; - send(client_socket, not_found_response, strlen(not_found_response), 0); - free(mime_type); - if (if_none_match) - free(if_none_match); - if (if_modified_since) - free(if_modified_since); - log_event("File not found, sent 404."); - } - else - { - struct stat st; - if (fstat(fd, &st) == -1) - { - log_event("Error getting file size."); - const char *internal_server_error = - "HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error"; - send(client_socket, internal_server_error, strlen(internal_server_error), 0); - close(fd); + const char* not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"; + send(client_socket, not_found_response, strlen(not_found_response), 0); free(mime_type); if (if_none_match) free(if_none_match); if (if_modified_since) free(if_modified_since); - goto cleanup; + log_event("File not found, sent 404."); } - - // Check If-None-Match for non-cached files - char current_etag[128]; - snprintf(current_etag, sizeof(current_etag), "\"%ld-%ld\"", - (long)st.st_size, (long)st.st_mtime); - - if (if_none_match && strstr(if_none_match, current_etag + 1)) + else { - // ETag matches - return 304 - char response_304[512]; + struct stat st; + if (fstat(fd, &st) == -1) + { + log_event("Error getting file size."); + const char* internal_server_error = + "HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error"; + send(client_socket, internal_server_error, strlen(internal_server_error), 0); + close(fd); + free(mime_type); + if (if_none_match) + free(if_none_match); + if (if_modified_since) + free(if_modified_since); + goto cleanup; + } + + // Check If-None-Match for non-cached files + char current_etag[128]; + snprintf(current_etag, sizeof(current_etag), "\"%ld-%ld\"", + (long)st.st_size, (long)st.st_mtime); + + if (if_none_match && strstr(if_none_match, current_etag + 1)) + { + // ETag matches - return 304 + char response_304[512]; + char last_modified[64]; + struct tm* tm_info = gmtime(&st.st_mtime); + strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); + + int header_len = snprintf(response_304, sizeof(response_304), + "HTTP/1.1 304 Not Modified\r\n" + "ETag: %s\r\n" + "Last-Modified: %s\r\n" + "Cache-Control: public, max-age=86400\r\n" + "Keep-Alive: timeout=5, max=100\r\n" + "Connection: Keep-Alive\r\n" + "\r\n", + current_etag, last_modified); + send(client_socket, response_304, header_len, 0); + close(fd); + free(mime_type); + free(if_none_match); + if (if_modified_since) + free(if_modified_since); + log_event("Served 304 Not Modified (file ETag match)"); + goto done_serving; + } + + if (if_none_match) + free(if_none_match); + if (if_modified_since) + free(if_modified_since); + + // Cache if eligible + if (st.st_size > 0 && st.st_size < MAX_MMAP_FILE_SIZE) + { + cache_file_mmap(filepath, st.st_size, mime_type); + } + + char response_header[2048]; + + // Format Last-Modified time char last_modified[64]; - struct tm *tm_info = gmtime(&st.st_mtime); + struct tm* tm_info = gmtime(&st.st_mtime); strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); - - int header_len = snprintf(response_304, sizeof(response_304), - "HTTP/1.1 304 Not Modified\r\n" - "ETag: %s\r\n" - "Last-Modified: %s\r\n" + + int header_len = snprintf(response_header, sizeof(response_header), + "HTTP/1.1 200 OK\r\n" + "Content-Length: %ld\r\n" + "Content-Type: %s\r\n" "Cache-Control: public, max-age=86400\r\n" + "ETag: \"%ld-%ld\"\r\n" + "Last-Modified: %s\r\n" + "%s" "Keep-Alive: timeout=5, max=100\r\n" "Connection: Keep-Alive\r\n" "\r\n", - current_etag, last_modified); - send(client_socket, response_304, header_len, 0); - close(fd); + (long)st.st_size, + mime_type, + (long)st.st_size, + (long)st.st_mtime, + last_modified, + SECURITY_HEADERS); + free(mime_type); - free(if_none_match); - if (if_modified_since) - free(if_modified_since); - log_event("Served 304 Not Modified (file ETag match)"); - goto done_serving; - } - - if (if_none_match) - free(if_none_match); - if (if_modified_since) - free(if_modified_since); - // Cache if eligible - if (st.st_size > 0 && st.st_size < MAX_MMAP_FILE_SIZE) - { - cache_file_mmap(filepath, st.st_size, mime_type); + send(client_socket, response_header, header_len, 0); + + // Use sendfile for zero-copy transfer + off_t offset = 0; + ssize_t sent = sendfile(client_socket, fd, &offset, st.st_size); + if (sent != st.st_size) + { + log_event("Error sending file with sendfile()"); + } + + close(fd); + log_event("Served requested file successfully."); } - char response_header[2048]; - - // Format Last-Modified time - char last_modified[64]; - struct tm *tm_info = gmtime(&st.st_mtime); - strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); - - int header_len = snprintf(response_header, sizeof(response_header), - "HTTP/1.1 200 OK\r\n" - "Content-Length: %ld\r\n" - "Content-Type: %s\r\n" - "Cache-Control: public, max-age=86400\r\n" - "ETag: \"%ld-%ld\"\r\n" - "Last-Modified: %s\r\n" - "%s" - "Keep-Alive: timeout=5, max=100\r\n" - "Connection: Keep-Alive\r\n" - "\r\n", - (long)st.st_size, - mime_type, - (long)st.st_size, - (long)st.st_mtime, - last_modified, - SECURITY_HEADERS); - - free(mime_type); - - send(client_socket, response_header, header_len, 0); - - // Use sendfile for zero-copy transfer - off_t offset = 0; - ssize_t sent = sendfile(client_socket, fd, &offset, st.st_size); - if (sent != st.st_size) - { - log_event("Error sending file with sendfile()"); - } - - close(fd); - log_event("Served requested file successfully."); + done_serving: + continue; + } + else if (bytes_received < 0) + { + break; } - - done_serving: - continue; - } - else if (bytes_received < 0) - { - break; - } - else if (bytes_received == 0) - { - break; - } } close(client_socket); @@ -1075,12 +1070,12 @@ cleanup: pthread_exit(NULL); } -void *handle_https_client(void *arg) +void* handle_https_client(void* arg) { - int client_socket = *((int *)arg); + int client_socket = *((int*)arg); free(arg); - SSL *ssl = SSL_new(ssl_ctx); + SSL* ssl = SSL_new(ssl_ctx); if (!ssl) { log_event("SSL_new failed"); @@ -1114,7 +1109,7 @@ void *handle_https_client(void *arg) // Check if HTTP/2 was negotiated via ALPN if (config.enable_http2) { - const unsigned char *alpn_data = NULL; + const unsigned char* alpn_data = NULL; unsigned int alpn_len = 0; SSL_get0_alpn_selected(ssl, &alpn_data, &alpn_len); @@ -1195,7 +1190,7 @@ void *handle_https_client(void *arg) SSL_write(ssl, response, strlen(response)); // Create WebSocket connection context - ws_connection_t *ws_conn = malloc(sizeof(ws_connection_t)); + ws_connection_t* ws_conn = malloc(sizeof(ws_connection_t)); if (ws_conn) { ws_conn->socket_fd = client_socket; @@ -1226,7 +1221,7 @@ void *handle_https_client(void *arg) if (parse_request_line(buffer, method, url, protocol) != 0) { log_event("Invalid request line."); - const char *bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request"; + const char* bad_request_response = "HTTP/1.1 400 Bad Request\r\n\r\nInvalid Request"; SSL_write(ssl, bad_request_response, strlen(bad_request_response)); goto cleanup; } @@ -1240,15 +1235,11 @@ void *handle_https_client(void *arg) log_event(protocol); } - // Check if client accepts gzip (case-insensitive) - int accepts_gzip = (stristr(buffer, "accept-encoding:") && - stristr(buffer, "gzip")) ? 1 : 0; - - char *sanitized_url = sanitize_url(url); + char* sanitized_url = sanitize_url(url); if (!sanitized_url) { log_event("Blocked malicious URL"); - const char *forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied"; + const char* forbidden_response = "HTTP/1.1 403 Forbidden\r\n\r\nAccess Denied"; SSL_write(ssl, forbidden_response, strlen(forbidden_response)); goto cleanup; } @@ -1256,27 +1247,27 @@ void *handle_https_client(void *arg) char client_ip[INET_ADDRSTRLEN]; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); - getpeername(client_socket, (struct sockaddr *)&addr, &addr_len); + getpeername(client_socket, (struct sockaddr*)&addr, &addr_len); inet_ntop(AF_INET, &addr.sin_addr, client_ip, sizeof(client_ip)); if (!check_rate_limit(client_ip)) { log_event("Rate limit exceeded for IP:"); log_event(client_ip); - const char *rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded"; + const char* rate_limit_response = "HTTP/1.1 429 Too Many Requests\r\n\r\nRate limit exceeded"; SSL_write(ssl, rate_limit_response, strlen(rate_limit_response)); goto cleanup; } char filepath[512]; int written = snprintf(filepath, sizeof(filepath), "%s%s", config.www_path, - (*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url); + (*sanitized_url == '/' && sanitized_url[1] == '\0') ? "/index.html" : sanitized_url); free(sanitized_url); - + if (written < 0 || written >= (int)sizeof(filepath)) { log_event("Path too long, potential buffer overflow attempt (HTTPS)"); - const char *error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n"; + const char* error_response = "HTTP/1.1 414 URI Too Long\r\n\r\n"; SSL_write(ssl, error_response, strlen(error_response)); goto cleanup; } @@ -1284,29 +1275,29 @@ void *handle_https_client(void *arg) log_event(filepath); // Get MIME type - char *mime_type = get_mime_type(filepath); + char* mime_type = get_mime_type(filepath); // Check for conditional request headers - char *if_none_match = extract_header_value(buffer, "If-None-Match"); - char *if_modified_since = extract_header_value(buffer, "If-Modified-Since"); + char* if_none_match = extract_header_value(buffer, "If-None-Match"); + char* if_modified_since = extract_header_value(buffer, "If-Modified-Since"); // Try to get file from cache first - mmap_cache_entry_t *cached = get_cached_file(filepath); + mmap_cache_entry_t* cached = get_cached_file(filepath); if (cached) { // Generate current ETag char current_etag[128]; - snprintf(current_etag, sizeof(current_etag), "\"%zu-%ld\"", + snprintf(current_etag, sizeof(current_etag), "\"%zu-%ld\"", cached->size, cached->last_access); - + // Check If-None-Match (ETag validation) if (if_none_match) { char stored_etag[128]; - snprintf(stored_etag, sizeof(stored_etag), "%zu-%ld", + snprintf(stored_etag, sizeof(stored_etag), "%zu-%ld", cached->size, cached->last_access); - + if (strstr(if_none_match, stored_etag)) { // ETag matches - return 304 Not Modified @@ -1328,17 +1319,17 @@ void *handle_https_client(void *arg) goto cleanup; } } - + free(if_none_match); free(if_modified_since); - + // Check if we should compress - unsigned char *compressed_data = NULL; + unsigned char* compressed_data = NULL; size_t compressed_size = 0; int using_compression = 0; int needs_free = 0; - - if (accepts_gzip && should_compress(cached->mime_type) && cached->size > 1024) + + if (should_compress(cached->mime_type) && cached->size > 1024) { // Check if we have cached compressed version if (cached->compressed_data && cached->compressed_size > 0) @@ -1350,12 +1341,12 @@ void *handle_https_client(void *arg) } else { - compressed_data = gzip_compress((unsigned char *)cached->mmap_data, cached->size, &compressed_size); + compressed_data = gzip_compress((unsigned char*)cached->mmap_data, cached->size, &compressed_size); if (compressed_data && compressed_size < cached->size * 0.9) { using_compression = 1; needs_free = 1; - + // Cache the compressed version cached->compressed_data = malloc(compressed_size); if (cached->compressed_data) @@ -1375,12 +1366,12 @@ void *handle_https_client(void *arg) // Serve from cache with optional compression char response_header[2048]; - + // Format Last-Modified time char last_modified[64]; - struct tm *tm_info = gmtime(&cached->last_access); + struct tm* tm_info = gmtime(&cached->last_access); strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); - + int header_len = snprintf(response_header, sizeof(response_header), "HTTP/1.1 200 OK\r\n" "Content-Length: %zu\r\n" @@ -1404,14 +1395,14 @@ void *handle_https_client(void *arg) SSL_write(ssl, response_header, header_len); // Send compressed or uncompressed data - void *data_to_send = using_compression ? compressed_data : cached->mmap_data; + void* data_to_send = using_compression ? compressed_data : cached->mmap_data; size_t size_to_send = using_compression ? compressed_size : cached->size; - + size_t total_sent = 0; while (total_sent < size_to_send) { int to_send = (size_to_send - total_sent > 65536) ? 65536 : (size_to_send - total_sent); - int sent = SSL_write(ssl, (char *)data_to_send + total_sent, to_send); + int sent = SSL_write(ssl, (char*)data_to_send + total_sent, to_send); if (sent <= 0) break; total_sent += sent; @@ -1430,7 +1421,7 @@ void *handle_https_client(void *arg) if (fd == -1) { log_event("File open failed"); - const char *not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"; + const char* not_found_response = "HTTP/1.1 404 Not Found\r\n\r\nFile Not Found"; SSL_write(ssl, not_found_response, strlen(not_found_response)); free(mime_type); if (if_none_match) @@ -1444,7 +1435,7 @@ void *handle_https_client(void *arg) if (fstat(fd, &st) == -1) { log_event("Error getting file size."); - const char *internal_server_error = + const char* internal_server_error = "HTTP/1.1 500 Internal Server Error\r\n\r\nInternal Server Error"; SSL_write(ssl, internal_server_error, strlen(internal_server_error)); close(fd); @@ -1458,17 +1449,17 @@ void *handle_https_client(void *arg) // Check If-None-Match for non-cached files char current_etag[128]; - snprintf(current_etag, sizeof(current_etag), "\"%ld-%ld\"", + snprintf(current_etag, sizeof(current_etag), "\"%ld-%ld\"", (long)st.st_size, (long)st.st_mtime); - + if (if_none_match && strstr(if_none_match, current_etag + 1)) { // ETag matches - return 304 char response_304[512]; char last_modified[64]; - struct tm *tm_info = gmtime(&st.st_mtime); + struct tm* tm_info = gmtime(&st.st_mtime); strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); - + int header_len = snprintf(response_304, sizeof(response_304), "HTTP/1.1 304 Not Modified\r\n" "ETag: %s\r\n" @@ -1486,7 +1477,7 @@ void *handle_https_client(void *arg) log_event("Served 304 Not Modified via HTTPS (file ETag match)"); goto cleanup; } - + if (if_none_match) free(if_none_match); if (if_modified_since) @@ -1499,12 +1490,12 @@ void *handle_https_client(void *arg) } char response_header[2048]; - + // Format Last-Modified time char last_modified[64]; - struct tm *tm_info = gmtime(&st.st_mtime); + struct tm* tm_info = gmtime(&st.st_mtime); strftime(last_modified, sizeof(last_modified), "%a, %d %b %Y %H:%M:%S GMT", tm_info); - + int header_len = snprintf(response_header, sizeof(response_header), "HTTP/1.1 200 OK\r\n" "Content-Length: %ld\r\n" @@ -1528,7 +1519,7 @@ void *handle_https_client(void *arg) SSL_write(ssl, response_header, header_len); // Use larger buffer for better performance - char *file_buffer = get_buffer_from_pool(16384); + char* file_buffer = get_buffer_from_pool(16384); ssize_t bytes_read; while ((bytes_read = read(fd, file_buffer, 16384)) > 0) { @@ -1543,11 +1534,9 @@ void *handle_https_client(void *arg) log_event("Served requested file successfully."); cleanup: - if (ssl) - { - SSL_shutdown(ssl); - SSL_free(ssl); - } + + SSL_shutdown(ssl); + SSL_free(ssl); close(client_socket); pthread_exit(NULL); } @@ -1638,7 +1627,7 @@ void shutdown_server() log_event("Server shutdown completed."); } -int parse_request_line(char *request_buffer, char *method, char *url, char *protocol) +int parse_request_line(char* request_buffer, char* method, char* url, char* protocol) { if (!request_buffer || !method || !url || !protocol) return -1; @@ -1648,12 +1637,12 @@ int parse_request_line(char *request_buffer, char *method, char *url, char *prot protocol[0] = '\0'; char *saveptr1, *saveptr2; - char *line = strtok_r(request_buffer, "\r\n", &saveptr1); + char* line = strtok_r(request_buffer, "\r\n", &saveptr1); if (line == NULL || strlen(line) == 0) return -1; - char *token = strtok_r(line, " ", &saveptr2); + char* token = strtok_r(line, " ", &saveptr2); if (token == NULL || strlen(token) > 7) return -1; strncpy(method, token, 7); @@ -1722,7 +1711,7 @@ void set_cpu_affinity(int thread_id) cpu_set_t cpuset; CPU_ZERO(&cpuset); CPU_SET(thread_id % num_cpus, &cpuset); - + if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset) != 0) { log_event("Warning: Failed to set CPU affinity"); @@ -1731,35 +1720,35 @@ void set_cpu_affinity(int thread_id) #endif } -void *worker_thread(void *arg) +void* worker_thread(void* arg) { - int thread_id = *((int *)arg); + int thread_id = *((int*)arg); free(arg); - + // Set CPU affinity for this worker thread set_cpu_affinity(thread_id); - + char log_msg[256]; int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); snprintf(log_msg, sizeof(log_msg), "Worker thread %d started on CPU %d", thread_id, thread_id % num_cpus); log_event(log_msg); - + while (workers_running) { - connection_task_t *task = dequeue_task(&worker_queue); - + connection_task_t* task = dequeue_task(&worker_queue); + if (!task || !workers_running) { break; } - + // Optimize socket before handling optimize_socket_for_send(task->socket_fd); - + // Handle the connection based on type if (task->is_https) { - int *socket_ptr = malloc(sizeof(int)); + int* socket_ptr = malloc(sizeof(int)); if (socket_ptr) { *socket_ptr = task->socket_fd; @@ -1768,17 +1757,17 @@ void *worker_thread(void *arg) } else { - int *socket_ptr = malloc(sizeof(int)); + int* socket_ptr = malloc(sizeof(int)); if (socket_ptr) { *socket_ptr = task->socket_fd; handle_http_client(socket_ptr); } } - + free(task); } - + return NULL; } @@ -1790,10 +1779,10 @@ void initialize_thread_pool() { log_event("Failed to allocate thread pool"); } - + // Initialize worker queue init_task_queue(&worker_queue); - + // Create worker threads int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); num_worker_threads = (num_cpus > 0) ? num_cpus * 2 : 8; @@ -1801,17 +1790,17 @@ void initialize_thread_pool() { num_worker_threads = MAX_THREAD_POOL_SIZE; } - + worker_threads = calloc(num_worker_threads, sizeof(pthread_t)); if (!worker_threads) { log_event("Failed to allocate worker threads"); return; } - + for (int i = 0; i < num_worker_threads; i++) { - int *thread_id = malloc(sizeof(int)); + int* thread_id = malloc(sizeof(int)); if (thread_id) { *thread_id = i; @@ -1822,21 +1811,21 @@ void initialize_thread_pool() } } } - + char msg[256]; snprintf(msg, sizeof(msg), "Initialized %d worker threads on %d CPUs", num_worker_threads, num_cpus); log_event(msg); } // Case-insensitive strstr -char *stristr(const char *haystack, const char *needle) +char* stristr(const char* haystack, const char* needle) { if (!haystack || !needle) return NULL; - + size_t needle_len = strlen(needle); - if (needle_len == 0) return (char *)haystack; - - for (const char *p = haystack; *p; p++) + if (needle_len == 0) return (char*)haystack; + + for (const char* p = haystack; *p; p++) { if (tolower(*p) == tolower(*needle)) { @@ -1847,23 +1836,23 @@ char *stristr(const char *haystack, const char *needle) break; } if (i == needle_len) - return (char *)p; + return (char*)p; } } return NULL; } // Check if MIME type should be compressed -int should_compress(const char *mime_type) +int should_compress(const char* mime_type) { return (strstr(mime_type, "text/") != NULL || - strstr(mime_type, "application/javascript") != NULL || - strstr(mime_type, "application/json") != NULL || - strstr(mime_type, "application/xml") != NULL); + strstr(mime_type, "application/javascript") != NULL || + strstr(mime_type, "application/json") != NULL || + strstr(mime_type, "application/xml") != NULL); } // Gzip compress data -unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *compressed_size) +unsigned char* gzip_compress(const unsigned char* data, size_t size, size_t* compressed_size) { z_stream stream = {0}; stream.zalloc = Z_NULL; @@ -1876,7 +1865,7 @@ unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *com } size_t max_compressed = deflateBound(&stream, size); - unsigned char *compressed = malloc(max_compressed); + unsigned char* compressed = malloc(max_compressed); if (!compressed) { deflateEnd(&stream); @@ -1884,7 +1873,7 @@ unsigned char *gzip_compress(const unsigned char *data, size_t size, size_t *com } stream.avail_in = size; - stream.next_in = (unsigned char *)data; + stream.next_in = (unsigned char*)data; stream.avail_out = max_compressed; stream.next_out = compressed; @@ -1905,7 +1894,7 @@ int main() { // Initialize default config first init_config(&config); - + if (load_config("server.conf", &config) != 0) { printf("Using default configuration.\n"); @@ -1915,10 +1904,13 @@ int main() // Initialize logging system based on config LogConfig log_cfg = { - .level = (config.log_mode == LOG_MODE_OFF) ? LOG_LEVEL_OFF : - (config.log_mode == LOG_MODE_DEBUG) ? LOG_LEVEL_DEBUG : - (config.log_mode == LOG_MODE_ADVANCED) ? LOG_LEVEL_TRACE : - LOG_LEVEL_INFO, + .level = (config.log_mode == LOG_MODE_OFF) + ? LOG_LEVEL_OFF + : (config.log_mode == LOG_MODE_DEBUG) + ? LOG_LEVEL_DEBUG + : (config.log_mode == LOG_MODE_ADVANCED) + ? LOG_LEVEL_TRACE + : LOG_LEVEL_INFO, .categories = LOG_CAT_ALL, .format = LOG_FORMAT_PLAIN, .console_output = true, @@ -1935,9 +1927,9 @@ int main() // Calculate dynamic rate limit based on system resources MAX_REQUESTS_DYNAMIC = calculate_dynamic_rate_limit(); - + char rate_limit_msg[256]; - snprintf(rate_limit_msg, sizeof(rate_limit_msg), + snprintf(rate_limit_msg, sizeof(rate_limit_msg), "Dynamic rate limit set to %d requests per IP per minute", MAX_REQUESTS_DYNAMIC); LOG_INFO(LOG_CAT_GENERAL, "%s", rate_limit_msg); @@ -2009,9 +2001,9 @@ int main() } -char *get_mime_type(const char *filepath) +char* get_mime_type(const char* filepath) { - const char *ext = strrchr(filepath, '.'); + const char* ext = strrchr(filepath, '.'); if (!ext) return strdup("application/octet-stream"); @@ -2055,14 +2047,14 @@ char *get_mime_type(const char *filepath) return strdup("application/octet-stream"); } - const char *mime = magic_file(magic, filepath); - char *result = mime ? strdup(mime) : strdup("application/octet-stream"); + const char* mime = magic_file(magic, filepath); + char* result = mime ? strdup(mime) : strdup("application/octet-stream"); magic_close(magic); return result; } -char *sanitize_url(const char *url) +char* sanitize_url(const char* url) { if (!url) return NULL; @@ -2071,7 +2063,7 @@ char *sanitize_url(const char *url) if (url_len == 0 || url_len > 2048) return NULL; - char *sanitized = calloc(1, url_len + 2); + char* sanitized = calloc(1, url_len + 2); if (!sanitized) { log_event("Memory allocation failed in sanitize_url"); @@ -2123,7 +2115,8 @@ char *sanitize_url(const char *url) { consecutive_dots++; if (consecutive_dots > 2) - { // Too many dots + { + // Too many dots free(sanitized); return NULL; } @@ -2151,15 +2144,15 @@ char *sanitize_url(const char *url) { char hex[3] = {url[i + 1], url[i + 2], 0}; int decoded = (int)strtol(hex, NULL, 16); - + // Block encoded directory traversal characters and control characters - if (decoded == '.' || decoded == '/' || decoded == '\\' || + if (decoded == '.' || decoded == '/' || decoded == '\\' || decoded == 0x00 || decoded < 0x20 || decoded > 0x7E) { free(sanitized); return NULL; } - + sanitized[j++] = (char)decoded; i += 2; } @@ -2194,7 +2187,7 @@ char *sanitize_url(const char *url) return sanitized; } -int check_rate_limit(const char *ip) +int check_rate_limit(const char* ip) { pthread_mutex_lock(&rate_limit_mutex); @@ -2240,7 +2233,7 @@ int check_rate_limit(const char *ip) } // Add new entry - RateLimit *new_limits = realloc(rate_limits, (rate_limit_count + 1) * sizeof(RateLimit)); + RateLimit* new_limits = realloc(rate_limits, (rate_limit_count + 1) * sizeof(RateLimit)); if (!new_limits) { pthread_mutex_unlock(&rate_limit_mutex); @@ -2267,12 +2260,12 @@ void cleanup_thread_pool() { // Signal worker threads to stop workers_running = 0; - + // Wake up all waiting workers pthread_mutex_lock(&worker_queue.mutex); pthread_cond_broadcast(&worker_queue.cond); pthread_mutex_unlock(&worker_queue.mutex); - + // Join all worker threads if (worker_threads) { @@ -2283,10 +2276,10 @@ void cleanup_thread_pool() free(worker_threads); worker_threads = NULL; } - + // Cleanup worker queue destroy_task_queue(&worker_queue); - + // Cleanup old thread pool structure if exists if (!thread_pool) { @@ -2294,49 +2287,49 @@ void cleanup_thread_pool() } pthread_mutex_lock(&thread_pool_mutex); - - ThreadInfo *temp = thread_pool; + + ThreadInfo* temp = thread_pool; thread_pool = NULL; thread_pool_size = 0; - + pthread_mutex_unlock(&thread_pool_mutex); - + // Free after releasing lock and nullifying pointer free(temp); } // Extract header value from HTTP request -char *extract_header_value(const char *request, const char *header_name) +char* extract_header_value(const char* request, const char* header_name) { - char *header_start = stristr(request, header_name); + char* header_start = stristr(request, header_name); if (!header_start) return NULL; - + header_start += strlen(header_name); while (*header_start == ' ' || *header_start == ':') header_start++; - - char *header_end = strstr(header_start, "\r\n"); + + char* header_end = strstr(header_start, "\r\n"); if (!header_end) return NULL; - + size_t value_len = header_end - header_start; if (value_len == 0 || value_len > 512) return NULL; - - char *value = malloc(value_len + 1); + + char* value = malloc(value_len + 1); if (!value) return NULL; - + memcpy(value, header_start, value_len); value[value_len] = '\0'; - + // Trim trailing whitespace while (value_len > 0 && (value[value_len - 1] == ' ' || value[value_len - 1] == '\t')) { value[--value_len] = '\0'; } - + return value; } @@ -2347,43 +2340,43 @@ int calculate_dynamic_rate_limit(void) int cpu_cores = sysconf(_SC_NPROCESSORS_ONLN); if (cpu_cores <= 0) cpu_cores = 4; - + // Get available memory long pages = sysconf(_SC_PHYS_PAGES); long page_size = sysconf(_SC_PAGE_SIZE); long ram_mb = (pages * page_size) / (1024 * 1024); - + // Formula: base_rate * (cpu_factor + ram_factor) * connection_factor // This allows more requests on powerful systems int base_rate = 100; // Base requests per IP per minute - + // CPU factor: 1.0 to 4.0 (1-16+ cores) double cpu_factor = 1.0 + (cpu_cores / 4.0); if (cpu_factor > 4.0) cpu_factor = 4.0; - + // RAM factor: 1.0 to 3.0 (1GB to 16GB+) double ram_factor = 1.0 + ((ram_mb / 4096.0) * 2.0); if (ram_factor > 3.0) ram_factor = 3.0; - + // Connection capacity factor based on max_connections double conn_factor = 1.0 + (config.max_connections / 2048.0); if (conn_factor > 2.0) conn_factor = 2.0; - + int dynamic_limit = (int)(base_rate * cpu_factor * ram_factor * conn_factor); - + // Ensure reasonable bounds if (dynamic_limit < 200) dynamic_limit = 200; if (dynamic_limit > 10000) dynamic_limit = 10000; - + return dynamic_limit; } -void cache_file(const char *path, const char *data, size_t size, const char *mime_type) +void cache_file(const char* path, const char* data, size_t size, const char* mime_type) { pthread_mutex_lock(&cache_mutex); @@ -2424,4 +2417,4 @@ void cache_file(const char *path, const char *data, size_t size, const char *mim cache_size++; pthread_mutex_unlock(&cache_mutex); -} +} \ No newline at end of file diff --git a/src/server_config.c b/src/server_config.c index 730dfb3..37a1215 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -1,8 +1,7 @@ -#include #include #include "server_config.h" -void init_config(ServerConfig *config) +void init_config(ServerConfig* config) { config->port = 8080; config->use_https = false; @@ -10,7 +9,7 @@ void init_config(ServerConfig *config) config->max_threads = 4; config->running = true; config->automatic_startup = false; - config->log_mode = LOG_MODE_CLASSIC; // Default to classic logging + config->log_mode = LOG_MODE_CLASSIC; // Default to classic logging strcpy(config->server_name, "127.0.0.1"); config->enable_http2 = false; config->enable_websocket = false; @@ -18,4 +17,4 @@ void init_config(ServerConfig *config) config->max_connections = 1024; strcpy(config->ssl_cert_path, "ssl/cert/cert.pem"); strcpy(config->ssl_key_path, "ssl/key/key.key"); -} +} \ No newline at end of file diff --git a/src/server_config.h b/src/server_config.h index bf14b2f..8e45b09 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -4,7 +4,8 @@ #include // Log modes -typedef enum { +typedef enum +{ LOG_MODE_OFF = 0, LOG_MODE_CLASSIC = 1, LOG_MODE_DEBUG = 2, @@ -20,7 +21,7 @@ typedef struct bool running; bool automatic_startup; char server_name[256]; - LogMode log_mode; // Replaces verbose - supports off/classic/debug/advanced + LogMode log_mode; // Replaces verbose - supports off/classic/debug/advanced bool enable_http2; bool enable_websocket; char www_path[256]; @@ -29,8 +30,8 @@ typedef struct char ssl_key_path[256]; } ServerConfig; -int load_config(const char *filename, ServerConfig *config); -void init_config(ServerConfig *config); -void log_event(const char *message); +int load_config(const char* filename, ServerConfig* config); +void init_config(ServerConfig* config); +void log_event(const char* message); -#endif +#endif \ No newline at end of file diff --git a/src/websocket.c b/src/websocket.c index 226787c..6a23972 100644 --- a/src/websocket.c +++ b/src/websocket.c @@ -13,21 +13,19 @@ #define SHA1_DIGEST_LENGTH 20 // Base64 encode function -static char *base64_encode(const unsigned char *input, int length) +static char* base64_encode(const unsigned char* input, int length) { - BIO *bmem, *b64; - BUF_MEM *bptr; - - b64 = BIO_new(BIO_f_base64()); - bmem = BIO_new(BIO_s_mem()); + BIO *bmem = BIO_new(BIO_s_mem()), *b64 = BIO_new(BIO_f_base64());; + BUF_MEM* bptr; b64 = BIO_push(b64, bmem); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO_write(b64, input, length); BIO_flush(b64); BIO_get_mem_ptr(b64, &bptr); - char *buff = (char *)malloc(bptr->length + 1); - if (!buff) { + char* buff = (char*)malloc(bptr->length + 1); + if (!buff) + { BIO_free_all(b64); return NULL; } @@ -40,15 +38,16 @@ static char *base64_encode(const unsigned char *input, int length) } // Generate WebSocket accept key from client key -char *ws_generate_accept_key(const char *client_key) +char* ws_generate_accept_key(const char* client_key) { - if (!client_key || strlen(client_key) > 128) { - return NULL; // Security: validate input length + if (!client_key || strlen(client_key) > 128) + { + return NULL; // Security: validate input length } - + char combined[256]; int written = snprintf(combined, sizeof(combined), "%s%s", client_key, WS_GUID); - + if (written < 0 || written >= (int)sizeof(combined)) { return NULL; @@ -56,15 +55,17 @@ char *ws_generate_accept_key(const char *client_key) unsigned char hash[SHA1_DIGEST_LENGTH]; unsigned int hash_len = 0; - - EVP_MD_CTX *ctx = EVP_MD_CTX_new(); - if (!ctx) { + + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + if (!ctx) + { return NULL; } - + if (EVP_DigestInit_ex(ctx, EVP_sha1(), NULL) != 1 || EVP_DigestUpdate(ctx, combined, strlen(combined)) != 1 || - EVP_DigestFinal_ex(ctx, hash, &hash_len) != 1) { + EVP_DigestFinal_ex(ctx, hash, &hash_len) != 1) + { EVP_MD_CTX_free(ctx); return NULL; } @@ -74,20 +75,20 @@ char *ws_generate_accept_key(const char *client_key) } // Handle WebSocket handshake -int ws_handle_handshake(int client_socket, const char *request, char *response, size_t response_size) +int ws_handle_handshake(int client_socket, const char* request, char* response, size_t response_size) { (void)client_socket; // Unused in this implementation // Extract Sec-WebSocket-Key from request - const char *key_header = "Sec-WebSocket-Key: "; - char *key_start = strstr(request, key_header); + const char* key_header = "Sec-WebSocket-Key: "; + char* key_start = strstr(request, key_header); if (!key_start) { return -1; } key_start += strlen(key_header); - char *key_end = strstr(key_start, "\r\n"); + char* key_end = strstr(key_start, "\r\n"); if (!key_end) { return -1; @@ -95,7 +96,7 @@ int ws_handle_handshake(int client_socket, const char *request, char *response, char client_key[256]; size_t key_len = key_end - key_start; - if (key_len >= sizeof(client_key) || key_len == 0 || key_len > 1024) + if (key_len >= sizeof(client_key) || key_len == 0) { return -1; } @@ -103,7 +104,7 @@ int ws_handle_handshake(int client_socket, const char *request, char *response, client_key[key_len] = '\0'; // Generate accept key - char *accept_key = ws_generate_accept_key(client_key); + char* accept_key = ws_generate_accept_key(client_key); if (!accept_key) { return -1; @@ -111,36 +112,36 @@ int ws_handle_handshake(int client_socket, const char *request, char *response, // Create handshake response int written = snprintf(response, response_size, - "HTTP/1.1 101 Switching Protocols\r\n" - "Upgrade: websocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Accept: %s\r\n" - "\r\n", - accept_key); + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "\r\n", + accept_key); free(accept_key); - + if (written < 0 || written >= (int)response_size) { return -1; } - + return 0; } // Handle WebSocket handshake for SSL connections -int ws_handle_handshake_ssl(SSL *ssl, const char *request, char *response, size_t response_size) +int ws_handle_handshake_ssl(SSL* ssl, const char* request, char* response, size_t response_size) { (void)ssl; // Use the same logic, just different transport return ws_handle_handshake(0, request, response, response_size); } // Parse WebSocket frame -int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload) +int ws_parse_frame(const uint8_t* data, size_t len, ws_frame_header_t* header, uint8_t** payload) { // Maximum allowed WebSocket payload size (10MB) - #define MAX_WEBSOCKET_PAYLOAD (10 * 1024 * 1024) - +#define MAX_WEBSOCKET_PAYLOAD (10 * 1024 * 1024) + if (len < 2) { return -1; @@ -195,7 +196,7 @@ int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, u } // Unmask payload if masked - *payload = (uint8_t *)malloc(header->payload_length); + *payload = (uint8_t*)malloc(header->payload_length); if (!*payload) { return -1; @@ -217,10 +218,10 @@ int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, u } // Create WebSocket frame -int ws_create_frame(uint8_t *buffer, size_t buffer_size, uint8_t opcode, const uint8_t *payload, size_t payload_len) +int ws_create_frame(uint8_t* buffer, size_t buffer_size, uint8_t opcode, const uint8_t* payload, size_t payload_len) { size_t header_size; - + // Calculate total frame size first if (payload_len < 126) { @@ -234,13 +235,13 @@ int ws_create_frame(uint8_t *buffer, size_t buffer_size, uint8_t opcode, const u { header_size = 10; } - + // Check buffer size before writing anything if (buffer_size < header_size + payload_len) { return -1; } - + size_t offset = 0; // First byte: FIN + opcode @@ -277,7 +278,7 @@ int ws_create_frame(uint8_t *buffer, size_t buffer_size, uint8_t opcode, const u } // Send WebSocket frame -int ws_send_frame(ws_connection_t *conn, uint8_t opcode, const uint8_t *payload, size_t payload_len) +int ws_send_frame(ws_connection_t* conn, uint8_t opcode, const uint8_t* payload, size_t payload_len) { // Allocate buffer with enough space for header (max 10 bytes) + payload // Check for integer overflow @@ -285,7 +286,7 @@ int ws_send_frame(ws_connection_t *conn, uint8_t opcode, const uint8_t *payload, { return -1; } - + size_t max_frame_size = 10 + payload_len; if (max_frame_size > 65536) { @@ -319,19 +320,19 @@ int ws_send_frame(ws_connection_t *conn, uint8_t opcode, const uint8_t *payload, } // Send text message -int ws_send_text(ws_connection_t *conn, const char *text) +int ws_send_text(ws_connection_t* conn, const char* text) { - return ws_send_frame(conn, WS_OPCODE_TEXT, (const uint8_t *)text, strlen(text)); + return ws_send_frame(conn, WS_OPCODE_TEXT, (const uint8_t*)text, strlen(text)); } // Send pong response -int ws_send_pong(ws_connection_t *conn, const uint8_t *payload, size_t payload_len) +int ws_send_pong(ws_connection_t* conn, const uint8_t* payload, size_t payload_len) { return ws_send_frame(conn, WS_OPCODE_PONG, payload, payload_len); } // Close WebSocket connection -void ws_close_connection(ws_connection_t *conn, uint16_t status_code) +void ws_close_connection(ws_connection_t* conn, uint16_t status_code) { uint8_t close_payload[2]; close_payload[0] = (status_code >> 8) & 0xFF; @@ -348,7 +349,7 @@ void ws_close_connection(ws_connection_t *conn, uint16_t status_code) } // Validate UTF-8 encoding -bool ws_is_valid_utf8(const uint8_t *data, size_t len) +bool ws_is_valid_utf8(const uint8_t* data, size_t len) { size_t i = 0; while (i < len) @@ -371,7 +372,8 @@ bool ws_is_valid_utf8(const uint8_t *data, size_t len) } else if ((data[i] & 0xF8) == 0xF0) { - if (i + 3 >= len || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80) + if (i + 3 >= len || (data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != + 0x80) return false; i += 4; } diff --git a/src/websocket.h b/src/websocket.h index ae2521d..1ff7b5e 100644 --- a/src/websocket.h +++ b/src/websocket.h @@ -27,23 +27,23 @@ typedef struct typedef struct { int socket_fd; - SSL *ssl; + SSL* ssl; bool is_ssl; bool handshake_complete; } ws_connection_t; // Function prototypes -int ws_handle_handshake(int client_socket, const char *request, char *response, size_t response_size); -int ws_handle_handshake_ssl(SSL *ssl, const char *request, char *response, size_t response_size); -int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_header_t *header, uint8_t **payload); -int ws_create_frame(uint8_t *buffer, size_t buffer_size, uint8_t opcode, const uint8_t *payload, size_t payload_len); -int ws_send_frame(ws_connection_t *conn, uint8_t opcode, const uint8_t *payload, size_t payload_len); -int ws_send_text(ws_connection_t *conn, const char *text); -int ws_send_pong(ws_connection_t *conn, const uint8_t *payload, size_t payload_len); -void ws_close_connection(ws_connection_t *conn, uint16_t status_code); +int ws_handle_handshake(int client_socket, const char* request, char* response, size_t response_size); +int ws_handle_handshake_ssl(SSL* ssl, const char* request, char* response, size_t response_size); +int ws_parse_frame(const uint8_t* data, size_t len, ws_frame_header_t* header, uint8_t** payload); +int ws_create_frame(uint8_t* buffer, size_t buffer_size, uint8_t opcode, const uint8_t* payload, size_t payload_len); +int ws_send_frame(ws_connection_t* conn, uint8_t opcode, const uint8_t* payload, size_t payload_len); +int ws_send_text(ws_connection_t* conn, const char* text); +int ws_send_pong(ws_connection_t* conn, const uint8_t* payload, size_t payload_len); +void ws_close_connection(ws_connection_t* conn, uint16_t status_code); // Helper functions -char *ws_generate_accept_key(const char *client_key); -bool ws_is_valid_utf8(const uint8_t *data, size_t len); +char* ws_generate_accept_key(const char* client_key); +bool ws_is_valid_utf8(const uint8_t* data, size_t len); #endif