Refactor server configuration management
- Removed old server configuration files (server.json, server.log, server_config.c, server_config.h). - Introduced a new configuration file (server.conf) with a more structured format. - Implemented a configuration parser (config_parser.c) to read and apply settings from server.conf. - Updated server logic to utilize the new configuration structure. - Enhanced logging functionality and added rate limiting features. - Improved error handling and security measures in request processing.
This commit is contained in:
8
Makefile
8
Makefile
@@ -11,18 +11,18 @@ NC := \033[0m
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -Wall -Wextra -O2 -D_GNU_SOURCE
|
CFLAGS = -Wall -Wextra -O2 -D_GNU_SOURCE
|
||||||
LDFLAGS = -pthread
|
LDFLAGS = -pthread
|
||||||
LIBS = -lssl -lcrypto -lcjson -lmagic
|
LIBS = -lssl -lcrypto -lmagic
|
||||||
|
|
||||||
# Source files and object files
|
# Source files and object files
|
||||||
SRCS = server.c config_parser.c server_config.c
|
SRCS = src/server.c src/config_parser.c src/server_config.c
|
||||||
OBJS = $(SRCS:.c=.o)
|
OBJS = $(SRCS:.c=.o)
|
||||||
TARGET = server
|
TARGET = server
|
||||||
|
|
||||||
# Header files
|
# Header files
|
||||||
HEADERS = server_config.h
|
HEADERS = src/server_config.h
|
||||||
|
|
||||||
# Include directories
|
# Include directories
|
||||||
INCLUDES = -I/usr/include/cjson
|
INCLUDES =
|
||||||
|
|
||||||
# Count total number of source files
|
# Count total number of source files
|
||||||
TOTAL_FILES := $(words $(SRCS))
|
TOTAL_FILES := $(words $(SRCS))
|
||||||
|
|||||||
57
README.md
57
README.md
@@ -137,27 +137,47 @@ openssl req -x509 -newkey rsa:2048 \
|
|||||||
|
|
||||||
### Server Configuration
|
### Server Configuration
|
||||||
|
|
||||||
Create or edit `server.json` in the project root:
|
Create or edit `server.conf` in the project root. Carbon uses a traditional Linux-style configuration format with `key = value` pairs:
|
||||||
|
|
||||||
```json
|
```conf
|
||||||
{
|
# Carbon Web Server Configuration File
|
||||||
"port": 8080,
|
# Lines starting with # are comments
|
||||||
"use_https": false,
|
|
||||||
"log_file": "log/server.log",
|
# Server listening port
|
||||||
"max_threads": 4,
|
port = 8080
|
||||||
"running": true,
|
|
||||||
"server_name": "localhost",
|
# Enable HTTPS (requires valid certificates in certs/ directory)
|
||||||
"verbose": true
|
use_https = false
|
||||||
}
|
|
||||||
|
# Log file location
|
||||||
|
log_file = log/server.log
|
||||||
|
|
||||||
|
# Maximum number of worker threads
|
||||||
|
max_threads = 4
|
||||||
|
|
||||||
|
# Server running state
|
||||||
|
running = true
|
||||||
|
|
||||||
|
# Server name or IP address (used for logging and response headers)
|
||||||
|
server_name = localhost
|
||||||
|
|
||||||
|
# Enable verbose logging
|
||||||
|
verbose = true
|
||||||
```
|
```
|
||||||
|
|
||||||
**Configuration Options:**
|
**Configuration Options:**
|
||||||
- `port`: HTTP port (default: 8080)
|
- `port`: HTTP port (default: 8080)
|
||||||
- `use_https`: Enable HTTPS (requires SSL certificates)
|
- `use_https`: Enable HTTPS - accepts: true/false, yes/no, on/off, 1/0 (requires SSL certificates)
|
||||||
- `log_file`: Path to log file
|
- `log_file`: Path to log file
|
||||||
- `max_threads`: Number of worker threads
|
- `max_threads`: Number of worker threads
|
||||||
- `server_name`: Your domain or IP address
|
- `server_name`: Your domain or IP address
|
||||||
- `verbose`: Enable detailed logging
|
- `verbose`: Enable detailed logging - accepts: true/false, yes/no, on/off, 1/0
|
||||||
|
|
||||||
|
**Note:** Boolean values are flexible and accept multiple formats:
|
||||||
|
- True: `true`, `yes`, `on`, `1`
|
||||||
|
- False: `false`, `no`, `off`, `0`
|
||||||
|
|
||||||
|
Values can optionally be quoted with single or double quotes.
|
||||||
|
|
||||||
### Directory Structure
|
### Directory Structure
|
||||||
|
|
||||||
@@ -215,12 +235,13 @@ curl -k https://localhost:443
|
|||||||
|
|
||||||
```
|
```
|
||||||
Carbon/
|
Carbon/
|
||||||
├── server.c # Main server implementation
|
├── src/
|
||||||
├── server_config.c # Configuration management
|
│ ├── server.c # Main server implementation
|
||||||
├── server_config.h # Configuration headers
|
│ ├── server_config.c # Configuration management
|
||||||
├── config_parser.c # JSON configuration parser
|
│ ├── server_config.h # Configuration headers
|
||||||
|
│ └── config_parser.c # Configuration file parser
|
||||||
├── Makefile # Build configuration
|
├── Makefile # Build configuration
|
||||||
├── server.json # Server configuration file
|
├── server.conf # Server configuration file (Linux-style)
|
||||||
├── README.md # This file
|
├── README.md # This file
|
||||||
├── LICENSE # MIT License
|
├── LICENSE # MIT License
|
||||||
├── certs/ # SSL certificates (create this)
|
├── certs/ # SSL certificates (create this)
|
||||||
|
|||||||
108
config_parser.c
108
config_parser.c
@@ -1,108 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "server_config.h"
|
|
||||||
#include <cJSON.h>
|
|
||||||
|
|
||||||
int load_config(const char *filename, ServerConfig *config) {
|
|
||||||
FILE *fp = fopen(filename, "r");
|
|
||||||
if (!fp) {
|
|
||||||
perror("Error opening config file");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
|
||||||
long file_size = ftell(fp);
|
|
||||||
fseek(fp, 0, SEEK_SET);
|
|
||||||
|
|
||||||
char *buffer = malloc(file_size + 1);
|
|
||||||
if (!buffer) {
|
|
||||||
perror("Error allocating memory for config file");
|
|
||||||
fclose(fp);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t items_read = fread(buffer, file_size, 1, fp);
|
|
||||||
fclose(fp); // Close file immediately after reading
|
|
||||||
|
|
||||||
if (items_read != 1) {
|
|
||||||
perror("Error reading config file");
|
|
||||||
free(buffer);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[file_size] = '\0';
|
|
||||||
|
|
||||||
cJSON *root = cJSON_Parse(buffer);
|
|
||||||
free(buffer); // Free buffer after parsing
|
|
||||||
|
|
||||||
if (!root) {
|
|
||||||
const char *error_ptr = cJSON_GetErrorPtr();
|
|
||||||
if (error_ptr != NULL) {
|
|
||||||
fprintf(stderr, "Error before: %s\n", error_ptr);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON *port = cJSON_GetObjectItemCaseSensitive(root, "port");
|
|
||||||
if (cJSON_IsNumber(port)) {
|
|
||||||
config->port = port->valueint;
|
|
||||||
printf("load_config: port = %d\n", config->port);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "load_config: port not found or not a number. Using default.\n");
|
|
||||||
config->port = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON *use_https = cJSON_GetObjectItemCaseSensitive(root, "use_https");
|
|
||||||
if (cJSON_IsBool(use_https)) {
|
|
||||||
config->use_https = cJSON_IsTrue(use_https);
|
|
||||||
printf("load_config: use_https = %d\n", config->use_https);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "load_config: use_https not found or not a boolean. Using default.\n");
|
|
||||||
config->use_https = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON *log_file = cJSON_GetObjectItemCaseSensitive(root, "log_file");
|
|
||||||
if (cJSON_IsString(log_file) && (log_file->valuestring != NULL)) {
|
|
||||||
strncpy(config->log_file, log_file->valuestring, sizeof(config->log_file) - 1);
|
|
||||||
config->log_file[sizeof(config->log_file) - 1] = '\0';
|
|
||||||
printf("load_config: log_file = %s\n", config->log_file);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "load_config: log_file not found or not a string. Using default.\n");
|
|
||||||
strcpy(config->log_file, "server.log");
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON *max_threads = cJSON_GetObjectItemCaseSensitive(root, "max_threads");
|
|
||||||
if (cJSON_IsNumber(max_threads)) {
|
|
||||||
config->max_threads = max_threads->valueint;
|
|
||||||
printf("load_config: max_threads = %d\n", config->max_threads);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "load_config: max_threads not found or not a number. Using default.\n");
|
|
||||||
config->max_threads = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON *running = cJSON_GetObjectItemCaseSensitive(root, "running");
|
|
||||||
if (cJSON_IsBool(running)) {
|
|
||||||
config->running = cJSON_IsTrue(running);
|
|
||||||
printf("load_config: running = %d\n", config->running);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "load_config: running not found or not a boolean. Using default.\n");
|
|
||||||
config->running = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
cJSON *server_name = cJSON_GetObjectItemCaseSensitive(root, "server_name");
|
|
||||||
if (cJSON_IsString(server_name) && (server_name->valuestring != NULL)) {
|
|
||||||
strncpy(config->server_name, server_name->valuestring, sizeof(config->server_name) - 1);
|
|
||||||
config->server_name[sizeof(config->server_name) - 1] = '\0';
|
|
||||||
printf("load_config: server_name = %s\n", config->server_name);
|
|
||||||
if (strcmp(config->server_name, "Your_domain/IP") == 0) {
|
|
||||||
fprintf(stderr, "WARNING: server_name is set to 127.0.0.1\nPlease set server_name in server.json to the server's IP address or domain name for proper operation.\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "load_config: server_name not found or not a string. Using default.\n");
|
|
||||||
strcpy(config->server_name, "127.0.0.1");
|
|
||||||
}
|
|
||||||
cJSON_Delete(root);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
23
server.conf
Normal file
23
server.conf
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
# Server name or IP address (used for logging and response headers)
|
||||||
|
server_name = 10.0.0.206
|
||||||
|
|
||||||
|
# Enable verbose logging
|
||||||
|
verbose = true
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"port": 8080,
|
|
||||||
"use_https": false,
|
|
||||||
"log_file": "log/server.log",
|
|
||||||
"max_threads": 4,
|
|
||||||
"running": true,
|
|
||||||
"server_name": "Your_domain/IP",
|
|
||||||
"verbose": true
|
|
||||||
}
|
|
||||||
125
src/config_parser.c
Normal file
125
src/config_parser.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "server_config.h"
|
||||||
|
|
||||||
|
// Trim whitespace from both ends of a string
|
||||||
|
static char* trim_whitespace(char *str) {
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
// Trim leading space
|
||||||
|
while(isspace((unsigned char)*str)) str++;
|
||||||
|
|
||||||
|
if(*str == 0) return str;
|
||||||
|
|
||||||
|
// Trim trailing space
|
||||||
|
end = str + strlen(str) - 1;
|
||||||
|
while(end > str && isspace((unsigned char)*end)) end--;
|
||||||
|
|
||||||
|
end[1] = '\0';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a boolean value (true/false, yes/no, on/off, 1/0)
|
||||||
|
static bool parse_bool(const char *value) {
|
||||||
|
if (strcasecmp(value, "true") == 0 ||
|
||||||
|
strcasecmp(value, "yes") == 0 ||
|
||||||
|
strcasecmp(value, "on") == 0 ||
|
||||||
|
strcmp(value, "1") == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_config(const char *filename, ServerConfig *config) {
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
if (!fp) {
|
||||||
|
perror("Error opening config file");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[512];
|
||||||
|
int line_number = 0;
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
line_number++;
|
||||||
|
|
||||||
|
// Remove newline
|
||||||
|
line[strcspn(line, "\r\n")] = 0;
|
||||||
|
|
||||||
|
// Trim whitespace
|
||||||
|
char *trimmed = trim_whitespace(line);
|
||||||
|
|
||||||
|
// Skip empty lines and comments
|
||||||
|
if (trimmed[0] == '\0' || trimmed[0] == '#' || trimmed[0] == ';') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the delimiter (= or space)
|
||||||
|
char *delim = strchr(trimmed, '=');
|
||||||
|
if (!delim) {
|
||||||
|
// Try space as delimiter
|
||||||
|
delim = strchr(trimmed, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!delim) {
|
||||||
|
fprintf(stderr, "Warning: Invalid config line %d: %s\n", line_number, trimmed);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split into key and value
|
||||||
|
*delim = '\0';
|
||||||
|
char *key = trim_whitespace(trimmed);
|
||||||
|
char *value = trim_whitespace(delim + 1);
|
||||||
|
|
||||||
|
// Remove quotes from value if present
|
||||||
|
if ((value[0] == '"' || value[0] == '\'') &&
|
||||||
|
value[strlen(value) - 1] == value[0]) {
|
||||||
|
value[strlen(value) - 1] = '\0';
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse configuration options
|
||||||
|
if (strcasecmp(key, "port") == 0) {
|
||||||
|
config->port = atoi(value);
|
||||||
|
printf("load_config: port = %d\n", config->port);
|
||||||
|
}
|
||||||
|
else if (strcasecmp(key, "use_https") == 0) {
|
||||||
|
config->use_https = parse_bool(value);
|
||||||
|
printf("load_config: use_https = %d\n", config->use_https);
|
||||||
|
}
|
||||||
|
else if (strcasecmp(key, "log_file") == 0) {
|
||||||
|
strncpy(config->log_file, value, sizeof(config->log_file) - 1);
|
||||||
|
config->log_file[sizeof(config->log_file) - 1] = '\0';
|
||||||
|
printf("load_config: log_file = %s\n", config->log_file);
|
||||||
|
}
|
||||||
|
else if (strcasecmp(key, "max_threads") == 0) {
|
||||||
|
config->max_threads = atoi(value);
|
||||||
|
printf("load_config: max_threads = %d\n", config->max_threads);
|
||||||
|
}
|
||||||
|
else if (strcasecmp(key, "running") == 0) {
|
||||||
|
config->running = parse_bool(value);
|
||||||
|
printf("load_config: running = %d\n", config->running);
|
||||||
|
}
|
||||||
|
else if (strcasecmp(key, "server_name") == 0) {
|
||||||
|
strncpy(config->server_name, value, sizeof(config->server_name) - 1);
|
||||||
|
config->server_name[sizeof(config->server_name) - 1] = '\0';
|
||||||
|
printf("load_config: server_name = %s\n", config->server_name);
|
||||||
|
if (strcmp(config->server_name, "Your_domain/IP") == 0) {
|
||||||
|
fprintf(stderr, "WARNING: server_name is set to default\nPlease set server_name in server.conf to the server's IP address or domain name for proper operation.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcasecmp(key, "verbose") == 0) {
|
||||||
|
config->verbose = parse_bool(value);
|
||||||
|
printf("load_config: verbose = %d\n", config->verbose);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, "Warning: Unknown config option '%s' on line %d\n", key, line_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <cJSON.h>
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -778,7 +777,7 @@ void signal_handler(int sig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
if (load_config("server.json", &config) != 0) {
|
if (load_config("server.conf", &config) != 0) {
|
||||||
printf("Using default configuration.\n");
|
printf("Using default configuration.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user