From d400722563f8d5c2e70d9e1d7dea85c3c645844d Mon Sep 17 00:00:00 2001 From: Francesco Date: Sat, 28 Mar 2026 19:04:25 +0100 Subject: [PATCH] refactor(response): do not close connection if not requested --- README.md | 6 ------ src/core/server.c | 47 +++++++++++++++++++++++++++++++++------------ src/core/worker.c | 2 ++ src/http/handler.c | 2 +- src/http/response.c | 33 ++++++++++++++++++++++++++----- src/main.c | 2 +- 6 files changed, 67 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f7df132..d3a4156 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,6 @@ meson compile -C build 2. Run `./build/cws` 3. Open `http://localhost:3030` in your browser -## Roadmap - -- [ ] Virtual hosts support -- [ ] Minimal templating engine -- [ ] IPv6 compatibility - ## Performance Tested with [goku](https://github.com/jcaromiq/goku) (`-c 400 -d 30`): diff --git a/src/core/server.c b/src/core/server.c index b5c8de9..0c83a22 100644 --- a/src/core/server.c +++ b/src/core/server.c @@ -1,6 +1,7 @@ #include "core/server.h" #include +#include #include #include #include @@ -45,56 +46,74 @@ cws_return cws_server_setup(cws_server_s *server, cws_config_s *config) { return CWS_CONFIG_ERROR; } - memset(server, 0, sizeof *server); + cws_return returncode = CWS_OK; - struct addrinfo hints; - struct addrinfo *res; + struct addrinfo hints = {0}; + struct addrinfo *res = {0}; cws_server_setup_hints(&hints, config->host); int status = getaddrinfo(config->host, config->port, &hints, &res); if (status != 0) { cws_log_error("getaddrinfo() error: %s", gai_strerror(status)); - return CWS_GETADDRINFO_ERROR; + returncode = CWS_GETADDRINFO_ERROR; + goto cleanup; } server->sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (server->sockfd < 0) { cws_log_error("socket(): %s", strerror(errno)); - return CWS_SOCKET_ERROR; + returncode = CWS_SOCKET_ERROR; + goto cleanup; } const int opt = 1; status = setsockopt(server->sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); if (status != 0) { cws_log_error("setsockopt(): %s", strerror(errno)); - return CWS_SETSOCKOPT_ERROR; + returncode = CWS_SETSOCKOPT_ERROR; + goto cleanup; } status = bind(server->sockfd, res->ai_addr, res->ai_addrlen); if (status != 0) { cws_log_error("bind(): %s", strerror(errno)); - return CWS_BIND_ERROR; + returncode = CWS_BIND_ERROR; + goto cleanup; } status = listen(server->sockfd, CWS_SERVER_BACKLOG); if (status != 0) { cws_log_error("listen(): %s", strerror(errno)); - return CWS_LISTEN_ERROR; + returncode = CWS_LISTEN_ERROR; + goto cleanup; } freeaddrinfo(res); cws_return ret = cws_server_setup_epoll(server->sockfd, &server->epfd); if (ret != CWS_OK) { - return ret; + returncode = ret; + goto cleanup; } server->workers = cws_worker_new(config->workers, config); if (server->workers == NULL) { - return CWS_WORKER_ERROR; + returncode = CWS_WORKER_ERROR; + goto cleanup; } return CWS_OK; + +cleanup: + if (res) { + freeaddrinfo(res); + } + + if (server->sockfd >= 0) { + close(server->sockfd); + } + + return returncode; } cws_return cws_server_start(cws_server_s *server) { @@ -115,6 +134,10 @@ cws_return cws_server_start(cws_server_s *server) { } for (int i = 0; i < nfds; ++i) { + if (events[i].data.fd != server->sockfd) { + continue; + } + int client_fd = cws_server_handle_new_client(server->sockfd); if (client_fd < 0) { continue; @@ -163,11 +186,11 @@ void cws_server_shutdown(cws_server_s *server) { return; } - if (server->sockfd > 0) { + if (server->sockfd >= 0) { close(server->sockfd); } - if (server->epfd > 0) { + if (server->epfd >= 0) { close(server->epfd); } diff --git a/src/core/worker.c b/src/core/worker.c index 5e01574..3920428 100644 --- a/src/core/worker.c +++ b/src/core/worker.c @@ -151,6 +151,7 @@ cws_worker_s **cws_worker_new(size_t workers_num, cws_config_s *config) { /* Create per-worker epoll instance */ if (worker_setup_epoll(workers[i]) != CWS_OK) { for (size_t j = 0; j < i; ++j) { + close(workers[j]->epfd); free(workers[j]); } free(workers); @@ -174,6 +175,7 @@ void cws_worker_free(cws_worker_s **workers, size_t workers_num) { for (size_t i = 0; i < workers_num; ++i) { pthread_join(workers[i]->thread, NULL); + close(workers[i]->epfd); free(workers[i]); } diff --git a/src/http/handler.c b/src/http/handler.c index 77323da..755571f 100644 --- a/src/http/handler.c +++ b/src/http/handler.c @@ -5,7 +5,7 @@ #include /* Sanitize and resolve file path */ -/* TODO: fix path traversal */ +/* @TODO: fix path traversal */ static string_s *resolve_file_path(const char *url_path, cws_handler_config_s *config) { string_s *full_path = string_new(config->root, 256); diff --git a/src/http/response.c b/src/http/response.c index 10dd155..c158224 100644 --- a/src/http/response.c +++ b/src/http/response.c @@ -3,6 +3,7 @@ #include "utils/debug.h" #include "utils/hash.h" #include +#include #include #include #include @@ -16,6 +17,26 @@ static hashmap_s *response_headers_new(void) { sizeof(char) * HEADER_VALUE_MAX); } +static int response_get_headers(hashmap_s *headers, char *out_headers, size_t len) { + size_t keys_len = 0; + char **keys = (char **)hm_get_keys(headers, &keys_len); + if (!keys) { + cws_log_debug("no headers??"); + return -1; + } + + size_t offset = 0; + for (size_t i = 0; i < keys_len; ++i) { + bucket_s *bucket = hm_get(headers, keys[i]); + offset += snprintf(out_headers, *out_headers + offset, "%s: %s", keys[i], (char *)bucket->value); + if ((size_t)(headers + offset) >= len) { + return -1; + } + } + + return 0; +} + cws_response_s *cws_response_new(cws_http_status_e status) { cws_response_s *resp = malloc(sizeof(*resp)); if (!resp) { @@ -137,16 +158,18 @@ int cws_response_send(int sockfd, cws_response_s *response) { } char headers[HEADERS_BUFFER_SIZE]; + + response_get_headers(response->headers, headers, sizeof headers); int offset = snprintf(headers, sizeof(headers), "HTTP/1.1 %s\r\n", cws_http_status_string(response->status)); char content_length_str[32]; snprintf(content_length_str, sizeof(content_length_str), "%zu", response->content_length); - cws_response_set_header(response, "Content-Length", content_length_str); - offset += snprintf(headers + offset, sizeof(headers) - offset, - "Content-Length: %zu\r\n" - "Connection: close\r\n", - response->content_length); + /* @TODO: I can do this in the response_get_header() but I need to check + * if I have space left for \r\n + */ + cws_response_set_header(response, "Content-Length", content_length_str); + offset += snprintf(headers + offset, sizeof(headers) - offset, "Content-Length: %zu\r\n", response->content_length); offset += snprintf(headers + offset, sizeof(headers) - offset, "\r\n"); diff --git a/src/main.c b/src/main.c index 7f74895..2cae78f 100644 --- a/src/main.c +++ b/src/main.c @@ -32,7 +32,7 @@ int main(void) { return EXIT_FAILURE; } - cws_server_s server; + cws_server_s server = {0}; cws_return ret; ret = cws_server_setup(&server, config);