diff --git a/Makefile b/Makefile index ff1a928..c870371 100644 --- a/Makefile +++ b/Makefile @@ -14,12 +14,12 @@ LDFLAGS = -pthread LIBS = -lssl -lcrypto -lmagic -lnghttp2 # Source files and object files -SRCS = src/server.c src/config_parser.c src/server_config.c src/websocket.c src/http2.c +SRCS = src/server.c src/config_parser.c src/server_config.c src/websocket.c src/http2.c src/performance.c OBJS = $(SRCS:.c=.o) TARGET = server # Header files -HEADERS = src/server_config.h src/websocket.h +HEADERS = src/server_config.h src/websocket.h src/http2.h src/performance.h # Include directories INCLUDES = diff --git a/server.conf b/server.conf index da49ebd..96dfb68 100644 --- a/server.conf +++ b/server.conf @@ -1,31 +1,32 @@ # Carbon Web Server Configuration File # Lines starting with # are comments - -# Server listening port -port = 8080 - -# Enable HTTPS (requires valid certificates in certs/ directory) -use_https = false - -# Log file location -log_file = log/server.log - -# Maximum number of worker threads -max_threads = 4 - # Server running state running = true +# ---Network configuration--- +# Server listening port +port = 8080 +# Enable HTTPS (requires valid certificates in certs/ directory) +use_https = false +# Enable HTTP/2 support (requires HTTPS) +enable_http2 = false +# Enable WebSocket support +enable_websocket = false # Server name or IP address (used for logging and response headers) + server_name = Your_domain/IP + +# ---Performance configuration--- +# Maximum number of worker threads +max_threads = 4 +max_connections = 1024 + + + +# ---Path configuration--- +# Log file location +log_file = log/server.log # Enable verbose logging verbose = true - -# Enable HTTP/2 support (requires HTTPS) -enable_http2 = false - -# Enable WebSocket support -enable_websocket = false - # Path to www www_path = www \ No newline at end of file diff --git a/src/config_parser.c b/src/config_parser.c index fa20d75..3711e09 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -16,6 +16,7 @@ typedef enum { CONFIG_ENABLE_HTTP2, CONFIG_ENABLE_WEBSOCKET, CONFIG_WWW_PATH, + CONFIG_MAX_CONNECTIONS, CONFIG_UNKNOWN } ConfigKey; @@ -64,6 +65,7 @@ static ConfigKey get_config_key(const char *key) { {"enable_http2", CONFIG_ENABLE_HTTP2}, {"enable_websocket",CONFIG_ENABLE_WEBSOCKET}, {"www_path", CONFIG_WWW_PATH}, + {"max_connections", CONFIG_MAX_CONNECTIONS}, {NULL, CONFIG_UNKNOWN} }; @@ -147,6 +149,10 @@ int load_config(const char *filename, ServerConfig *config) { case CONFIG_RUNNING: config->running = parse_bool(value); + if (config->running == 0 || false) { + fprintf(stderr, "ERROR: current run state is false cannot run the server!\n"); + exit(EXIT_FAILURE); + } printf("load_config: running = %d\n", config->running); break; @@ -181,6 +187,12 @@ int load_config(const char *filename, ServerConfig *config) { printf("load_config: www_path = %s\n", config->www_path); break; + case CONFIG_MAX_CONNECTIONS: + config->max_connections = atoi(value); + printf("load_config: max_connections: = %d\n", config->max_connections); + + break; + case CONFIG_UNKNOWN: default: fprintf(stderr, "Warning: Unknown config option '%s' on line %d\n", key, line_number); diff --git a/src/performance.c b/src/performance.c new file mode 100644 index 0000000..d179afb --- /dev/null +++ b/src/performance.c @@ -0,0 +1,275 @@ +#include "performance.h" +#include +#include +#include +#include +#include +#include + +#define MAX_MMAP_CACHE_SIZE 50 +#define MAX_MMAP_FILE_SIZE (10 * 1024 * 1024) // 10MB +#define BUFFER_POOL_SIZE 32 +#define DEFAULT_BUFFER_SIZE 16384 + +// Global cache structures +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 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"; + +// Task queue implementation +void init_task_queue(task_queue_t *queue) { + queue->head = NULL; + queue->tail = NULL; + queue->count = 0; + pthread_mutex_init(&queue->mutex, NULL); + pthread_cond_init(&queue->cond, NULL); +} + +void enqueue_task(task_queue_t *queue, int socket_fd, SSL *ssl, bool is_https) { + connection_task_t *task = malloc(sizeof(connection_task_t)); + if (!task) return; + + task->socket_fd = socket_fd; + task->ssl = ssl; + task->is_https = is_https; + task->next = NULL; + + pthread_mutex_lock(&queue->mutex); + + if (queue->tail) { + queue->tail->next = task; + } else { + queue->head = task; + } + queue->tail = task; + queue->count++; + + pthread_cond_signal(&queue->cond); + pthread_mutex_unlock(&queue->mutex); +} + +connection_task_t* dequeue_task(task_queue_t *queue) { + pthread_mutex_lock(&queue->mutex); + + while (queue->head == NULL) { + pthread_cond_wait(&queue->cond, &queue->mutex); + } + + connection_task_t *task = queue->head; + queue->head = task->next; + + if (queue->head == NULL) { + queue->tail = NULL; + } + queue->count--; + + pthread_mutex_unlock(&queue->mutex); + return task; +} + +void destroy_task_queue(task_queue_t *queue) { + pthread_mutex_lock(&queue->mutex); + + connection_task_t *current = queue->head; + while (current) { + connection_task_t *next = current->next; + free(current); + current = next; + } + + pthread_mutex_unlock(&queue->mutex); + pthread_mutex_destroy(&queue->mutex); + pthread_cond_destroy(&queue->cond); +} + +// Memory-mapped file cache implementation +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) { + pthread_mutex_lock(&mmap_cache_mutex); + + for (int i = 0; i < mmap_cache_size; i++) { + if (mmap_cache[i].path && strcmp(mmap_cache[i].path, path) == 0) { + mmap_cache[i].last_access = time(NULL); + mmap_cache[i].ref_count++; + pthread_mutex_unlock(&mmap_cache_mutex); + return &mmap_cache[i]; + } + } + + pthread_mutex_unlock(&mmap_cache_mutex); + return NULL; +} + +void cache_file_mmap(const char *path, size_t size, const char *mime_type) { + if (size > MAX_MMAP_FILE_SIZE) return; + + pthread_mutex_lock(&mmap_cache_mutex); + + // Check if already cached + for (int i = 0; i < mmap_cache_size; i++) { + if (mmap_cache[i].path && strcmp(mmap_cache[i].path, path) == 0) { + pthread_mutex_unlock(&mmap_cache_mutex); + return; + } + } + + // Find slot (evict LRU if full) + int slot = mmap_cache_size; + if (mmap_cache_size >= MAX_MMAP_CACHE_SIZE) { + time_t oldest = time(NULL); + for (int i = 0; i < mmap_cache_size; i++) { + if (mmap_cache[i].ref_count == 0 && mmap_cache[i].last_access < oldest) { + oldest = mmap_cache[i].last_access; + slot = i; + } + } + + if (slot == mmap_cache_size) { + pthread_mutex_unlock(&mmap_cache_mutex); + return; // All entries in use + } + + // Evict old entry + if (mmap_cache[slot].mmap_data) { + munmap(mmap_cache[slot].mmap_data, mmap_cache[slot].size); + } + free(mmap_cache[slot].path); + free(mmap_cache[slot].mime_type); + } else { + mmap_cache_size++; + } + + // Map file + int fd = open(path, O_RDONLY); + if (fd < 0) { + pthread_mutex_unlock(&mmap_cache_mutex); + return; + } + + void *mapped = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + + if (mapped == MAP_FAILED) { + pthread_mutex_unlock(&mmap_cache_mutex); + return; + } + + // Advise kernel about access pattern + madvise(mapped, size, MADV_WILLNEED | MADV_SEQUENTIAL); + + mmap_cache[slot].path = strdup(path); + mmap_cache[slot].mmap_data = mapped; + mmap_cache[slot].size = size; + mmap_cache[slot].last_access = time(NULL); + mmap_cache[slot].mime_type = strdup(mime_type); + mmap_cache[slot].ref_count = 0; + + pthread_mutex_unlock(&mmap_cache_mutex); +} + +void release_cached_file(mmap_cache_entry_t *entry) { + pthread_mutex_lock(&mmap_cache_mutex); + entry->ref_count--; + pthread_mutex_unlock(&mmap_cache_mutex); +} + +void cleanup_mmap_cache(void) { + pthread_mutex_lock(&mmap_cache_mutex); + + for (int i = 0; i < mmap_cache_size; i++) { + if (mmap_cache[i].mmap_data) { + munmap(mmap_cache[i].mmap_data, mmap_cache[i].size); + } + free(mmap_cache[i].path); + free(mmap_cache[i].mime_type); + } + + free(mmap_cache); + mmap_cache = NULL; + mmap_cache_size = 0; + + pthread_mutex_unlock(&mmap_cache_mutex); +} + +// Buffer pool implementation +void init_buffer_pool(void) { + pthread_mutex_lock(&buffer_pool_mutex); + + for (int i = 0; i < BUFFER_POOL_SIZE; i++) { + buffer_pool_t *buf = malloc(sizeof(buffer_pool_t)); + if (buf) { + buf->buffer = malloc(DEFAULT_BUFFER_SIZE); + buf->size = DEFAULT_BUFFER_SIZE; + buf->in_use = false; + buf->next = buffer_pool; + buffer_pool = buf; + } + } + + pthread_mutex_unlock(&buffer_pool_mutex); +} + +char* get_buffer_from_pool(size_t min_size) { + pthread_mutex_lock(&buffer_pool_mutex); + + buffer_pool_t *current = buffer_pool; + while (current) { + if (!current->in_use && current->size >= min_size) { + current->in_use = true; + pthread_mutex_unlock(&buffer_pool_mutex); + return current->buffer; + } + current = current->next; + } + + pthread_mutex_unlock(&buffer_pool_mutex); + + return malloc(min_size); +} + +void return_buffer_to_pool(char *buffer) { + pthread_mutex_lock(&buffer_pool_mutex); + + buffer_pool_t *current = buffer_pool; + while (current) { + if (current->buffer == buffer) { + current->in_use = false; + pthread_mutex_unlock(&buffer_pool_mutex); + return; + } + current = current->next; + } + + pthread_mutex_unlock(&buffer_pool_mutex); + + // Not from pool, free it + free(buffer); +} + +void cleanup_buffer_pool(void) { + pthread_mutex_lock(&buffer_pool_mutex); + + buffer_pool_t *current = buffer_pool; + while (current) { + buffer_pool_t *next = current->next; + free(current->buffer); + free(current); + current = next; + } + + buffer_pool = NULL; + pthread_mutex_unlock(&buffer_pool_mutex); +} diff --git a/src/performance.h b/src/performance.h new file mode 100644 index 0000000..e62a854 --- /dev/null +++ b/src/performance.h @@ -0,0 +1,68 @@ +#ifndef PERFORMANCE_H +#define PERFORMANCE_H + +#include +#include +#include +#include +#include + +// Connection pool structures +typedef struct connection_task_t { + int socket_fd; + SSL *ssl; + bool is_https; + struct connection_task_t *next; +} connection_task_t; + +typedef struct { + connection_task_t *head; + connection_task_t *tail; + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; +} task_queue_t; + +// Memory-mapped file cache +typedef struct { + char *path; + void *mmap_data; + size_t size; + time_t last_access; + char *mime_type; + int ref_count; +} mmap_cache_entry_t; + +// Response buffer pool +typedef struct buffer_pool_t { + char *buffer; + size_t size; + bool in_use; + 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_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); +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); +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; + +#endif diff --git a/src/server.c b/src/server.c index dc506e0..772be26 100644 --- a/src/server.c +++ b/src/server.c @@ -19,14 +19,15 @@ #include #include #include +#include #include "server_config.h" #include "websocket.h" #include "http2.h" +#include "performance.h" -#define MAX_REQUEST_SIZE 8192 +#define MAX_REQUEST_SIZE 16384 #define MAX_LOG_SIZE 2048 -#define MAX_CLIENTS 1024 #define MAX_EVENTS 1024 #define BOLD "\x1b[1m" @@ -67,6 +68,7 @@ #define MAX_CACHE_SIZE 100 #define MAX_CACHE_FILE_SIZE (1024 * 1024) // 1MB +#define MAX_MMAP_FILE_SIZE (10 * 1024 * 1024) // 10MB typedef struct { pthread_t thread; @@ -99,7 +101,7 @@ 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[MAX_CLIENTS]; +pthread_t *client_threads = NULL; int num_client_threads = 0; pthread_mutex_t thread_count_mutex = PTHREAD_MUTEX_INITIALIZER; SSL_CTX *ssl_ctx = NULL; @@ -190,7 +192,9 @@ void set_socket_options(int socket_fd) { int nodelay = 1; setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + #ifdef SO_REUSEPORT setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse)); + #endif setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive)); setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); setsockopt(socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); @@ -272,7 +276,7 @@ void *start_http_server(void *arg) { } pthread_mutex_lock(&thread_count_mutex); - if (num_client_threads < MAX_CLIENTS) { + if (num_client_threads < config.max_connections) { pthread_t client_thread; int *client_socket_ptr = malloc(sizeof(int)); *client_socket_ptr = client_socket; @@ -344,7 +348,7 @@ void *start_https_server(void *arg) { } pthread_mutex_lock(&thread_count_mutex); - if (num_client_threads < MAX_CLIENTS) { + if (num_client_threads < config.max_connections) { pthread_t client_thread; int *client_socket_ptr = malloc(sizeof(int)); *client_socket_ptr = client_socket; @@ -563,6 +567,39 @@ void *handle_http_client(void *arg) { // Get MIME type char *mime_type = get_mime_type(filepath); + + // Try cache first + mmap_cache_entry_t *cached = get_cached_file(filepath); + + if (cached) { + // Serve from cache + char response_header[1024]; + 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" + "%s" + "\r\n", + cached->size, + cached->mime_type, + SECURITY_HEADERS); + + send(client_socket, response_header, header_len, 0); + + // Send cached data + size_t total_sent = 0; + while (total_sent < cached->size) { + ssize_t sent = send(client_socket, (char*)cached->mmap_data + total_sent, + cached->size - total_sent, 0); + if (sent <= 0) break; + total_sent += sent; + } + + release_cached_file(cached); + free(mime_type); + log_event("Served file from cache"); + goto done_serving; + } int fd = open(filepath, O_RDONLY); if (fd == -1) { @@ -581,9 +618,14 @@ void *handle_http_client(void *arg) { free(mime_type); goto cleanup; } + + // 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[512]; - snprintf(response_header, sizeof(response_header), + char response_header[1024]; + 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" @@ -595,8 +637,9 @@ void *handle_http_client(void *arg) { free(mime_type); - send(client_socket, response_header, strlen(response_header), 0); + 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) { @@ -606,6 +649,8 @@ void *handle_http_client(void *arg) { close(fd); log_event("Served requested file successfully."); } + +done_serving: } else if (bytes_received < 0) { HANDLE_ERROR("Error receiving request"); } @@ -787,55 +832,94 @@ void *handle_https_client(void *arg) { // Get MIME type char *mime_type = get_mime_type(filepath); + + // Try to get file from cache first + mmap_cache_entry_t *cached = get_cached_file(filepath); + + if (cached) { + // Serve from cache (fast path) + char response_header[1024]; + 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" + "%s" + "\r\n", + cached->size, + cached->mime_type, + SECURITY_HEADERS); + SSL_write(ssl, response_header, header_len); + + // Send cached data directly from memory + size_t total_sent = 0; + while (total_sent < cached->size) { + int to_send = (cached->size - total_sent > 16384) ? 16384 : (cached->size - total_sent); + int sent = SSL_write(ssl, (char*)cached->mmap_data + total_sent, to_send); + if (sent <= 0) break; + total_sent += sent; + } + + release_cached_file(cached); + free(mime_type); + log_event("Served file from cache (mmap)"); + goto cleanup; + } + + // Not in cache - load from disk int fd = open(filepath, O_RDONLY); if (fd == -1) { - perror("open error"); log_event("File open failed"); 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); goto cleanup; - } else { - struct stat st; - if (fstat(fd, &st) == -1) { - perror("fstat error"); - log_event("Error getting file size."); - 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); - free(mime_type); - goto cleanup; - } - - char response_header[512]; - snprintf(response_header, sizeof(response_header), - "HTTP/1.1 200 OK\r\n" - "Content-Length: %ld\r\n" - "Content-Type: %s\r\n" - "%s" - "\r\n", - (long)st.st_size, - mime_type, - SECURITY_HEADERS); - - free(mime_type); - - SSL_write(ssl, response_header, strlen(response_header)); - - char file_buffer[1024]; - ssize_t bytes_read; - while ((bytes_read = read(fd, file_buffer, sizeof(file_buffer))) > 0) { - if (SSL_write(ssl, file_buffer, bytes_read) <= 0) { - perror("SSL_write error"); - log_event("Error sending file content."); - break; - } - } - close(fd); - log_event("Served requested file successfully."); } + + 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"; + SSL_write(ssl, internal_server_error, strlen(internal_server_error)); + close(fd); + free(mime_type); + goto cleanup; + } + + // Cache file if it's small enough + if (st.st_size > 0 && st.st_size < MAX_MMAP_FILE_SIZE) { + cache_file_mmap(filepath, st.st_size, mime_type); + } + + char response_header[1024]; + 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" + "%s" + "\r\n", + (long)st.st_size, + mime_type, + SECURITY_HEADERS); + + free(mime_type); + + SSL_write(ssl, response_header, header_len); + + // Use larger buffer for better performance + char *file_buffer = get_buffer_from_pool(16384); + ssize_t bytes_read; + while ((bytes_read = read(fd, file_buffer, 16384)) > 0) { + if (SSL_write(ssl, file_buffer, bytes_read) <= 0) { + log_event("Error sending file content."); + break; + } + } + return_buffer_to_pool(file_buffer); + close(fd); + log_event("Served requested file successfully."); + cleanup: if (ssl) { @@ -874,12 +958,13 @@ void shutdown_server() { } // Wait for all threads with timeout - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - ts.tv_sec += 5; // 5 second timeout + time_t start_time = time(NULL); pthread_mutex_lock(&thread_count_mutex); - while (num_client_threads > 0 && clock_gettime(CLOCK_REALTIME, &ts) < 5) { + while (num_client_threads > 0 && (time(NULL) - start_time) < 5) { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 100000000; // 100ms pthread_cond_timedwait(&thread_pool_cond, &thread_count_mutex, &ts); } @@ -896,6 +981,8 @@ void shutdown_server() { // Cleanup resources cleanup_openssl(); cleanup_thread_pool(); + cleanup_mmap_cache(); + cleanup_buffer_pool(); if (rate_limits) { free(rate_limits); @@ -912,27 +999,41 @@ void shutdown_server() { file_cache = NULL; } + if (client_threads) { + free(client_threads); + client_threads = NULL; + } + log_event("Server shutdown completed."); } int parse_request_line(char *request_buffer, char *method, char *url, char *protocol) { + if (!request_buffer || !method || !url || !protocol) return -1; + + method[0] = '\0'; + url[0] = '\0'; + protocol[0] = '\0'; + char *saveptr1, *saveptr2; char *line = strtok_r(request_buffer, "\r\n", &saveptr1); - if (line == NULL) return -1; + if (line == NULL || strlen(line) == 0) return -1; char *token = strtok_r(line, " ", &saveptr2); - if (token == NULL) return -1; - strncpy(method, token, 7); method[7] = '\0'; + if (token == NULL || strlen(token) > 7) return -1; + strncpy(method, token, 7); + method[7] = '\0'; token = strtok_r(NULL, " ", &saveptr2); - if (token == NULL) return -1; - strncpy(url, token, 255); url[255] = '\0'; + if (token == NULL || strlen(token) > 255) return -1; + strncpy(url, token, 255); + url[255] = '\0'; token = strtok_r(NULL, " ", &saveptr2); - if (token == NULL) return -1; - strncpy(protocol, token, 15); protocol[15] = '\0'; + if (token == NULL || strlen(token) > 15) return -1; + strncpy(protocol, token, 15); + protocol[15] = '\0'; return 0; } @@ -966,12 +1067,35 @@ void signal_handler(int sig) { } } +void initialize_thread_pool() { + thread_pool = calloc(MAX_THREAD_POOL_SIZE, sizeof(ThreadInfo)); + if (!thread_pool) { + perror("Failed to allocate thread pool"); + exit(EXIT_FAILURE); + } +} + int main() { if (load_config("server.conf", &config) != 0) { printf("Using default configuration.\n"); } config.running = 1; + + // Allocate client threads array + client_threads = calloc(config.max_connections, sizeof(pthread_t)); + if (!client_threads) { + perror("Failed to allocate client threads array"); + exit(EXIT_FAILURE); + } + + // Initialize thread pool + initialize_thread_pool(); + + // Initialize performance optimizations + init_mmap_cache(); + init_buffer_pool(); + log_event("Performance optimizations initialized"); if (config.use_https) { initialize_openssl(); @@ -1139,69 +1263,92 @@ char* sanitize_url(const char *url) { if (!url) return NULL; size_t url_len = strlen(url); - if (url_len == 0 || url_len > 255) return NULL; + if (url_len == 0 || url_len > 2048) return NULL; - char *sanitized = malloc(url_len + 1); + char *sanitized = calloc(1, url_len + 2); if (!sanitized) { log_event("Memory allocation failed in sanitize_url"); return NULL; } - int i, j = 0; + int j = 0; int slash_count = 0; - int dot_count = 0; + int consecutive_dots = 0; + bool last_was_slash = false; // Must start with '/' if (url[0] != '/') { sanitized[j++] = '/'; } - for (i = 0; url[i]; i++) { - if (j >= 255) { // Prevent buffer overflow - free(sanitized); - return NULL; - } + for (size_t i = 0; i < url_len && j < (int)url_len; i++) { + char c = url[i]; - // Reset dot count on directory change - if (url[i] == '/') { - dot_count = 0; + // Check for null bytes (security) + if (c == '\0') break; + + // Handle slashes + if (c == '/') { + if (last_was_slash) continue; + last_was_slash = true; + consecutive_dots = 0; slash_count++; - if (slash_count > 10) { // Limit directory depth + + if (slash_count > 20) { free(sanitized); return NULL; } + + sanitized[j++] = c; + continue; } - // Count consecutive dots - if (url[i] == '.') { - dot_count++; - if (dot_count > 1) { // Prevent directory traversal + last_was_slash = false; + + // Handle dots (prevent traversal) + if (c == '.') { + consecutive_dots++; + if (consecutive_dots > 2) { // Too many dots + free(sanitized); + return NULL; + } + // Check for path traversal patterns + if (consecutive_dots == 2 && (i == 0 || url[i-1] == '/')) { free(sanitized); return NULL; } } else { - dot_count = 0; + consecutive_dots = 0; } - // Only allow safe characters - if (isalnum((unsigned char)url[i]) || - url[i] == '/' || - url[i] == '.' || - url[i] == '-' || - url[i] == '_') { - sanitized[j++] = url[i]; + // Only allow safe characters (alphanumeric, dash, underscore, dot) + if (isalnum((unsigned char)c) || c == '-' || c == '_' || c == '.') { + sanitized[j++] = c; + } else if (c == '%') { + // URL encoding - only allow safe encoded characters + if (i + 2 < url_len && isxdigit(url[i+1]) && isxdigit(url[i+2])) { + sanitized[j++] = c; + } else { + free(sanitized); + return NULL; + } } + // Skip other characters silently } - // Ensure proper termination sanitized[j] = '\0'; - // Additional security checks - if (strstr(sanitized, "//") || // No double slashes - strstr(sanitized, "./") || // No current directory - strstr(sanitized, "..") || // No parent directory - strstr(sanitized, "/.") || // No hidden files - strlen(sanitized) < 1) { // Must have content + // Final security checks + if (j == 0 || j > 2048) { + free(sanitized); + return NULL; + } + + // Check for dangerous patterns + if (strstr(sanitized, "/../") || + strstr(sanitized, "/./") || + strstr(sanitized, "//") || + (strlen(sanitized) >= 3 && strcmp(sanitized + strlen(sanitized) - 3, "/..") == 0)) { free(sanitized); return NULL; } @@ -1265,15 +1412,11 @@ int check_rate_limit(const char *ip) { return 1; // Request allowed } -void initialize_thread_pool() { - thread_pool = calloc(MAX_THREAD_POOL_SIZE, sizeof(ThreadInfo)); - if (!thread_pool) { - perror("Failed to allocate thread pool"); - exit(EXIT_FAILURE); - } -} - void cleanup_thread_pool() { + if (!thread_pool) { + return; + } + for (int i = 0; i < thread_pool_size; i++) { if (thread_pool[i].busy) { pthread_cancel(thread_pool[i].thread); @@ -1281,6 +1424,8 @@ void cleanup_thread_pool() { } } free(thread_pool); + thread_pool = NULL; + thread_pool_size = 0; } void cache_file(const char *path, const char *data, size_t size, const char *mime_type) { diff --git a/src/server_config.c b/src/server_config.c index 192d499..8ede6f6 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -14,4 +14,5 @@ void init_config(ServerConfig *config) { config->enable_http2 = false; config->enable_websocket = false; strcpy(config->www_path, "www"); + config->max_connections = 1024; } diff --git a/src/server_config.h b/src/server_config.h index 18ad304..9e9b265 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -15,6 +15,7 @@ typedef struct { bool enable_http2; bool enable_websocket; char www_path[256]; + int max_connections; } ServerConfig; int load_config(const char *filename, ServerConfig *config);