feat(config): use toml config file
This commit is contained in:
@@ -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
@@ -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]]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.");
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user