add custom return codes

This commit is contained in:
2025-08-02 14:09:13 +02:00
parent 2918aeadcb
commit f0abac8be7
4 changed files with 112 additions and 89 deletions

View File

@@ -4,12 +4,9 @@
#include <stddef.h>
#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
#endif

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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) {