From ad21ec0fa4f928fe6ea3a4209c405826b9f05f52 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 30 Apr 2025 02:49:18 +0200 Subject: [PATCH] add header parser and fix hash map --- README.md | 1 - include/http/http.h | 7 +++---- include/utils/utils.h | 8 +++++--- src/http/http.c | 48 ++++++++++++++++++++++++++++++++++--------- src/server/server.c | 2 +- src/utils/hashmap.c | 2 +- src/utils/utils.c | 21 ++++++++++++++++--- 7 files changed, 66 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 99d10f2..6f3ffb7 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ And then open the `docs/html/index.html`. ## Roadmap -- Request parser - Implement Keep-Alive - Support for virtual hosting - HTTPS support with TLS diff --git a/include/http/http.h b/include/http/http.h index de7adfa..0a83c1e 100644 --- a/include/http/http.h +++ b/include/http/http.h @@ -1,13 +1,13 @@ #ifndef CWS_HTTP_H #define CWS_HTTP_H +#include "utils/hashmap.h" + #define CWS_WWW "../www" /**< Directory used to get html files */ /** In the future I'll move conf stuff under a server struct, I can skip just because I want something that works */ #define CWS_HTTP_LOCATION_LEN 512 #define CWS_HTTP_LOCATION_PATH_LEN 1024 #define CWS_HTTP_VERSION_LEN 8 -#define CWS_HTTP_USER_AGENT_LEN 1024 -#define CWS_HTTP_HOST_LEN 1024 typedef enum cws_http_method_t { CWS_HTTP_GET, /**< GET method */ @@ -27,8 +27,7 @@ typedef struct cws_http_t { 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 */ - char user_agent[CWS_HTTP_USER_AGENT_LEN]; /**< User-Agent */ - char host[CWS_HTTP_HOST_LEN]; /**< Host */ + cws_hashmap *headers; /**< Headers hash map */ } cws_http; /* Connection */ /* Accept-Encoding */ diff --git a/include/utils/utils.h b/include/utils/utils.h index d468076..451773e 100644 --- a/include/utils/utils.h +++ b/include/utils/utils.h @@ -25,9 +25,11 @@ void cws_utils_print_ips(const char *hostname, const char *port); void cws_utils_get_client_ip(struct sockaddr_storage *sa, char *ip); /* TODO: add docs */ +char *cws_strip(char *str); + /* Functions used for hash maps */ -int my_hash_fn(void *key); -bool my_equal_fn(void *a, void *b); -void my_free_value_fn(void *value); +int my_str_hash_fn(void *key); +bool my_str_equal_fn(void *a, void *b); +void my_str_free_fn(void *value); #endif diff --git a/src/http/http.c b/src/http/http.c index e96d292..4f41de2 100644 --- a/src/http/http.c +++ b/src/http/http.c @@ -6,6 +6,7 @@ #include #include "utils/colors.h" +#include "utils/utils.h" cws_http *cws_http_parse(char *request_str, int sockfd) { cws_http *request = malloc(sizeof(cws_http)); @@ -22,7 +23,7 @@ cws_http *cws_http_parse(char *request_str, int sockfd) { if (pch == NULL) { return NULL; } - CWS_LOG_DEBUG("[client::http] method: %s", pch); + CWS_LOG_DEBUG("[http] method: %s", pch); cws_http_parse_method(request, pch); /* Parse location */ @@ -30,29 +31,53 @@ cws_http *cws_http_parse(char *request_str, int sockfd) { if (pch == NULL) { return NULL; } - CWS_LOG_DEBUG("[client::http] location: %s", pch); + CWS_LOG_DEBUG("[http] location: %s", pch); strncpy(request->location, pch, CWS_HTTP_LOCATION_LEN); - /* Parse location path */ + /* Adjust location path */ if (strcmp(request->location, "/") == 0) { snprintf(request->location_path, CWS_HTTP_LOCATION_PATH_LEN, "%s/index.html", CWS_WWW); } else { snprintf(request->location_path, CWS_HTTP_LOCATION_PATH_LEN, "%s%s", CWS_WWW, request->location); } - CWS_LOG_DEBUG("[client::http] location path: %s", request->location_path); + CWS_LOG_DEBUG("[http] location path: %s", request->location_path); /* Parse HTTP version */ pch = strtok(NULL, " \r\n"); if (pch == NULL) { return NULL; } - CWS_LOG_DEBUG("[client::http] version: %s", pch); + CWS_LOG_DEBUG("[http] version: %s", pch); strncpy(request->http_version, pch, CWS_HTTP_VERSION_LEN); - /* Parse other stuff... */ - /* Parse until a \r\n and store the header with its value - * into a hashmap - */ + /* Parse headers until a \r\n */ + request->headers = cws_hm_init(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn); + char *header_colon; + while (pch) { + /* Get header line */ + pch = strtok(NULL, "\r\n"); + if (pch == NULL) { + break; + } + /* Find ":" */ + header_colon = strchr(pch, ':'); + if (header_colon != NULL) { + *header_colon = '\0'; + } else { + break; + } + + /* Header key */ + char *hkey = pch; + char *hkey_dup = strdup(hkey); + /* Header value (starting from ": ") */ + char *hvalue = header_colon + 2; + char *hvalue_dup = strdup(hvalue); + + cws_hm_set(request->headers, hkey_dup, hvalue_dup); + } + + /* Parse body */ return request; } @@ -178,4 +203,7 @@ void cws_http_send_not_found(cws_http *request) { send(request->sockfd, response, response_len, 0); } -void cws_http_free(cws_http *request) { free(request); } +void cws_http_free(cws_http *request) { + cws_hm_free(request->headers); + free(request); +} diff --git a/src/server/server.c b/src/server/server.c index 6a0ad95..55409d0 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -78,7 +78,7 @@ void cws_server_loop(int sockfd) { struct sockaddr_storage their_sa; socklen_t theirsa_size = sizeof their_sa; - cws_hashmap *clients = cws_hm_init(my_hash_fn, my_equal_fn, NULL, NULL); + cws_hashmap *clients = cws_hm_init(my_str_hash_fn, my_str_equal_fn, NULL, NULL); int epfd = epoll_create1(0); cws_fd_set_nonblocking(sockfd); diff --git a/src/utils/hashmap.c b/src/utils/hashmap.c index b36ca0d..61ffcab 100644 --- a/src/utils/hashmap.c +++ b/src/utils/hashmap.c @@ -58,7 +58,7 @@ void cws_hm_free(cws_hashmap *hashmap) { bool cws_hm_set(cws_hashmap *hashmap, void *key, void *value) { /* Get hash index */ - int index = hashmap->hash_fn(key); + int index = hashmap->hash_fn(key) % CWS_HASHMAP_SIZE; cws_bucket *bucket = &hashmap->map[index]; /* Check if the key at index is empty */ diff --git a/src/utils/utils.c b/src/utils/utils.c index ea3c468..f3371d4 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -1,5 +1,6 @@ #include "utils/utils.h" +#include #include #include #include @@ -45,7 +46,21 @@ void cws_utils_get_client_ip(struct sockaddr_storage *sa, char *ip) { inet_ntop(AF_INET, &sin->sin_addr, ip, INET_ADDRSTRLEN); } -int my_hash_fn(void *key) { +char *cws_strip(char *str) { + char *end; + + while (isspace((int)*str)) str++; + + if (*str == 0) return str; + + end = str + strlen(str) - 1; + while (end > str && isspace((int)*end)) end--; + *(end + 1) = '\0'; + + return str; +} + +int my_str_hash_fn(void *key) { char *key_str = (char *)key; size_t key_len = strlen(key_str); @@ -58,7 +73,7 @@ int my_hash_fn(void *key) { return total % 2069; } -bool my_equal_fn(void *a, void *b) { +bool my_str_equal_fn(void *a, void *b) { if (strcmp((char *)a, (char *)b) == 0) { return true; } @@ -66,4 +81,4 @@ bool my_equal_fn(void *a, void *b) { return false; } -void my_free_str_fn(void *value) { free(value); } +void my_str_free_fn(void *value) { free(value); }