refactor(response): do not close connection if not requested

This commit is contained in:
2026-03-28 19:04:25 +01:00
parent cff0d58de4
commit d400722563
6 changed files with 67 additions and 25 deletions
-6
View File
@@ -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`):
+35 -12
View File
@@ -1,6 +1,7 @@
#include "core/server.h"
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
@@ -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);
}
+2
View File
@@ -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]);
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <sys/stat.h>
/* 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);
+28 -5
View File
@@ -3,6 +3,7 @@
#include "utils/debug.h"
#include "utils/hash.h"
#include <core/socket.h>
#include <myclib/myhashmap.h>
#include <myclib/mystring.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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");
+1 -1
View File
@@ -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);