From f0abac8be7d8be69e0feb6f8a0fb2d5a9f848f47 Mon Sep 17 00:00:00 2001 From: Francesco Date: Sat, 2 Aug 2025 14:09:13 +0200 Subject: [PATCH] add custom return codes --- include/http/http.h | 24 +++++-------- include/server/server.h | 34 +++++++++++++----- src/http/http.c | 76 ++++++++++++++++++++++++----------------- src/server/server.c | 67 +++++++++++++++++++----------------- 4 files changed, 112 insertions(+), 89 deletions(-) diff --git a/include/http/http.h b/include/http/http.h index 83c257b..4e3a3c4 100644 --- a/include/http/http.h +++ b/include/http/http.h @@ -4,12 +4,9 @@ #include #include "myclib/hashmap/myhashmap.h" +#include "myclib/string/mystring.h" #include "utils/config.h" -#define CWS_HTTP_LOCATION_LEN 512 -#define CWS_HTTP_LOCATION_PATH_LEN 1024 -#define CWS_HTTP_VERSION_LEN 8 - typedef enum cws_http_method_t { CWS_HTTP_GET, /**< GET method */ CWS_HTTP_POST, /**< POST method */ @@ -29,16 +26,13 @@ typedef enum cws_http_status_t { * */ typedef struct cws_http_t { - int sockfd; /**< Socket file descriptor */ - cws_http_method method; /**< HTTP request method */ - char location[CWS_HTTP_LOCATION_LEN]; /**< Resource requested */ - char location_path[CWS_HTTP_LOCATION_PATH_LEN]; /**< Full resource path */ - char http_version[CWS_HTTP_VERSION_LEN]; /**< HTTP version */ - mcl_hashmap *headers; /**< Headers hash map */ + int sockfd; /**< Socket file descriptor */ + cws_http_method method; /**< HTTP request method */ + mcl_string *location; /**< Resource requested */ + mcl_string *location_path; /**< Full resource path */ + mcl_string *http_version; /**< HTTP version */ + mcl_hashmap *headers; /**< Headers hash map */ } cws_http; -/* Connection */ -/* Accept-Encoding */ -/* Accept-Language */ /** * @brief Parses a HTTP request @@ -48,9 +42,7 @@ typedef struct cws_http_t { */ cws_http *cws_http_parse(char *request_str, int sockfd, cws_config *config); -int cws_http_parse_method(cws_http *request, const char *method); int cws_http_get_content_type(cws_http *request, char *content_type); -char *cws_http_status_string(cws_http_status status); /** * @brief Build the http response @@ -66,4 +58,4 @@ void cws_http_send_simple_html(cws_http *request, cws_http_status status, char * void cws_http_free(cws_http *request); -#endif \ No newline at end of file +#endif diff --git a/include/server/server.h b/include/server/server.h index b32b9de..d5a3bbb 100644 --- a/include/server/server.h +++ b/include/server/server.h @@ -22,6 +22,24 @@ /* Main server loop */ extern volatile sig_atomic_t cws_server_run; +typedef enum cws_server_ret_t { + CWS_SERVER_OK, + CWS_SERVER_FD_ERROR, + CWS_SERVER_CLIENT_NOT_FOUND, + CWS_SERVER_CLIENT_DISCONNECTED, + CWS_SERVER_CLIENT_DISCONNECTED_ERROR, + CWS_SERVER_HTTP_PARSE_ERROR, + CWS_SERVER_GETADDRINFO_ERROR, + CWS_SERVER_SOCKET_ERROR, + CWS_SERVER_SETSOCKOPT_ERROR, + CWS_SERVER_BIND_ERROR, + CWS_SERVER_LISTEN_ERROR, + CWS_SERVER_EPOLL_ADD_ERROR, + CWS_SERVER_EPOLL_DEL_ERROR, + CWS_SERVER_FD_NONBLOCKING_ERROR, + CWS_SERVER_ACCEPT_CLIENT_ERROR, +} cws_server_ret; + /** * @brief Setups hints object * @@ -35,16 +53,15 @@ void cws_server_setup_hints(struct addrinfo *hints, size_t len, const char *host * @brief Runs the server * * @param[in] config The server's config - * @return 0 on success, -1 on error */ -int cws_server_start(cws_config *config); +cws_server_ret cws_server_start(cws_config *config); /** * @brief Main server loop * * @param[in,out] sockfd Socket of the commincation endpoint */ -int cws_server_loop(int sockfd, cws_config *config); +cws_server_ret cws_server_loop(int sockfd, cws_config *config); /** * @brief Adds a file descriptor to the interest list @@ -53,7 +70,7 @@ int cws_server_loop(int sockfd, cws_config *config); * @param[in] sockfd The file descriptor to watch * @param[in] events The events to follow */ -int cws_epoll_add(int epfd, int sockfd, uint32_t events); +cws_server_ret cws_epoll_add(int epfd, int sockfd, uint32_t events); /** * @brief Removes a file descriptor from the interest list @@ -61,14 +78,14 @@ int cws_epoll_add(int epfd, int sockfd, uint32_t events); * @param[in] epfd epoll file descriptor * @param[in] sockfd The file descriptor to remove */ -int cws_epoll_del(int epfd, int sockfd); +cws_server_ret cws_epoll_del(int epfd, int sockfd); /** * @brief Makes a file descriptor non-blocking * * @param[in] sockfd The file descriptor to make non-blocking */ -int cws_fd_set_nonblocking(int sockfd); +cws_server_ret cws_fd_set_nonblocking(int sockfd); /** * @brief Handles the new client @@ -76,7 +93,6 @@ int cws_fd_set_nonblocking(int sockfd); * @param[in] sockfd Server's file descriptor * @param[out] their_sa Populates the struct with client's information * @param[in] theirsa_size Size of the struct - * @return Returns -1 on error or the file descriptor on success */ int cws_server_accept_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *theirsa_size); @@ -89,7 +105,7 @@ int cws_server_accept_client(int sockfd, struct sockaddr_storage *their_sa, sock */ void cws_server_close_client(int epfd, int client_fd, mcl_hashmap *hashmap); -int cws_server_handle_new_client(int sockfd, int epfd, mcl_hashmap *clients); -int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_config *config); +cws_server_ret cws_server_handle_new_client(int sockfd, int epfd, mcl_hashmap *clients); +cws_server_ret cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_config *config); #endif diff --git a/src/http/http.c b/src/http/http.c index cac5ab2..084b53c 100644 --- a/src/http/http.c +++ b/src/http/http.c @@ -8,12 +8,38 @@ #include "utils/colors.h" #include "utils/utils.h" +static void cws_http_init(cws_http **request) { + *request = calloc(1, sizeof(cws_http)); + if (!*request) { + return; + } + + (*request)->http_version = mcl_string_new("", 16); + (*request)->location = mcl_string_new("", 128); + (*request)->location_path = mcl_string_new("", 512); +} + +static int cws_http_parse_method(cws_http *request, const char *method) { + if (strcmp(method, "GET") == 0) { + request->method = CWS_HTTP_GET; + return 0; + } + + if (strcmp(method, "POST") == 0) { + request->method = CWS_HTTP_POST; + return 0; + } + + return -1; +} + cws_http *cws_http_parse(char *request_str, int sockfd, cws_config *config) { if (!request_str || !config) { return NULL; } - cws_http *request = calloc(1, sizeof(cws_http)); + cws_http *request; + cws_http_init(&request); if (request == NULL) { return NULL; } @@ -59,19 +85,16 @@ cws_http *cws_http_parse(char *request_str, int sockfd, cws_config *config) { return NULL; } CWS_LOG_DEBUG("location: %s", pch); - strncpy(request->location, pch, CWS_HTTP_LOCATION_LEN); + mcl_string_append(request->location, pch); + mcl_string_append(request->location_path, config->www); /* Adjust location path */ - if (strcmp(request->location, "/") == 0) { - snprintf(request->location_path, CWS_HTTP_LOCATION_PATH_LEN, "%s/index.html", config->www); + if (strcmp(mcl_string_cstr(request->location), "/") == 0) { + mcl_string_append(request->location_path, "/index.html"); } else { - size_t location_path_len = strlen(request->location); - if (location_path_len > CWS_HTTP_LOCATION_PATH_LEN) { - request->location[location_path_len - 1] = '\0'; - } - snprintf(request->location_path, CWS_HTTP_LOCATION_PATH_LEN, "%s%s", config->www, request->location); + mcl_string_append(request->location_path, mcl_string_cstr(request->location)); } - CWS_LOG_DEBUG("location path: %s", request->location_path); + CWS_LOG_DEBUG("location path: %s", mcl_string_cstr(request->location_path)); /* Parse HTTP version */ pch = strtok_r(NULL, " \r\n", &saveptr); @@ -82,7 +105,7 @@ cws_http *cws_http_parse(char *request_str, int sockfd, cws_config *config) { return NULL; } CWS_LOG_DEBUG("version: %s", pch); - strncpy(request->http_version, pch, CWS_HTTP_VERSION_LEN); + mcl_string_append(request->http_version, pch); /* Parse headers until a \r\n */ request->headers = mcl_hm_init(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn); @@ -116,21 +139,7 @@ cws_http *cws_http_parse(char *request_str, int sockfd, cws_config *config) { return request; } -int cws_http_parse_method(cws_http *request, const char *method) { - if (strcmp(method, "GET") == 0) { - request->method = CWS_HTTP_GET; - return 0; - } - - if (strcmp(method, "POST") == 0) { - request->method = CWS_HTTP_POST; - return 0; - } - - return -1; -} - -char *cws_http_status_string(cws_http_status status) { +static char *cws_http_status_string(cws_http_status status) { switch (status) { case CWS_HTTP_OK: { return "200 OK"; @@ -192,7 +201,9 @@ void cws_http_send_response(cws_http *request, cws_http_status status) { } int cws_http_send_resource(cws_http *request) { - FILE *file = fopen(request->location_path, "rb"); + int keepalive = 0; + + FILE *file = fopen(mcl_string_cstr(request->location_path), "rb"); if (file == NULL) { cws_http_send_response(request, CWS_HTTP_NOT_FOUND); return -1; @@ -228,10 +239,11 @@ int cws_http_send_resource(cws_http *request) { return -1; } - char conn[32] = "close"; + char conn[32]; mcl_bucket *connection = mcl_hm_get(request->headers, "Connection"); - if (connection) { - strncpy(conn, (char *)connection->value, sizeof(conn)); + if (connection && strcmp((char *)connection->value, "keep-alive") == 0) { + strcpy(conn, "keep-alive"); + keepalive = 1; } char *response = NULL; @@ -247,11 +259,11 @@ int cws_http_send_resource(cws_http *request) { free(response); free(file_data); - return 0; + return keepalive; } int cws_http_get_content_type(cws_http *request, char *content_type) { - char *ptr = strrchr(request->location_path, '.'); + char *ptr = strrchr(mcl_string_cstr(request->location_path), '.'); if (ptr == NULL) { return -1; } diff --git a/src/server/server.c b/src/server/server.c index e099703..d1dfc2a 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -34,7 +34,7 @@ void cws_server_setup_hints(struct addrinfo *hints, size_t len, const char *host } } -int cws_server_start(cws_config *config) { +cws_server_ret cws_server_start(cws_config *config) { struct addrinfo hints; struct addrinfo *res; @@ -43,44 +43,44 @@ int cws_server_start(cws_config *config) { int status = getaddrinfo(config->hostname, config->port, &hints, &res); if (status != 0) { CWS_LOG_ERROR("getaddrinfo() error: %s", gai_strerror(status)); - return -1; + return CWS_SERVER_GETADDRINFO_ERROR; } int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd < 0) { CWS_LOG_ERROR("socket(): %s", strerror(errno)); - return -1; + return CWS_SERVER_SOCKET_ERROR; } const int opt = 1; status = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); if (status != 0) { CWS_LOG_ERROR("setsockopt(): %s", strerror(errno)); - return -1; + return CWS_SERVER_SETSOCKOPT_ERROR; } status = bind(sockfd, res->ai_addr, res->ai_addrlen); if (status != 0) { CWS_LOG_ERROR("bind(): %s", strerror(errno)); - return -1; + return CWS_SERVER_BIND_ERROR; } status = listen(sockfd, CWS_SERVER_BACKLOG); if (status != 0) { CWS_LOG_ERROR("listen(): %s", strerror(errno)); - return -1; + return CWS_SERVER_LISTEN_ERROR; } - int ret = cws_server_loop(sockfd, config); + cws_server_ret ret = cws_server_loop(sockfd, config); CWS_LOG_DEBUG("cws_server_loop ret: %d", ret); freeaddrinfo(res); close(sockfd); - return 0; + return CWS_SERVER_OK; } -int cws_server_loop(int sockfd, cws_config *config) { +cws_server_ret cws_server_loop(int sockfd, cws_config *config) { mcl_hashmap *clients = mcl_hm_init(my_int_hash_fn, my_int_equal_fn, my_int_free_key_fn, my_str_free_fn); if (!clients) { return -1; @@ -119,13 +119,13 @@ int cws_server_loop(int sockfd, cws_config *config) { for (int i = 0; i < nfds; ++i) { if (revents[i].data.fd == sockfd) { ret = cws_server_handle_new_client(sockfd, epfd, clients); - if (ret < 0) { - CWS_LOG_DEBUG("handle new client error"); + if (ret != CWS_SERVER_OK) { + CWS_LOG_DEBUG("%d", ret); } } else { ret = cws_server_handle_client_data(revents[i].data.fd, epfd, clients, config); - if (ret < 0) { - CWS_LOG_DEBUG("handle client data error"); + if (ret != CWS_SERVER_OK) { + CWS_LOG_DEBUG("%d", ret); } } } @@ -139,14 +139,14 @@ int cws_server_loop(int sockfd, cws_config *config) { return 0; } -int cws_server_handle_new_client(int sockfd, int epfd, mcl_hashmap *clients) { +cws_server_ret cws_server_handle_new_client(int sockfd, int epfd, mcl_hashmap *clients) { struct sockaddr_storage their_sa; socklen_t theirsa_size = sizeof their_sa; char ip[INET_ADDRSTRLEN]; int client_fd = cws_server_accept_client(sockfd, &their_sa, &theirsa_size); if (client_fd < 0) { - return -1; + return CWS_SERVER_FD_ERROR; } cws_utils_get_client_ip(&their_sa, ip); @@ -162,10 +162,10 @@ int cws_server_handle_new_client(int sockfd, int epfd, mcl_hashmap *clients) { mcl_hm_set(clients, key, value); - return 0; + return CWS_SERVER_OK; } -int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_config *config) { +cws_server_ret cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_config *config) { char data[4096] = {0}; char ip[INET_ADDRSTRLEN] = {0}; @@ -183,7 +183,7 @@ int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_epoll_del(epfd, client_fd); close(client_fd); - return -1; + return CWS_SERVER_CLIENT_NOT_FOUND; } struct sockaddr_storage client_sas = *(struct sockaddr_storage *)client->value; @@ -194,7 +194,7 @@ int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, CWS_LOG_INFO("Client (%s) disconnected", ip); cws_server_close_client(epfd, client_fd, clients); - return -1; + return CWS_SERVER_CLIENT_DISCONNECTED; } if (bytes_read < 0) { @@ -204,7 +204,7 @@ int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, cws_server_close_client(epfd, client_fd, clients); } - return -1; + return CWS_SERVER_CLIENT_DISCONNECTED_ERROR; } /* Parse HTTP request */ @@ -214,16 +214,19 @@ int cws_server_handle_client_data(int client_fd, int epfd, mcl_hashmap *clients, CWS_LOG_INFO("Client (%s) disconnected (request NULL)", ip); cws_server_close_client(epfd, client_fd, clients); - return -1; + return CWS_SERVER_HTTP_PARSE_ERROR; } - cws_http_send_resource(request); + int keepalive = cws_http_send_resource(request); cws_http_free(request); + if (!keepalive) { + cws_server_close_client(epfd, client_fd, clients); + } - return 0; + return CWS_SERVER_OK; } -int cws_epoll_add(int epfd, int sockfd, uint32_t events) { +cws_server_ret cws_epoll_add(int epfd, int sockfd, uint32_t events) { struct epoll_event event; event.events = events; event.data.fd = sockfd; @@ -231,32 +234,32 @@ int cws_epoll_add(int epfd, int sockfd, uint32_t events) { if (status != 0) { CWS_LOG_ERROR("epoll_ctl_add(): %s", strerror(errno)); - return -1; + return CWS_SERVER_EPOLL_ADD_ERROR; } - return 0; + return CWS_SERVER_OK; } -int cws_epoll_del(int epfd, int sockfd) { +cws_server_ret cws_epoll_del(int epfd, int sockfd) { const int status = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL); if (status != 0) { CWS_LOG_ERROR("epoll_ctl_del(): %s", strerror(errno)); - return -1; + return CWS_SERVER_EPOLL_DEL_ERROR; } - return 0; + return CWS_SERVER_OK; } -int cws_fd_set_nonblocking(int sockfd) { +cws_server_ret cws_fd_set_nonblocking(int sockfd) { const int status = fcntl(sockfd, F_SETFL, O_NONBLOCK); if (status == -1) { CWS_LOG_ERROR("fcntl(): %s", strerror(errno)); - return -1; + return CWS_SERVER_FD_NONBLOCKING_ERROR; } - return 0; + return CWS_SERVER_OK; } int cws_server_accept_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *theirsa_size) {