Enhance server configuration and performance optimizations
- Added max_connections option to server configuration - Updated Makefile to include performance.c and related headers - Implemented memory-mapped file caching and buffer pooling for improved performance - Refactored config parser to handle new configuration options - Increased maximum request size and optimized file handling
This commit is contained in:
4
Makefile
4
Makefile
@@ -14,12 +14,12 @@ LDFLAGS = -pthread
|
|||||||
LIBS = -lssl -lcrypto -lmagic -lnghttp2
|
LIBS = -lssl -lcrypto -lmagic -lnghttp2
|
||||||
|
|
||||||
# Source files and object files
|
# 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)
|
OBJS = $(SRCS:.c=.o)
|
||||||
TARGET = server
|
TARGET = server
|
||||||
|
|
||||||
# Header files
|
# 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
|
# Include directories
|
||||||
INCLUDES =
|
INCLUDES =
|
||||||
|
|||||||
41
server.conf
41
server.conf
@@ -1,31 +1,32 @@
|
|||||||
# Carbon Web Server Configuration File
|
# Carbon Web Server Configuration File
|
||||||
# Lines starting with # are comments
|
# 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
|
# Server running state
|
||||||
running = true
|
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 or IP address (used for logging and response headers)
|
||||||
|
|
||||||
server_name = Your_domain/IP
|
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
|
# Enable verbose logging
|
||||||
verbose = true
|
verbose = true
|
||||||
|
|
||||||
# Enable HTTP/2 support (requires HTTPS)
|
|
||||||
enable_http2 = false
|
|
||||||
|
|
||||||
# Enable WebSocket support
|
|
||||||
enable_websocket = false
|
|
||||||
|
|
||||||
# Path to www
|
# Path to www
|
||||||
www_path = www
|
www_path = www
|
||||||
@@ -16,6 +16,7 @@ typedef enum {
|
|||||||
CONFIG_ENABLE_HTTP2,
|
CONFIG_ENABLE_HTTP2,
|
||||||
CONFIG_ENABLE_WEBSOCKET,
|
CONFIG_ENABLE_WEBSOCKET,
|
||||||
CONFIG_WWW_PATH,
|
CONFIG_WWW_PATH,
|
||||||
|
CONFIG_MAX_CONNECTIONS,
|
||||||
CONFIG_UNKNOWN
|
CONFIG_UNKNOWN
|
||||||
|
|
||||||
} ConfigKey;
|
} ConfigKey;
|
||||||
@@ -64,6 +65,7 @@ static ConfigKey get_config_key(const char *key) {
|
|||||||
{"enable_http2", CONFIG_ENABLE_HTTP2},
|
{"enable_http2", CONFIG_ENABLE_HTTP2},
|
||||||
{"enable_websocket",CONFIG_ENABLE_WEBSOCKET},
|
{"enable_websocket",CONFIG_ENABLE_WEBSOCKET},
|
||||||
{"www_path", CONFIG_WWW_PATH},
|
{"www_path", CONFIG_WWW_PATH},
|
||||||
|
{"max_connections", CONFIG_MAX_CONNECTIONS},
|
||||||
{NULL, CONFIG_UNKNOWN}
|
{NULL, CONFIG_UNKNOWN}
|
||||||
|
|
||||||
};
|
};
|
||||||
@@ -147,6 +149,10 @@ int load_config(const char *filename, ServerConfig *config) {
|
|||||||
|
|
||||||
case CONFIG_RUNNING:
|
case CONFIG_RUNNING:
|
||||||
config->running = parse_bool(value);
|
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);
|
printf("load_config: running = %d\n", config->running);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -181,6 +187,12 @@ int load_config(const char *filename, ServerConfig *config) {
|
|||||||
printf("load_config: www_path = %s\n", config->www_path);
|
printf("load_config: www_path = %s\n", config->www_path);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CONFIG_MAX_CONNECTIONS:
|
||||||
|
config->max_connections = atoi(value);
|
||||||
|
printf("load_config: max_connections: = %d\n", config->max_connections);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case CONFIG_UNKNOWN:
|
case CONFIG_UNKNOWN:
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Warning: Unknown config option '%s' on line %d\n", key, line_number);
|
fprintf(stderr, "Warning: Unknown config option '%s' on line %d\n", key, line_number);
|
||||||
|
|||||||
275
src/performance.c
Normal file
275
src/performance.c
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
#include "performance.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
68
src/performance.h
Normal file
68
src/performance.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#ifndef PERFORMANCE_H
|
||||||
|
#define PERFORMANCE_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
|
||||||
|
// 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
|
||||||
343
src/server.c
343
src/server.c
@@ -19,14 +19,15 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
#include "server_config.h"
|
#include "server_config.h"
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
#include "http2.h"
|
#include "http2.h"
|
||||||
|
#include "performance.h"
|
||||||
|
|
||||||
#define MAX_REQUEST_SIZE 8192
|
#define MAX_REQUEST_SIZE 16384
|
||||||
#define MAX_LOG_SIZE 2048
|
#define MAX_LOG_SIZE 2048
|
||||||
#define MAX_CLIENTS 1024
|
|
||||||
#define MAX_EVENTS 1024
|
#define MAX_EVENTS 1024
|
||||||
|
|
||||||
#define BOLD "\x1b[1m"
|
#define BOLD "\x1b[1m"
|
||||||
@@ -67,6 +68,7 @@
|
|||||||
|
|
||||||
#define MAX_CACHE_SIZE 100
|
#define MAX_CACHE_SIZE 100
|
||||||
#define MAX_CACHE_FILE_SIZE (1024 * 1024) // 1MB
|
#define MAX_CACHE_FILE_SIZE (1024 * 1024) // 1MB
|
||||||
|
#define MAX_MMAP_FILE_SIZE (10 * 1024 * 1024) // 10MB
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
@@ -99,7 +101,7 @@ pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|||||||
ServerConfig config;
|
ServerConfig config;
|
||||||
char server_log[MAX_LOG_SIZE];
|
char server_log[MAX_LOG_SIZE];
|
||||||
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
pthread_t client_threads[MAX_CLIENTS];
|
pthread_t *client_threads = NULL;
|
||||||
int num_client_threads = 0;
|
int num_client_threads = 0;
|
||||||
pthread_mutex_t thread_count_mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t thread_count_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
SSL_CTX *ssl_ctx = NULL;
|
SSL_CTX *ssl_ctx = NULL;
|
||||||
@@ -190,7 +192,9 @@ void set_socket_options(int socket_fd) {
|
|||||||
int nodelay = 1;
|
int nodelay = 1;
|
||||||
|
|
||||||
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
|
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
|
||||||
|
#ifdef SO_REUSEPORT
|
||||||
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
|
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
|
||||||
|
#endif
|
||||||
setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
|
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_NODELAY, &nodelay, sizeof(nodelay));
|
||||||
setsockopt(socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
|
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);
|
pthread_mutex_lock(&thread_count_mutex);
|
||||||
if (num_client_threads < MAX_CLIENTS) {
|
if (num_client_threads < config.max_connections) {
|
||||||
pthread_t client_thread;
|
pthread_t client_thread;
|
||||||
int *client_socket_ptr = malloc(sizeof(int));
|
int *client_socket_ptr = malloc(sizeof(int));
|
||||||
*client_socket_ptr = client_socket;
|
*client_socket_ptr = client_socket;
|
||||||
@@ -344,7 +348,7 @@ void *start_https_server(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&thread_count_mutex);
|
pthread_mutex_lock(&thread_count_mutex);
|
||||||
if (num_client_threads < MAX_CLIENTS) {
|
if (num_client_threads < config.max_connections) {
|
||||||
pthread_t client_thread;
|
pthread_t client_thread;
|
||||||
int *client_socket_ptr = malloc(sizeof(int));
|
int *client_socket_ptr = malloc(sizeof(int));
|
||||||
*client_socket_ptr = client_socket;
|
*client_socket_ptr = client_socket;
|
||||||
@@ -563,6 +567,39 @@ void *handle_http_client(void *arg) {
|
|||||||
|
|
||||||
// Get MIME type
|
// Get MIME type
|
||||||
char *mime_type = get_mime_type(filepath);
|
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);
|
int fd = open(filepath, O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
@@ -581,9 +618,14 @@ void *handle_http_client(void *arg) {
|
|||||||
free(mime_type);
|
free(mime_type);
|
||||||
goto cleanup;
|
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];
|
char response_header[1024];
|
||||||
snprintf(response_header, sizeof(response_header),
|
int header_len = snprintf(response_header, sizeof(response_header),
|
||||||
"HTTP/1.1 200 OK\r\n"
|
"HTTP/1.1 200 OK\r\n"
|
||||||
"Content-Length: %ld\r\n"
|
"Content-Length: %ld\r\n"
|
||||||
"Content-Type: %s\r\n"
|
"Content-Type: %s\r\n"
|
||||||
@@ -595,8 +637,9 @@ void *handle_http_client(void *arg) {
|
|||||||
|
|
||||||
free(mime_type);
|
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;
|
off_t offset = 0;
|
||||||
ssize_t sent = sendfile(client_socket, fd, &offset, st.st_size);
|
ssize_t sent = sendfile(client_socket, fd, &offset, st.st_size);
|
||||||
if (sent != st.st_size) {
|
if (sent != st.st_size) {
|
||||||
@@ -606,6 +649,8 @@ void *handle_http_client(void *arg) {
|
|||||||
close(fd);
|
close(fd);
|
||||||
log_event("Served requested file successfully.");
|
log_event("Served requested file successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done_serving:
|
||||||
} else if (bytes_received < 0) {
|
} else if (bytes_received < 0) {
|
||||||
HANDLE_ERROR("Error receiving request");
|
HANDLE_ERROR("Error receiving request");
|
||||||
}
|
}
|
||||||
@@ -787,55 +832,94 @@ void *handle_https_client(void *arg) {
|
|||||||
|
|
||||||
// Get MIME type
|
// Get MIME type
|
||||||
char *mime_type = get_mime_type(filepath);
|
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);
|
int fd = open(filepath, O_RDONLY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
perror("open error");
|
|
||||||
log_event("File open failed");
|
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));
|
SSL_write(ssl, not_found_response, strlen(not_found_response));
|
||||||
free(mime_type);
|
free(mime_type);
|
||||||
goto cleanup;
|
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:
|
cleanup:
|
||||||
if (ssl) {
|
if (ssl) {
|
||||||
@@ -874,12 +958,13 @@ void shutdown_server() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all threads with timeout
|
// Wait for all threads with timeout
|
||||||
struct timespec ts;
|
time_t start_time = time(NULL);
|
||||||
clock_gettime(CLOCK_REALTIME, &ts);
|
|
||||||
ts.tv_sec += 5; // 5 second timeout
|
|
||||||
|
|
||||||
pthread_mutex_lock(&thread_count_mutex);
|
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);
|
pthread_cond_timedwait(&thread_pool_cond, &thread_count_mutex, &ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -896,6 +981,8 @@ void shutdown_server() {
|
|||||||
// Cleanup resources
|
// Cleanup resources
|
||||||
cleanup_openssl();
|
cleanup_openssl();
|
||||||
cleanup_thread_pool();
|
cleanup_thread_pool();
|
||||||
|
cleanup_mmap_cache();
|
||||||
|
cleanup_buffer_pool();
|
||||||
|
|
||||||
if (rate_limits) {
|
if (rate_limits) {
|
||||||
free(rate_limits);
|
free(rate_limits);
|
||||||
@@ -912,27 +999,41 @@ void shutdown_server() {
|
|||||||
file_cache = NULL;
|
file_cache = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (client_threads) {
|
||||||
|
free(client_threads);
|
||||||
|
client_threads = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
log_event("Server shutdown completed.");
|
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;
|
||||||
|
|
||||||
|
method[0] = '\0';
|
||||||
|
url[0] = '\0';
|
||||||
|
protocol[0] = '\0';
|
||||||
|
|
||||||
char *saveptr1, *saveptr2;
|
char *saveptr1, *saveptr2;
|
||||||
char *line = strtok_r(request_buffer, "\r\n", &saveptr1);
|
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);
|
char *token = strtok_r(line, " ", &saveptr2);
|
||||||
if (token == NULL) return -1;
|
if (token == NULL || strlen(token) > 7) return -1;
|
||||||
strncpy(method, token, 7); method[7] = '\0';
|
strncpy(method, token, 7);
|
||||||
|
method[7] = '\0';
|
||||||
|
|
||||||
token = strtok_r(NULL, " ", &saveptr2);
|
token = strtok_r(NULL, " ", &saveptr2);
|
||||||
if (token == NULL) return -1;
|
if (token == NULL || strlen(token) > 255) return -1;
|
||||||
strncpy(url, token, 255); url[255] = '\0';
|
strncpy(url, token, 255);
|
||||||
|
url[255] = '\0';
|
||||||
|
|
||||||
token = strtok_r(NULL, " ", &saveptr2);
|
token = strtok_r(NULL, " ", &saveptr2);
|
||||||
if (token == NULL) return -1;
|
if (token == NULL || strlen(token) > 15) return -1;
|
||||||
strncpy(protocol, token, 15); protocol[15] = '\0';
|
strncpy(protocol, token, 15);
|
||||||
|
protocol[15] = '\0';
|
||||||
|
|
||||||
return 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() {
|
int main() {
|
||||||
if (load_config("server.conf", &config) != 0) {
|
if (load_config("server.conf", &config) != 0) {
|
||||||
printf("Using default configuration.\n");
|
printf("Using default configuration.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
config.running = 1;
|
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) {
|
if (config.use_https) {
|
||||||
initialize_openssl();
|
initialize_openssl();
|
||||||
@@ -1139,69 +1263,92 @@ char* sanitize_url(const char *url) {
|
|||||||
if (!url) return NULL;
|
if (!url) return NULL;
|
||||||
|
|
||||||
size_t url_len = strlen(url);
|
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) {
|
if (!sanitized) {
|
||||||
log_event("Memory allocation failed in sanitize_url");
|
log_event("Memory allocation failed in sanitize_url");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i, j = 0;
|
int j = 0;
|
||||||
int slash_count = 0;
|
int slash_count = 0;
|
||||||
int dot_count = 0;
|
int consecutive_dots = 0;
|
||||||
|
bool last_was_slash = false;
|
||||||
|
|
||||||
// Must start with '/'
|
// Must start with '/'
|
||||||
if (url[0] != '/') {
|
if (url[0] != '/') {
|
||||||
sanitized[j++] = '/';
|
sanitized[j++] = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; url[i]; i++) {
|
for (size_t i = 0; i < url_len && j < (int)url_len; i++) {
|
||||||
if (j >= 255) { // Prevent buffer overflow
|
char c = url[i];
|
||||||
free(sanitized);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset dot count on directory change
|
// Check for null bytes (security)
|
||||||
if (url[i] == '/') {
|
if (c == '\0') break;
|
||||||
dot_count = 0;
|
|
||||||
|
// Handle slashes
|
||||||
|
if (c == '/') {
|
||||||
|
if (last_was_slash) continue;
|
||||||
|
last_was_slash = true;
|
||||||
|
consecutive_dots = 0;
|
||||||
slash_count++;
|
slash_count++;
|
||||||
if (slash_count > 10) { // Limit directory depth
|
|
||||||
|
if (slash_count > 20) {
|
||||||
free(sanitized);
|
free(sanitized);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sanitized[j++] = c;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count consecutive dots
|
last_was_slash = false;
|
||||||
if (url[i] == '.') {
|
|
||||||
dot_count++;
|
// Handle dots (prevent traversal)
|
||||||
if (dot_count > 1) { // Prevent directory 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);
|
free(sanitized);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
dot_count = 0;
|
consecutive_dots = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only allow safe characters
|
// Only allow safe characters (alphanumeric, dash, underscore, dot)
|
||||||
if (isalnum((unsigned char)url[i]) ||
|
if (isalnum((unsigned char)c) || c == '-' || c == '_' || c == '.') {
|
||||||
url[i] == '/' ||
|
sanitized[j++] = c;
|
||||||
url[i] == '.' ||
|
} else if (c == '%') {
|
||||||
url[i] == '-' ||
|
// URL encoding - only allow safe encoded characters
|
||||||
url[i] == '_') {
|
if (i + 2 < url_len && isxdigit(url[i+1]) && isxdigit(url[i+2])) {
|
||||||
sanitized[j++] = url[i];
|
sanitized[j++] = c;
|
||||||
|
} else {
|
||||||
|
free(sanitized);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Skip other characters silently
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure proper termination
|
|
||||||
sanitized[j] = '\0';
|
sanitized[j] = '\0';
|
||||||
|
|
||||||
// Additional security checks
|
// Final security checks
|
||||||
if (strstr(sanitized, "//") || // No double slashes
|
if (j == 0 || j > 2048) {
|
||||||
strstr(sanitized, "./") || // No current directory
|
free(sanitized);
|
||||||
strstr(sanitized, "..") || // No parent directory
|
return NULL;
|
||||||
strstr(sanitized, "/.") || // No hidden files
|
}
|
||||||
strlen(sanitized) < 1) { // Must have content
|
|
||||||
|
// Check for dangerous patterns
|
||||||
|
if (strstr(sanitized, "/../") ||
|
||||||
|
strstr(sanitized, "/./") ||
|
||||||
|
strstr(sanitized, "//") ||
|
||||||
|
(strlen(sanitized) >= 3 && strcmp(sanitized + strlen(sanitized) - 3, "/..") == 0)) {
|
||||||
free(sanitized);
|
free(sanitized);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -1265,15 +1412,11 @@ int check_rate_limit(const char *ip) {
|
|||||||
return 1; // Request allowed
|
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() {
|
void cleanup_thread_pool() {
|
||||||
|
if (!thread_pool) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < thread_pool_size; i++) {
|
for (int i = 0; i < thread_pool_size; i++) {
|
||||||
if (thread_pool[i].busy) {
|
if (thread_pool[i].busy) {
|
||||||
pthread_cancel(thread_pool[i].thread);
|
pthread_cancel(thread_pool[i].thread);
|
||||||
@@ -1281,6 +1424,8 @@ void cleanup_thread_pool() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(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) {
|
void cache_file(const char *path, const char *data, size_t size, const char *mime_type) {
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ void init_config(ServerConfig *config) {
|
|||||||
config->enable_http2 = false;
|
config->enable_http2 = false;
|
||||||
config->enable_websocket = false;
|
config->enable_websocket = false;
|
||||||
strcpy(config->www_path, "www");
|
strcpy(config->www_path, "www");
|
||||||
|
config->max_connections = 1024;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ typedef struct {
|
|||||||
bool enable_http2;
|
bool enable_http2;
|
||||||
bool enable_websocket;
|
bool enable_websocket;
|
||||||
char www_path[256];
|
char www_path[256];
|
||||||
|
int max_connections;
|
||||||
} ServerConfig;
|
} ServerConfig;
|
||||||
|
|
||||||
int load_config(const char *filename, ServerConfig *config);
|
int load_config(const char *filename, ServerConfig *config);
|
||||||
|
|||||||
Reference in New Issue
Block a user