feat(config): use toml config file

This commit is contained in:
2026-02-25 19:06:52 +01:00
parent a276e644a7
commit 825c02b626
10 changed files with 73 additions and 36 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ meson compile -C build
## Usage ## Usage
1. Copy `config.yaml` and `www/` directory to your working directory 1. Copy `config.toml` and `www/` directory to your working directory
2. Run `./build/cws` 2. Run `./build/cws`
3. Open `http://localhost:3030` in your browser 3. Open `http://localhost:3030` in your browser
+2 -1
View File
@@ -6,7 +6,8 @@ port = "3030"
root = "www" root = "www"
[[virtual_hosts]] [[virtual_hosts]]
domain = "localhost" # "default" domain is required
domain = "default"
root = "www" root = "www"
[[virtual_hosts.pages]] [[virtual_hosts.pages]]
+2 -8
View File
@@ -4,17 +4,11 @@
#include "http/request.h" #include "http/request.h"
#include "http/response.h" #include "http/response.h"
/* Configuration for static file serving */
typedef struct cws_handler_config { typedef struct cws_handler_config {
const char *root_dir; char *root;
const char *index_file; char *domain;
} cws_handler_config_s; } cws_handler_config_s;
/* Static file handler */
cws_response_s *cws_handler_static_file(cws_request_s *request, cws_handler_config_s *config); cws_response_s *cws_handler_static_file(cws_request_s *request, cws_handler_config_s *config);
/* Error handlers */
cws_response_s *cws_handler_not_found(cws_request_s *request);
cws_response_s *cws_handler_not_implemented(cws_request_s *request);
#endif #endif
+3
View File
@@ -12,6 +12,7 @@
typedef struct cws_request { typedef struct cws_request {
cws_http_method_e method; cws_http_method_e method;
string_s *host;
string_s *path; string_s *path;
string_s *query_string; string_s *query_string;
string_s *http_version; string_s *http_version;
@@ -21,6 +22,8 @@ typedef struct cws_request {
cws_request_s *cws_http_parse(string_s *request_str); cws_request_s *cws_http_parse(string_s *request_str);
char *cws_http_get_host(cws_request_s *request);
void cws_http_free(cws_request_s *request); void cws_http_free(cws_request_s *request);
#endif #endif
+3 -1
View File
@@ -2,9 +2,11 @@ project(
'cws', 'cws',
'c', 'c',
version: '0.1.0', version: '0.1.0',
default_options: ['c_std=c11', 'warning_level=3'], default_options: ['c_std=gnu23', 'warning_level=3'],
) )
add_global_arguments('-Wno-pedantic', language: 'c')
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
subdir('src') subdir('src')
+18 -3
View File
@@ -72,6 +72,17 @@ static bool parse_vhosts(cws_config_s *config, toml_result_t result) {
return true; return true;
} }
static bool find_default(cws_config_s *config) {
for (unsigned i = 0; i < config->virtual_hosts_count; ++i) {
cws_vhost_s *vh = config->virtual_hosts;
if (!strcmp(vh[i].domain, "default")) {
return true;
}
}
return false;
}
static bool parse_toml(cws_config_s *config) { static bool parse_toml(cws_config_s *config) {
const char *path = "config.toml"; const char *path = "config.toml";
@@ -104,7 +115,7 @@ static bool parse_toml(cws_config_s *config) {
toml_free(result); toml_free(result);
return true; return find_default(config);
} }
cws_config_s *cws_config_init(void) { cws_config_s *cws_config_init(void) {
@@ -113,7 +124,9 @@ cws_config_s *cws_config_init(void) {
return NULL; return NULL;
} }
parse_toml(config); if (!parse_toml(config)) {
return NULL;
}
return config; return config;
} }
@@ -156,5 +169,7 @@ void cws_config_free(cws_config_s *config) {
} }
} }
free(config); if (config) {
free(config);
}
} }
+21 -8
View File
@@ -27,7 +27,19 @@ static void worker_close_client(int epfd, int client_fd) {
close(client_fd); close(client_fd);
} }
static cws_return worker_handle_client_data(int epfd, int client_fd) { static cws_vhost_s *get_vhost(cws_config_s *config, char *host) {
for (unsigned i = 0; i < config->virtual_hosts_count; ++i) {
cws_vhost_s *vh = config->virtual_hosts;
if (!strcmp(vh[i].domain, host)) {
return vh;
}
}
/* ?? */
return NULL;
}
static cws_return worker_handle_client_data(int epfd, int client_fd, cws_config_s *config) {
string_s *data = string_new("", 4096); string_s *data = string_new("", 4096);
/* Read data from socket */ /* Read data from socket */
@@ -55,14 +67,15 @@ static cws_return worker_handle_client_data(int epfd, int client_fd) {
} }
/* Configure handler */ /* Configure handler */
/* TODO: use vhosts */ char *host = cws_http_get_host(request);
cws_handler_config_s config = { cws_vhost_s *vh = get_vhost(config, host);
.root_dir = "www", cws_handler_config_s conf = {
.index_file = "index.html", .domain = vh->domain,
.root = vh->root,
}; };
/* Handle request and generate response */ /* Handle request and generate response */
cws_response_s *response = cws_handler_static_file(request, &config); cws_response_s *response = cws_handler_static_file(request, &conf);
/* Send response */ /* Send response */
if (response) { if (response) {
@@ -81,7 +94,7 @@ static cws_return worker_handle_client_data(int epfd, int client_fd) {
/* Worker thread: process events on its epoll instance */ /* Worker thread: process events on its epoll instance */
static void *cws_worker_loop(void *arg) { static void *cws_worker_loop(void *arg) {
cws_worker_s *worker = arg; cws_worker_s *worker = (cws_worker_s *)arg;
struct epoll_event events[64]; struct epoll_event events[64];
while (cws_server_run) { while (cws_server_run) {
@@ -94,7 +107,7 @@ static void *cws_worker_loop(void *arg) {
for (int i = 0; i < nfds; ++i) { for (int i = 0; i < nfds; ++i) {
int client_fd = events[i].data.fd; int client_fd = events[i].data.fd;
worker_handle_client_data(worker->epfd, client_fd); worker_handle_client_data(worker->epfd, client_fd, worker->config);
} }
} }
+13 -14
View File
@@ -6,11 +6,12 @@
/* Sanitize and resolve file path */ /* Sanitize and resolve file path */
static string_s *resolve_file_path(const char *url_path, cws_handler_config_s *config) { static string_s *resolve_file_path(const char *url_path, cws_handler_config_s *config) {
string_s *full_path = string_new(config->root_dir, 256); string_s *full_path = string_new(config->root, 256);
if (strcmp(url_path, "/") == 0) { if (strcmp(url_path, "/") == 0) {
string_append(full_path, "/"); string_append(full_path, "/");
string_append(full_path, config->index_file); /* Use vhost index file */
string_append(full_path, "index.html");
return full_path; return full_path;
} }
@@ -24,13 +25,21 @@ static bool file_exists(const char *filepath) {
return stat(filepath, &st) == 0 && S_ISREG(st.st_mode); return stat(filepath, &st) == 0 && S_ISREG(st.st_mode);
} }
static cws_response_s *cws_handler_not_found(void) {
return cws_response_error(HTTP_NOT_FOUND, "The requested resource was not found.");
}
static cws_response_s *cws_handler_not_implemented(void) {
return cws_response_error(HTTP_NOT_IMPLEMENTED, "Method not implemented.");
}
cws_response_s *cws_handler_static_file(cws_request_s *request, cws_handler_config_s *config) { cws_response_s *cws_handler_static_file(cws_request_s *request, cws_handler_config_s *config) {
if (!request || !config) { if (!request || !config) {
return cws_response_error(HTTP_INTERNAL_ERROR, "Invalid request or configuration"); return cws_response_error(HTTP_INTERNAL_ERROR, "Invalid request or configuration");
} }
if (request->method != HTTP_GET) { if (request->method != HTTP_GET) {
return cws_handler_not_implemented(request); return cws_handler_not_implemented();
} }
string_s *filepath = resolve_file_path(string_cstr(request->path), config); string_s *filepath = resolve_file_path(string_cstr(request->path), config);
@@ -38,7 +47,7 @@ cws_response_s *cws_handler_static_file(cws_request_s *request, cws_handler_conf
if (!file_exists(path)) { if (!file_exists(path)) {
string_free(filepath); string_free(filepath);
return cws_handler_not_found(request); return cws_handler_not_found();
} }
cws_response_s *response = cws_response_new(HTTP_OK); cws_response_s *response = cws_response_new(HTTP_OK);
@@ -53,13 +62,3 @@ cws_response_s *cws_handler_static_file(cws_request_s *request, cws_handler_conf
return response; return response;
} }
cws_response_s *cws_handler_not_found(cws_request_s *request) {
(void)request;
return cws_response_error(HTTP_NOT_FOUND, "The requested resource was not found.");
}
cws_response_s *cws_handler_not_implemented(cws_request_s *request) {
(void)request;
return cws_response_error(HTTP_NOT_IMPLEMENTED, "Method not implemented.");
}
+9
View File
@@ -183,6 +183,15 @@ cws_request_s *cws_http_parse(string_s *request_str) {
return request; return request;
} }
char *cws_http_get_host(cws_request_s *request) {
bucket_s *host = hm_get(request->headers, "Host");
if (!host) {
return "default";
}
return (char *)host->value;
}
void cws_http_free(cws_request_s *request) { void cws_http_free(cws_request_s *request) {
if (!request) { if (!request) {
return; return;
+1
View File
@@ -22,6 +22,7 @@ int main(void) {
cws_config_s *config = cws_config_init(); cws_config_s *config = cws_config_init();
if (!config) { if (!config) {
cws_log_error("Unable to parse config");
cws_log_shutdown(); cws_log_shutdown();
return EXIT_FAILURE; return EXIT_FAILURE;
} }