Compare commits

...

5 Commits

12 changed files with 151 additions and 89 deletions
+1
View File
@@ -22,6 +22,7 @@ typedef struct cws_config {
int workers; int workers;
cws_vhost_s *virtual_hosts; cws_vhost_s *virtual_hosts;
unsigned virtual_hosts_count; unsigned virtual_hosts_count;
cws_vhost_s *default_vh;
} cws_config_s; } cws_config_s;
cws_config_s *cws_config_init(void); cws_config_s *cws_config_init(void);
+2 -2
View File
@@ -4,8 +4,8 @@
#include <myclib/mystring.h> #include <myclib/mystring.h>
#include <sys/types.h> #include <sys/types.h>
ssize_t cws_socket_read(int sockfd, string_s *str); int cws_socket_read(int sockfd, string_s *str);
ssize_t cws_socket_send(int sockfd, const char *buffer, size_t len, int flags); int cws_socket_send(int sockfd, const char *buffer, size_t len, int flags);
#endif #endif
+3 -7
View File
@@ -6,10 +6,6 @@
#include <myclib/mystring.h> #include <myclib/mystring.h>
#include <stddef.h> #include <stddef.h>
#define CWS_HTTP_CONTENT_TYPE 64
#define CWS_HTTP_HEADER_MAX 512
#define CWS_HTTP_HEADER_CONTENT_MAX 1024
typedef struct cws_request { typedef struct cws_request {
cws_http_method_e method; cws_http_method_e method;
string_s *host; string_s *host;
@@ -20,10 +16,10 @@ typedef struct cws_request {
string_s *body; string_s *body;
} cws_request_s; } cws_request_s;
cws_request_s *cws_http_parse(string_s *request_str); cws_request_s *cws_request_parse(string_s *request_str);
char *cws_http_get_host(cws_request_s *request); char *cws_request_get_header(cws_request_s *request, const char *header);
void cws_http_free(cws_request_s *request); void cws_request_free(cws_request_s *request);
#endif #endif
+6 -3
View File
@@ -9,9 +9,12 @@ add_global_arguments('-Wno-pedantic', language: 'c')
cc = meson.get_compiler('c') cc = meson.get_compiler('c')
subdir('src')
incdir = include_directories('include') incdir = include_directories('include')
srcdir = include_directories('src')
include_dirs = [incdir, srcdir]
subdir('src')
libtomlc17 = dependency('libtomlc17', required: true) libtomlc17 = dependency('libtomlc17', required: true)
libmath = cc.find_library('m', required: true) libmath = cc.find_library('m', required: true)
@@ -23,7 +26,7 @@ add_global_arguments('-DUSE_COLORS', language: 'c')
add_global_arguments('-DEVELOPER', language: 'c') add_global_arguments('-DEVELOPER', language: 'c')
add_global_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c') add_global_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c')
exe = executable('cws', server, include_directories: incdir, dependencies: deps) exe = executable('cws', server, include_directories: include_dirs, dependencies: deps)
# Test # Test
test_src = files('test/server.c') test_src = files('test/server.c')
+28 -15
View File
@@ -6,23 +6,44 @@
#include "utils/debug.h" #include "utils/debug.h"
static bool is_default(const char *domain) {
if (!strcmp(domain, "default")) {
return true;
}
return false;
}
static bool parse_vhosts(cws_config_s *config, toml_result_t result) { static bool parse_vhosts(cws_config_s *config, toml_result_t result) {
toml_datum_t vhosts = toml_seek(result.toptab, "virtual_hosts"); toml_datum_t vhosts = toml_seek(result.toptab, "virtual_hosts");
/* Retrieve virtual hosts counter */
config->virtual_hosts_count = vhosts.u.arr.size; config->virtual_hosts_count = vhosts.u.arr.size;
/* Allocate virtual hosts array */
config->virtual_hosts = malloc(sizeof *config->virtual_hosts * config->virtual_hosts_count); config->virtual_hosts = malloc(sizeof *config->virtual_hosts * config->virtual_hosts_count);
if (!config->virtual_hosts) { if (!config->virtual_hosts) {
return false; return false;
} }
/* Iterate for each virtual host */
for (int i = 0; i < vhosts.u.arr.size; ++i) { for (int i = 0; i < vhosts.u.arr.size; ++i) {
cws_vhost_s *vh = &config->virtual_hosts[i]; cws_vhost_s *vh = &config->virtual_hosts[i];
toml_datum_t elem = vhosts.u.arr.elem[i]; toml_datum_t elem = vhosts.u.arr.elem[i];
/* Retrieve vh's domain */
toml_datum_t domain = toml_seek(elem, "domain"); toml_datum_t domain = toml_seek(elem, "domain");
vh->domain = strdup(domain.u.str.ptr); vh->domain = strdup(domain.u.str.ptr);
if (!vh->domain) { if (!vh->domain) {
return false; return false;
} }
/* Check if vh->domain is the default domain */
if (is_default(vh->domain)) {
config->default_vh = vh;
}
/* Retrieve vh's root folder */
toml_datum_t root = toml_seek(elem, "root"); toml_datum_t root = toml_seek(elem, "root");
vh->root = strdup(root.u.str.ptr); vh->root = strdup(root.u.str.ptr);
if (!vh->root) { if (!vh->root) {
@@ -32,13 +53,17 @@ static bool parse_vhosts(cws_config_s *config, toml_result_t result) {
/* Pages */ /* Pages */
toml_datum_t pages = toml_seek(elem, "pages"); toml_datum_t pages = toml_seek(elem, "pages");
vh->error_pages_count = pages.u.arr.size; vh->error_pages_count = pages.u.arr.size;
/* Allocate error pages array */
vh->error_pages = malloc(sizeof *vh->error_pages * vh->error_pages_count); vh->error_pages = malloc(sizeof *vh->error_pages * vh->error_pages_count);
if (!vh->error_pages) { if (!vh->error_pages) {
return false; return false;
} }
/* Iterate for each page */
for (int j = 0; j < pages.u.arr.size; ++j) { for (int j = 0; j < pages.u.arr.size; ++j) {
toml_datum_t page = pages.u.arr.elem[i]; toml_datum_t page = pages.u.arr.elem[i];
toml_datum_t status = toml_seek(page, "status"); toml_datum_t status = toml_seek(page, "status");
vh->error_pages[j].status = strdup(status.u.str.ptr); vh->error_pages[j].status = strdup(status.u.str.ptr);
if (!vh->error_pages[i].status) { if (!vh->error_pages[i].status) {
@@ -56,17 +81,6 @@ 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";
@@ -98,15 +112,14 @@ static bool parse_toml(cws_config_s *config) {
toml_datum_t workers = toml_seek(result.toptab, "server.workers"); toml_datum_t workers = toml_seek(result.toptab, "server.workers");
config->workers = workers.u.int64; config->workers = workers.u.int64;
parse_vhosts(config, result); bool ret = parse_vhosts(config, result);
toml_free(result); toml_free(result);
return find_default(config); return ret;
} }
cws_config_s *cws_config_init(void) { cws_config_s *cws_config_init(void) {
cws_config_s *config = malloc(sizeof *config); cws_config_s *config = calloc(1, sizeof *config);
if (!config) { if (!config) {
return NULL; return NULL;
} }
+40 -22
View File
@@ -3,46 +3,64 @@
#include <errno.h> #include <errno.h>
#include <sys/socket.h> #include <sys/socket.h>
ssize_t cws_socket_read(int sockfd, string_s *str) { int cws_socket_read(int sockfd, string_s *str) {
char tmp[4096] = {0}; char tmp[4096] = {0};
ssize_t n = recv(sockfd, tmp, sizeof tmp, 0); for (;;) {
if (n < 0) { ssize_t n = recv(sockfd, tmp, sizeof tmp, 0);
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* We have some data */
if (n > 0) {
tmp[n] = '\0';
string_append(str, tmp);
return n;
}
/* Client closed */
if (n == 0) {
return 0; return 0;
} }
if (errno == EINTR) {
continue;
}
/* No data now */
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return -2;
}
/* Something happened */
return -1; return -1;
} }
if (n == 0) {
return -1;
}
tmp[n] = '\0';
string_append(str, tmp);
return n;
} }
ssize_t cws_socket_send(int sockfd, const char *buffer, size_t len, int flags) { int cws_socket_send(int sockfd, const char *buffer, size_t len, int flags) {
size_t total_sent = 0; size_t total_sent = 0;
ssize_t n;
while (total_sent < len) { while (total_sent < len) {
n = send(sockfd, buffer + total_sent, len - total_sent, flags); ssize_t n = send(sockfd, buffer + total_sent, len - total_sent, flags);
if (n < 0) { if (n > 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { total_sent += (size_t)n;
break; continue;
}
return -1;
} }
if (n == 0) { if (n == 0) {
break; break;
} }
total_sent += n; if (errno == EINTR) {
continue;
}
/* Partial write */
if (errno == EAGAIN || errno == EWOULDBLOCK) {
break;
}
return -1;
} }
return total_sent; return (ssize_t)total_sent;
} }
+25 -13
View File
@@ -10,6 +10,7 @@
#include "http/handler.h" #include "http/handler.h"
#include "http/request.h" #include "http/request.h"
#include "http/response.h" #include "http/response.h"
#include "utils/debug.h"
#include "utils/error.h" #include "utils/error.h"
/* Create epoll instance for a worker */ /* Create epoll instance for a worker */
@@ -35,32 +36,40 @@ static cws_vhost_s *get_vhost(cws_config_s *config, char *host) {
} }
} }
/* Return first domain */ /* Return default domain */
/* TODO: return default domain */ return config->default_vh;
return &config->virtual_hosts[0];
} }
static cws_return worker_handle_client_data(int epfd, int client_fd, cws_config_s *config) { 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 */
ssize_t total_bytes = cws_socket_read(client_fd, data); int total_bytes = cws_socket_read(client_fd, data);
if (total_bytes == 0) { /* Partial request, wait for more data */
/* Partial request; wait for more data */ if (total_bytes == -2) {
string_free(data); string_free(data);
return CWS_OK; return CWS_OK;
} }
if (total_bytes < 0) { /* Connection closed */
if (total_bytes == 0) {
cws_log_info("Client (fd: %d) disconnected", client_fd);
worker_close_client(epfd, client_fd);
string_free(data);
return CWS_CLIENT_DISCONNECTED_ERROR;
}
/* Client error */
if (total_bytes == -1) {
worker_close_client(epfd, client_fd); worker_close_client(epfd, client_fd);
string_free(data); string_free(data);
return CWS_CLIENT_DISCONNECTED_ERROR; return CWS_CLIENT_DISCONNECTED_ERROR;
} }
/* Parse HTTP request */ /* Parse HTTP request */
cws_request_s *request = cws_http_parse(data); cws_request_s *request = cws_request_parse(data);
string_free(data); string_free(data);
if (request == NULL) { if (request == NULL) {
worker_close_client(epfd, client_fd); worker_close_client(epfd, client_fd);
@@ -68,7 +77,7 @@ static cws_return worker_handle_client_data(int epfd, int client_fd, cws_config_
} }
/* Configure handler */ /* Configure handler */
char *host = cws_http_get_host(request); char *host = cws_request_get_header(request, "host");
cws_vhost_s *vh = get_vhost(config, host); cws_vhost_s *vh = get_vhost(config, host);
cws_handler_config_s conf = { cws_handler_config_s conf = {
.domain = vh->domain, .domain = vh->domain,
@@ -84,11 +93,14 @@ static cws_return worker_handle_client_data(int epfd, int client_fd, cws_config_
cws_response_free(response); cws_response_free(response);
} }
/* Cleanup */ /* Close connection if requested */
cws_http_free(request); /* keep-alive by default (http/1.1) */
if (!strcmp(cws_request_get_header(request, "Connection"), "close")) {
worker_close_client(epfd, client_fd);
}
/* TODO: check Connection: keep-alive */ /* Cleanup */
worker_close_client(epfd, client_fd); cws_request_free(request);
return CWS_OK; return CWS_OK;
} }
+6
View File
@@ -51,12 +51,18 @@ cws_response_s *cws_handler_static_file(cws_request_s *request, cws_handler_conf
return cws_handler_not_found(); return cws_handler_not_found();
} }
/* Allocate a response object */
/* @TODO: do not use http 200 ok as default */
cws_response_s *response = cws_response_new(HTTP_OK); cws_response_s *response = cws_response_new(HTTP_OK);
if (!response) { if (!response) {
string_free(filepath); string_free(filepath);
return cws_response_error(HTTP_INTERNAL_ERROR, "Failed to create response"); return cws_response_error(HTTP_INTERNAL_ERROR, "Failed to create response");
} }
/* Retrieve Connection header and set it in the response */
const char *conn = cws_request_get_header(request, "Connection");
cws_response_set_header(response, "Connection", conn);
cws_response_set_body_file(response, path); cws_response_set_body_file(response, path);
cws_log_debug("Serving file: %s (%zu bytes)", path, response->content_length); cws_log_debug("Serving file: %s (%zu bytes)", path, response->content_length);
string_free(filepath); string_free(filepath);
+4 -3
View File
@@ -3,9 +3,10 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "http/request.h"
#include "utils/error.h" #include "utils/error.h"
#include "internal/common.h"
static mimetype mimetypes[] = {{"html", "text/html"}, {"css", "text/css"}, {"js", "application/javascript"}, static mimetype mimetypes[] = {{"html", "text/html"}, {"css", "text/css"}, {"js", "application/javascript"},
{"jpg", "image/jpeg"}, {"png", "image/png"}, {"ico", "image/x-icon"}}; {"jpg", "image/jpeg"}, {"png", "image/png"}, {"ico", "image/x-icon"}};
@@ -18,13 +19,13 @@ int cws_mime_get_ct(const char *location_path, char *content_type) {
for (size_t i = 0; i < ARR_SIZE(mimetypes); ++i) { for (size_t i = 0; i < ARR_SIZE(mimetypes); ++i) {
if (!strcmp(ptr, mimetypes[i].ext)) { if (!strcmp(ptr, mimetypes[i].ext)) {
snprintf(content_type, CWS_HTTP_CONTENT_TYPE - 1, "%s", mimetypes[i].type); snprintf(content_type, CONTENT_TYPE_MAX - 1, "%s", mimetypes[i].type);
return CWS_OK; return CWS_OK;
} }
} }
snprintf(content_type, CWS_HTTP_CONTENT_TYPE - 1, "%s", "Content-Type not supported"); snprintf(content_type, CONTENT_TYPE_MAX - 1, "%s", "Content-Type not supported");
return CWS_OK; return CWS_OK;
} }
+21 -15
View File
@@ -9,6 +9,8 @@
#include "utils/debug.h" #include "utils/debug.h"
#include "utils/hash.h" #include "utils/hash.h"
#include "internal/common.h"
static cws_request_s *http_request_new(void) { static cws_request_s *http_request_new(void) {
cws_request_s *request = malloc(sizeof(*request)); cws_request_s *request = malloc(sizeof(*request));
if (!request) { if (!request) {
@@ -91,7 +93,7 @@ static bool parse_version(cws_request_s *req, char **cursor) {
static bool parse_headers(cws_request_s *req, char **cursor) { static bool parse_headers(cws_request_s *req, char **cursor) {
req->headers = hm_new(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn, req->headers = hm_new(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn,
sizeof(char) * CWS_HTTP_HEADER_MAX, sizeof(char) * CWS_HTTP_HEADER_CONTENT_MAX); sizeof(char) * HEADER_KEY_MAX, sizeof(char) * HEADER_VALUE_MAX);
char *s = *cursor + strspn(*cursor, "\r\n"); char *s = *cursor + strspn(*cursor, "\r\n");
while (*s != '\0' && *s != '\r') { while (*s != '\0' && *s != '\r') {
@@ -113,8 +115,8 @@ static bool parse_headers(cws_request_s *req, char **cursor) {
char *header_value = colon + 1; char *header_value = colon + 1;
header_value += strspn(header_value, " \t"); header_value += strspn(header_value, " \t");
char hk[CWS_HTTP_HEADER_MAX]; char hk[HEADER_KEY_MAX];
char hv[CWS_HTTP_HEADER_CONTENT_MAX]; char hv[HEADER_VALUE_MAX];
strncpy(hk, header_key, sizeof(hk) - 1); strncpy(hk, header_key, sizeof(hk) - 1);
hk[sizeof(hk) - 1] = '\0'; hk[sizeof(hk) - 1] = '\0';
@@ -133,7 +135,7 @@ static bool parse_headers(cws_request_s *req, char **cursor) {
return true; return true;
} }
cws_request_s *cws_http_parse(string_s *request_str) { cws_request_s *cws_request_parse(string_s *request_str) {
if (!request_str || !string_cstr(request_str)) { if (!request_str || !string_cstr(request_str)) {
return NULL; return NULL;
} }
@@ -145,7 +147,7 @@ cws_request_s *cws_http_parse(string_s *request_str) {
char *str = string_copy(request_str); char *str = string_copy(request_str);
if (!str) { if (!str) {
cws_http_free(request); cws_request_free(request);
return NULL; return NULL;
} }
char *orig = str; char *orig = str;
@@ -153,28 +155,28 @@ cws_request_s *cws_http_parse(string_s *request_str) {
/* Parse HTTP method */ /* Parse HTTP method */
if (!parse_method(request, &str)) { if (!parse_method(request, &str)) {
free(orig); free(orig);
cws_http_free(request); cws_request_free(request);
return NULL; return NULL;
} }
/* Parse location (URL path) */ /* Parse location (URL path) */
if (!parse_location(request, &str)) { if (!parse_location(request, &str)) {
free(orig); free(orig);
cws_http_free(request); cws_request_free(request);
return NULL; return NULL;
} }
/* Parse HTTP version */ /* Parse HTTP version */
if (!parse_version(request, &str)) { if (!parse_version(request, &str)) {
free(orig); free(orig);
cws_http_free(request); cws_request_free(request);
return NULL; return NULL;
} }
/* Parse headers */ /* Parse headers */
if (!parse_headers(request, &str)) { if (!parse_headers(request, &str)) {
free(orig); free(orig);
cws_http_free(request); cws_request_free(request);
return NULL; return NULL;
} }
@@ -183,16 +185,20 @@ cws_request_s *cws_http_parse(string_s *request_str) {
return request; return request;
} }
char *cws_http_get_host(cws_request_s *request) { char *cws_request_get_header(cws_request_s *request, const char *header) {
bucket_s *host = hm_get(request->headers, "Host"); if (!request || !header || !request->headers) {
if (!host) { return "";
return "default";
} }
return (char *)host->value; bucket_s *bucket = hm_get(request->headers, (void *)header);
if (!bucket) {
return "";
}
return (char *)bucket->value;
} }
void cws_http_free(cws_request_s *request) { void cws_request_free(cws_request_s *request) {
if (!request) { if (!request) {
return; return;
} }
+5 -9
View File
@@ -9,12 +9,11 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#define CHUNK_SIZE 8192 #include "internal/common.h"
#define HEADERS_BUFFER_SIZE 2048
static hashmap_s *response_headers_new(void) { static hashmap_s *response_headers_new(void) {
return hm_new(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn, sizeof(char) * 256, return hm_new(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn, sizeof(char) * HEADER_KEY_MAX,
sizeof(char) * 512); sizeof(char) * HEADER_VALUE_MAX);
} }
cws_response_s *cws_response_new(cws_http_status_e status) { cws_response_s *cws_response_new(cws_http_status_e status) {
@@ -30,9 +29,6 @@ cws_response_s *cws_response_new(cws_http_status_e status) {
resp->body_file = NULL; resp->body_file = NULL;
resp->content_length = 0; resp->content_length = 0;
/* TODO: get the value from connection */
cws_response_set_header(resp, "Connection", "close");
return resp; return resp;
} }
@@ -61,7 +57,7 @@ void cws_response_set_header(cws_response_s *response, const char *key, const ch
return; return;
} }
char k[256], v[512]; char k[HEADER_KEY_MAX], v[HEADER_VALUE_MAX];
strncpy(k, key, sizeof(k) - 1); strncpy(k, key, sizeof(k) - 1);
k[sizeof(k) - 1] = '\0'; k[sizeof(k) - 1] = '\0';
strncpy(v, value, sizeof(v) - 1); strncpy(v, value, sizeof(v) - 1);
@@ -101,7 +97,7 @@ void cws_response_set_body_file(cws_response_s *response, const char *filepath)
return; return;
} }
char content_type[64]; char content_type[CONTENT_TYPE_MAX];
cws_mime_get_ct(filepath, content_type); cws_mime_get_ct(filepath, content_type);
cws_response_set_header(response, "Content-Type", content_type); cws_response_set_header(response, "Content-Type", content_type);
+10
View File
@@ -0,0 +1,10 @@
#ifndef CWS_INTERNALS_COMMON_H
#define CWS_INTERNALS_COMMON_H
#define CONTENT_TYPE_MAX 64
#define HEADER_KEY_MAX 256
#define HEADER_VALUE_MAX 1024
#define CHUNK_SIZE 8192
#define HEADERS_BUFFER_SIZE 2048
#endif