diff --git a/Makefile b/Makefile deleted file mode 100644 index 0682316..0000000 --- a/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -CC = gcc -CFLAGS = -std=c99 -Werror -Wall -Wextra -Wvla -pedantic -LDFLAGS = -LDLIBS = - -CFLAGS_DBG = -g -ASAN_DBG_FLAGS = -fsanitize=address - -UTILS_SRCS = src/utils/string/string.c \ - src/utils/time/fmt_time.c \ - src/utils/parsing/words.c \ - src/utils/parsing/blanks.c \ - src/utils/files/files.c -MODULES_SRCS = src/config/config.c \ - src/server/server.c \ - src/http/http.c \ - src/http/headers.c \ - src/logger/logs.c \ - src/logger/errors.c \ - src/daemon/daemon.c -SRCS = $(UTILS_SRCS) \ - $(MODULES_SRCS) \ - src/main.c - -UTILS_OBJS = ${UTILS_SRCS:.c=.o} -MODULES_OBJS = ${MODULES_SRCS:.c=.o} -OBJS = ${SRCS:.c=.o} - -TARGET=httpd - -$(TARGET): $(OBJS) - $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) - -check: $(TARGET) - cp $(TARGET) tests/$(TARGET) - cd tests - python3 -m venv env - env/bin/python -m pip install requests - env/bin/python -m pip install pytest - env/bin/python -m pip install pytest-timeout - - env/bin/pytest - -debug: CFLAGS += $(CFLAGS_DBG) -debug: $(OBJS) - $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LDLIBS) - - -asan: CFLAGS += $(CFLAGS_DBG) -asan: LDFLAGS += $(ASAN_DBG_FLAGS) -asan: $(OBJS) - $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LDLIBS) - -clean: - - pkill -9 $(TARGET) - $(RM) tests/$(TARGET) tests/out.log - $(RM) $(TARGET) - $(RM) $(OBJS) diff --git a/.gitignore b/httpd/.gitignore similarity index 70% rename from .gitignore rename to httpd/.gitignore index 85e1c00..a20dfd8 100644 --- a/.gitignore +++ b/httpd/.gitignore @@ -6,5 +6,3 @@ *.log *.core httpd -__pycache__ -env/ diff --git a/httpd/Makefile b/httpd/Makefile new file mode 100644 index 0000000..204e10d --- /dev/null +++ b/httpd/Makefile @@ -0,0 +1,37 @@ +CC = gcc +CFLAGS = -std=c99 -Werror -Wall -Wextra -Wvla -pedantic +LDFLAGS = +LDLIBS = + +CFLAGS_DBG = -g +ASAN_DBG_FLAGS = -fsanitize=address + +UTILS_SRCS = src/utils/string/string.c +CONFIG_SRCS = src/config/config.c +SRCS = $(UTILS_SRCS) $(CONFIG_SRCS) src/main.c + +UTILS_OBJS = ${UTILS_SRCS:.c=.o} +CONFIG_OBJS = ${CONFIG_SRCS:.c=.o} +OBJS = ${SRCS:.c=.o} + +TARGET=httpd + +$(TARGET): $(OBJS) + $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) + +check: + dash tests/run.sh + +debug: CFLAGS += $(CFLAGS_DBG) +debug: $(OBJS) + $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LDLIBS) + + +asan: CFLAGS += $(CFLAGS_DBG) +asan: LDFLAGS += $(ASAN_DBG_FLAGS) +asan: $(OBJS) + $(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LDLIBS) + +clean: + $(RM) $(TARGET) + $(RM) $(OBJS) diff --git a/tests/config.txt b/httpd/config.txt similarity index 90% rename from tests/config.txt rename to httpd/config.txt index 77d4974..239d20d 100644 --- a/tests/config.txt +++ b/httpd/config.txt @@ -1,7 +1,6 @@ [global] log = true pid_file = /tmp/HTTPd.pid -daemon = start [[vhosts]] server_name = my_server diff --git a/tests/config_reader.sh b/httpd/config_reader.sh similarity index 100% rename from tests/config_reader.sh rename to httpd/config_reader.sh diff --git a/src/config/config.c b/httpd/src/config/config.c similarity index 87% rename from src/config/config.c rename to httpd/src/config/config.c index c8edde6..c7f8153 100644 --- a/src/config/config.c +++ b/httpd/src/config/config.c @@ -5,8 +5,8 @@ #include #include -// #include "../utils/string/string.h" -// #include "bits/getopt_ext.h" +#include "../utils/string/string.h" +#include "bits/getopt_ext.h" #define ARG_VALID 0 #define ARG_INVALID 1 @@ -50,10 +50,6 @@ static void print_help(char *program_name) // limit imposed by school static int parse_daemon_arg(struct config *cfg) { - // Multiple contradictory parameters - if (cfg->daemon != NO_OPTION) - return ARG_INVALID; - if (strcmp(optarg, "start") == 0) cfg->daemon = START; else if (strcmp(optarg, "stop") == 0) @@ -101,7 +97,7 @@ static int handle_opt(char **argv, char opt, struct config *cfg) // Server name case 's': - cfg->servers->server_name = optarg; + cfg->servers->server_name = string_create(optarg, strlen(optarg)); break; // Port @@ -180,19 +176,6 @@ static void print_arg_error(int err, char **argv, struct option options[], } } -// static void apply_default_values(struct config *cfg) -// { -// // Default file -// if (cfg->servers->default_file == NULL) -// { -// char *default_df = DEFAULT_DF; -// cfg->servers->default_file = -// malloc((strlen(default_df) + 1) * sizeof(char)); -// // TODO handle error -// strcpy(cfg->servers->default_file, default_df); -// } -// } - // == Main functions struct config *parse_configuration(int argc, char *argv[]) @@ -240,12 +223,10 @@ struct config *parse_configuration(int argc, char *argv[]) } } - // apply_default_values(config); - // Check config validity if (check_config(config) != 0) { - printf("%s: Missing mandatory flags, cannot continue.\n", argv[0]); + printf("%s: Missing mandatory flags, cannot continue.", argv[0]); config_destroy(config); return NULL; } @@ -255,6 +236,7 @@ struct config *parse_configuration(int argc, char *argv[]) void config_destroy(struct config *config) { + string_destroy(config->servers->server_name); free(config->servers); free(config); } diff --git a/src/config/config.h b/httpd/src/config/config.h similarity index 91% rename from src/config/config.h rename to httpd/src/config/config.h index 72be5a8..aa4c318 100644 --- a/src/config/config.h +++ b/httpd/src/config/config.h @@ -2,13 +2,9 @@ #define CONFIG_H #define _XOPEN_SOURCE 500 -#define HTTP_VERSION "HTTP/1.1" #include -// Default values -#define DEFAULT_DF "index.html" - /* ** @brief Enum daemon ** NO_OPTION if the '--daemon' option is not given @@ -36,7 +32,6 @@ struct config char *pid_file; char *log_file; bool log; - char *protocol_version; struct server_config *servers; enum daemon daemon; @@ -53,7 +48,7 @@ struct config */ struct server_config { - char *server_name; + struct string *server_name; char *port; char *ip; char *root_dir; diff --git a/httpd/src/main.c b/httpd/src/main.c new file mode 100644 index 0000000..d86a4b8 --- /dev/null +++ b/httpd/src/main.c @@ -0,0 +1,10 @@ +#include +#include + +#include "config/config.h" + +int main(int argc, char **argv) +{ + parse_configuration(argc, argv); + return 0; +} diff --git a/httpd/src/utils/string/string.c b/httpd/src/utils/string/string.c new file mode 100644 index 0000000..68914b5 --- /dev/null +++ b/httpd/src/utils/string/string.c @@ -0,0 +1,77 @@ +#include "string.h" + +#include +#include + +struct string *string_create(const char *str, size_t size) +{ + struct string *res = malloc(sizeof(struct string)); + if (res == NULL) + return NULL; + + res->data = malloc(size * sizeof(char)); + if (res->data == NULL) + { + free(res); + return NULL; + } + + memcpy(res->data, str, size); + res->size = size; + return res; +} + +int string_compare_n_str(const struct string *str1, const char *str2, size_t n) +{ + size_t i = 0; + int res = 0; + char reached_str2_end = 0; + while (i < n) + { + if (i < str1->size) + res += str1->data[i]; + + if (!reached_str2_end) + { + if (str2[i] == '\0') + reached_str2_end = 1; + else + res -= str2[i]; + } + + i++; + } + + return res; +} + +void string_concat_str(struct string *str, const char *to_concat, size_t size) +{ + size_t new_size = str->size + size; + size_t str_size = str->size; + + if (new_size == 0) + return; + + str->size = new_size; + str->data = realloc(str->data, new_size); + if (str->data == NULL) + return; // Handle ? + + for (size_t i = 0; i < size; i++) + { + str->data[str_size + i] = to_concat[i]; + } +} + +void string_destroy(struct string *str) +{ + if (str != NULL) + { + if (str->data != NULL) + { + free(str->data); + } + free(str); + } +} diff --git a/src/utils/string/string.h b/httpd/src/utils/string/string.h similarity index 52% rename from src/utils/string/string.h rename to httpd/src/utils/string/string.h index a69f47c..afe6d9d 100644 --- a/src/utils/string/string.h +++ b/httpd/src/utils/string/string.h @@ -42,48 +42,6 @@ int string_compare_n_str(const struct string *str1, const char *str2, size_t n); */ void string_concat_str(struct string *str, const char *to_concat, size_t size); -/* - ** @brief Similar to string_concat_str but with str at the beginning of the - * result string - ** - ** @param str - ** @param to_concat - ** @param size - */ -void str_concat_string(const char *str, size_t size, struct string *to_concat); - -/* - ** @brief Concat a char * with its size in a struct string - ** - ** @param str - */ -void string_to_lowercase(struct string *str); - -/* - ** @brief Free all string content - ** - ** @param str - */ void string_destroy(struct string *str); -/* - ** @brief Converts the string to the native C implementation - ** - ** @param str - ** - ** @return a pointer to an allocated memory zone containing the string - */ -char *string_to_charptr(struct string *str); - -/* - ** @brief TODO - ** - ** @param str1 - ** @param str2 - ** @param n - ** - ** @return - */ -int string_compare_strictly_n_str(const struct string *str1, const char *str2, - size_t n); #endif /* ! STRING_H */ diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c deleted file mode 100644 index a918966..0000000 --- a/src/daemon/daemon.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "daemon.h" - -#include -#include -#include -#include - -#include "../server/server.h" -#include "../utils/files/files.h" - -static struct config *config; - -// === Functions - -void daemon_init(struct config *cfg) -{ - config = cfg; -} - -int get_pid(void) -{ - FILE *stream = fopen(config->pid_file, "r"); - if (stream == NULL) - return -2; - - char buf[10]; - size_t nread = fread(buf, sizeof(char), 10, stream); - if (nread > 8) // PID is max 8 chars - return -3; - buf[nread] = '\0'; - - int res = atoi(buf); - - fclose(stream); - - return res; -} - -void stop_daemon(void) -{ - int pid = get_pid(); - if (pid > 0) - kill(pid, SIGINT); -} - -int start_daemon(void) -{ - pid_t pid = fork(); - if (!pid) // Daemon - { - start_server(config); - } - else // Parent - { - // Write pid - int err = write_pid(config->pid_file, pid); - if (err != 0) - { - kill(pid, SIGINT); - return 1; - } - } - return 0; -} - -int restart_daemon(void) -{ - // Attempt to kill process - stop_daemon(); - - // Start again - return start_daemon(); -} diff --git a/src/daemon/daemon.h b/src/daemon/daemon.h deleted file mode 100644 index f1dcae8..0000000 --- a/src/daemon/daemon.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef DAEMON_H -#define DAEMON_H - -#include "../config/config.h" - -/* @brief - * - * @return - */ -int get_pid(void); - -/* @brief - */ -void daemon_init(struct config *cfg); - -/* @brief - */ -void stop_daemon(void); - -/* @brief - */ -int start_daemon(void); - -/* @brief - * - * @return - */ -int restart_daemon(void); - -#endif // ! DAEMON_H diff --git a/src/http/headers.c b/src/http/headers.c deleted file mode 100644 index ce7806c..0000000 --- a/src/http/headers.c +++ /dev/null @@ -1,162 +0,0 @@ -#include "headers.h" - -#include -#include -#include - -#include "../utils/parsing/words.h" -#include "../utils/string/string.h" - -void destroy_headers(struct http_header *headers) -{ - while (headers != NULL) - { - struct http_header *next = headers->next; - string_destroy(headers->field); - string_destroy(headers->value); - free(headers); - headers = next; - } -} - -ssize_t read_field(struct string *str, size_t offset, struct string **res) -{ - ssize_t nread = read_word_delim(str, offset, res, ":\n"); - if (nread <= 0) - return ERR_HTTP_INVALID_INPUT; - - if (str->size <= offset + nread || str->data[offset + nread] != ':') - return ERR_HTTP_INVALID_INPUT; - - string_to_lowercase(*res); - - return nread; -} - -ssize_t read_value(struct string *str, size_t offset, struct string **res) -{ - ssize_t nread = read_word_delim(str, offset, res, "\n"); - if (nread <= 0) - return ERR_HTTP_INVALID_INPUT; - - if (str->size <= offset + nread || str->data[offset + nread] != '\n') - return ERR_HTTP_INVALID_INPUT; - - // Trim trailing \r - if ((*res)->size > 0 && (*res)->data[(*res)->size - 1] == '\r') - { - (*res)->size--; - } - - return nread; -} - -ssize_t parse_headers(struct http_request *res, struct string *req, - size_t offset) -{ - size_t i = offset; - struct http_header *header = NULL; - - // Yes I know I do one useless allocation but I really don't care at this - // point - while (i < req->size && req->data[i] != '\n' - && req->data[i] != '\r') // ! Blank line - { - if (header == NULL) - { - // Init list - header = calloc(1, sizeof(struct http_header)); - res->headers = header; - } - else - { - // Append - header->next = calloc(1, sizeof(struct http_header)); - header = header->next; - } - - // Check allocation - if (header == NULL) - return ERR_HTTP_OUT_OF_MEMORY; - - // Read field - ssize_t nread = read_field(req, i, &header->field); - if (nread <= 0) - return nread; // Contains error code when negative - - i += nread + 1; - - // Read value - nread = read_value(req, i, &header->value); - if (nread <= 0) - return nread; // Contains error code when negative - - i += nread + 1; - } - - if (i < req->size && req->data[i] == '\r') - i++; - if (i < req->size && req->data[i] == '\n') - i++; - - return i; -} - -struct http_header *get_header(struct http_header *headers, const char *field) -{ - while (headers != NULL) - { - if (string_compare_n_str(headers->field, field, strlen(field)) == 0) - return headers; - - headers = headers->next; - } - - return NULL; -} - -struct http_header *create_header(const char *field, const char *value) -{ - struct http_header *res = calloc(1, sizeof(struct http_header)); - if (res == NULL) - return NULL; - - // Field - ssize_t field_size = strlen(field); - if (field_size < 0) - { - free(res); - return NULL; - } - res->field = string_create(field, field_size); - - // Value - ssize_t value_size = strlen(value); - if (value_size < 0) - { - string_destroy(res->field); - free(res); - return NULL; - } - res->value = string_create(value, value_size); - - return res; -} -void append_header(struct http_header **list, struct http_header *element) -{ - if (list == NULL) - return; - - if (*list == NULL) - *list = element; - else - { - struct http_header *cur_elt = *list; - while (cur_elt->next != NULL) - { - cur_elt = cur_elt->next; - } - - cur_elt->next = element; - } -} diff --git a/src/http/headers.h b/src/http/headers.h deleted file mode 100644 index 460cd3e..0000000 --- a/src/http/headers.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef HEADERS_H -#define HEADERS_H - -#include -#include - -#include "http.h" - -// === Functions - -/* - * @brief - * - * @param headers - */ -void destroy_headers(struct http_header *headers); - -/* - * @brief - * - * @param str - * @param offset - * @param res - * - * @return - */ -ssize_t read_field(struct string *str, size_t offset, struct string **res); - -/* - * @brief - * - * @param str - * @param offset - * @param res - * - * @return - */ -ssize_t read_value(struct string *str, size_t offset, struct string **res); - -/* - * @brief - * - * @param res - * @param req - * @param offset - * - * @return - */ -ssize_t parse_headers(struct http_request *res, struct string *req, - size_t offset); - -/* - * @brief - * - * @param headers - * @param field - * - * @return - */ -struct http_header *get_header(struct http_header *headers, const char *field); - -/* - * @brief - * - * @param field - * @param value - * - * @return - */ -struct http_header *create_header(const char *field, const char *value); - -/* - * @brief - * - * @param field - * @param value - * - * @return - */ -void append_header(struct http_header **list, struct http_header *element); - -#endif // ! HEADERS_H diff --git a/src/http/http.c b/src/http/http.c deleted file mode 100644 index 4fb2280..0000000 --- a/src/http/http.c +++ /dev/null @@ -1,511 +0,0 @@ -#include "http.h" - -#include -#include -// #include -#include -#include -#include -#include -#include - -#include "../config/config.h" -#include "../logger/logs.h" -#include "../utils/files/files.h" -#include "../utils/parsing/words.h" -#include "../utils/time/fmt_time.h" -#include "headers.h" - -// === Static variables - -static struct config *config; - -// === Static functions - -// Parses the status line of req, stores the result in res and returns the -// number of read characters or a negative number on error -// WARNING res must be pre allocated -// (See the header for error codes) -static ssize_t parse_reqline(struct http_request *res, struct string *req) -{ - ssize_t i = 0; - ssize_t skipped; - - if (res == NULL || req == NULL) - return ERR_HTTP_INTERNAL_ERROR; - - // Method - if (string_compare_n_str(req, "GET", strlen("GET")) == 0) - { - res->method = GET; - i += strlen("GET"); - } - else if (string_compare_n_str(req, "HEAD", strlen("HEAD")) == 0) - { - res->method = HEAD; - i += strlen("HEAD"); - } - else - return ERR_HTTP_NOT_IMPLEMENTED; - - // Skip space - if (req->data[i++] != ' ') - return ERR_HTTP_INVALID_INPUT; - - // Target (path) - skipped = read_word(req, i, &res->target); - if (skipped <= 0) - return ERR_HTTP_INVALID_INPUT; - i += skipped; - - // Skip space - if (req->data[i++] != ' ') - return ERR_HTTP_INVALID_INPUT; - - // Protocol - skipped = read_word(req, i, &res->protocol); - if (skipped <= 0) - return ERR_HTTP_INVALID_INPUT; - i += skipped; - - // CRLF (EOL) oh qu'il est casse couilles celui-là - ssize_t req_size = req->size; // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah - if (i < req_size && req->data[i] == '\r') - i++; - if (i >= req_size || req->data[i] != '\n') - return ERR_HTTP_INVALID_INPUT; - i++; - // Donc 2h de debug pour ça là ? Plutot envie de me tirer une balle si vous - // voulez mon avis - - return i; -} - -// Split target into path and queries -static void split_target(struct http_request *req) -{ - size_t i = 0; - while (i < req->target->size) - { - if (req->target->data[i] == '?') - { - req->queries = - string_create(req->target->data + i, req->target->size - i); - req->target->size = i; - return; - } - i++; - } -} - -// Finds a valid path based on the client input and returns the corresponding -// FILES return value (see files.h) -static int find_target(struct http_request *req) -{ - // Check filename - if (!check_filename(req->target)) - return ERR_FILES_FORBIDDEN; - - char *target = string_to_charptr(req->target); - int err = is_directory(target); - free(target); - - if (err == FILES_DIR) // Is a directory - { - // Append default file if directory - if (req->target->data[req->target->size - 1] != '/') - string_concat_str(req->target, "/", 1); - - string_concat_str(req->target, config->servers->default_file, - strlen(config->servers->default_file)); - // Recheck - target = string_to_charptr(req->target); - err = is_directory(target); - free(target); - return err; - } - - return err; -} - -// WARNING allocates result on the heap -static char *get_http_method(enum http_method method) -{ - char *res = malloc(8 * sizeof(char)); - switch (method) - { - case GET: - strcpy(res, "HEAD"); - return res; - case HEAD: - strcpy(res, "HEAD"); - return res; - default: - free(res); - return NULL; - } -} - -static struct string *generate_status_message(int status_code) -{ - char *message; - switch (status_code) - { - case 200: - message = "OK"; - break; - case 400: - message = "Bad Request"; - break; - case 403: - message = "Forbidden"; - break; - case 404: - message = "Not Found"; - break; - case 405: - message = "Method Not Allowed"; - break; - case 505: - message = "HTTP Version Not Supported"; - break; - default: - message = "WTF"; - } - - return string_create(message, strlen(message)); -} - -static void check_req(struct http_request *req, struct http_response *resp) -{ - // Method - if (req->method == INVALID_METHOD) - resp->status_code = 405; - - // Protocol - // Oui il y a plus beau, mais on a le temps ou on l'a pas, moi je l'ai pas, - // alors si t'es pas content t'as qu'à le modifier toi même vu que tu as - // visiblement le code source. <3 - if (string_compare_strictly_n_str(req->protocol, "HTTP/", strlen("HTTP/")) - != 0) - resp->status_code = 400; - else if (string_compare_strictly_n_str(req->protocol, "HTTP/1.1", - strlen("HTTP/1.1")) - != 0) - resp->status_code = 505; - - // Host - if (resp->status_code != 400 && resp->status_code != 505) - { - int host_count = 0; - struct http_header *cur = req->headers; - while (cur != NULL) - { - if (cur->field->size == 4 - && string_compare_n_str(cur->field, "host", 4) == 0) - { - host_count++; - if (cur->value == NULL || cur->value->size == 0) - { - resp->status_code = 400; - break; - } - } - cur = cur->next; - } - - if (host_count != 1) - resp->status_code = 400; - } - - // printf("%s %d\n", req->protocol->data, resp->status_code); -} - -// === Functions - -void http_init(struct config *cfg) -{ - config = cfg; -} - -// TODO handle logs -void handle_request(int client_fd, char *client_ip) -{ - char buffer[BUFFER_SIZE]; // Declared in server.h - struct string *str = string_create(NULL, 0); - if (str == NULL) - return; - - // Store request - ssize_t nread = 0; - // This line here is so hard... - // When you realise that you have to remake all your project to be able to - // parse requests of BUFFER_SIZE size... - // Rendez les bijoux de la couronne (svp) - while ((nread = recv(client_fd, buffer, BUFFER_SIZE, 0)) == BUFFER_SIZE) - { - string_concat_str(str, buffer, nread); - } - if (nread > 0) - string_concat_str(str, buffer, nread); - - // Parse request - struct http_request *req = parse_request(str); - if (req == NULL) - { - free(str); - return; - } - char *method = get_http_method(req->method); - char *target = string_to_charptr(req->target); - print_log_request(method, target, client_ip); - free(method); - free(target); - - // Generate response - struct http_response *resp = generate_response(req); - if (resp == NULL) - { - free(str); - free(req); - return; - } - - // Format response to string - struct string *res = format_response(resp); - if (res == NULL) - { - free(str); - free(req); - return; - } - - // Send response - ssize_t nsent; - size_t total_sent = 0; - while (total_sent < res->size) - { - nsent = - send(client_fd, res->data + total_sent, res->size - total_sent, 0); - if (nsent <= 0) - break; - total_sent += nsent; - } - - // Send file - struct http_header *cl_header = get_header(resp->headers, "Content-Length"); - if (cl_header != NULL) - { - char *target = string_to_charptr(req->target); - char *cl_str = string_to_charptr(cl_header->value); - int fd = open(target, O_RDONLY); - if (fd > 0) - sendfile(client_fd, fd, 0, atoi(cl_str)); - - free(target); - free(cl_str); - } - - // Log response - method = get_http_method(req->method); - target = string_to_charptr(req->target); - print_log_response(resp->status_code, method, target, client_ip); - free(method); - free(target); - - // Free - string_destroy(str); - string_destroy(res); - destroy_request(req); - destroy_response(resp); -} - -// TODO handle logs and free adequately -struct http_request *parse_request(struct string *req) -{ - struct http_request *res = calloc(1, sizeof(struct http_request)); - if (res == NULL) - return NULL; - - // Status line - size_t i = 0; - ssize_t nread = parse_reqline(res, req); - if (nread <= 0) - { - if (nread == ERR_HTTP_NOT_IMPLEMENTED) - res->status_code = 501; - else - res->status_code = 400; - - if (res->target == NULL) - res->target = string_create("", 0); - if (res->protocol == NULL) - res->protocol = string_create("HTTP/1.1", 8); - - return res; - } - - // Split path and query - split_target(res); - - i += nread; - - // Headers - nread = parse_headers(res, req, i); - if (nread <= 0) - { - res->status_code = 400; - return res; - } - - return res; -} - -// TODO handle logs and free adequately -struct http_response *generate_response(struct http_request *req) -{ - // Malloc - struct http_response *res = calloc(1, sizeof(struct http_response)); - if (res == NULL) - return NULL; - - // Protocol - // char *protocol = HTTP_VERSION; - char *protocol = "HTTP/1.1"; - res->protocol = string_create(protocol, strlen(protocol)); - - // Target - if (req->status_code == 0) - { - str_concat_string(config->servers->root_dir, - strlen(config->servers->root_dir), req->target); - } - - // Status code - if (req->status_code == 0) - { - switch (find_target(req)) - { - case FILES_REG: - res->status_code = 200; - break; - case ERR_FILES_NOT_FOUND: - res->status_code = 404; - break; - default: - res->status_code = 403; - break; - } - } - else - res->status_code = req->status_code; - - // Check protocol and method - if (req->status_code == 0) - check_req(req, res); - - // Headers - char *time = get_time(); - append_header(&res->headers, create_header("Date", time)); - free(time); // Yes, the one that completely disapeared this year - if (res->status_code == 200) - { - char buf[21] = { 0 }; // (20 ~= log10(2^64)) + 1 (null byte) - char *target = string_to_charptr(req->target); - ssize_t cl = get_file_content_size(target); - free(target); - if (cl >= 0) - { - sprintf(buf, "%lu", cl); - append_header(&res->headers, create_header("Content-Length", buf)); - } - else - { - res->status_code = 403; - } - } - else - { - append_header(&res->headers, create_header("Content-Length", "0")); - } - append_header(&res->headers, create_header("Connection", "close")); - - // Status msg - res->status_msg = generate_status_message(res->status_code); - - return res; -} - -struct string *format_response(struct http_response *resp) -{ - // Protocol - struct string *res = - string_create(resp->protocol->data, resp->protocol->size); - - string_concat_str(res, " ", 1); - - // Status code - char buf[4] = { 0 }; - sprintf(buf, "%d", resp->status_code); - string_concat_str(res, buf, strlen(buf)); - - string_concat_str(res, " ", 1); - - // Status message - char *status_msg = string_to_charptr(resp->status_msg); - string_concat_str(res, status_msg, strlen(status_msg)); - free(status_msg); - - string_concat_str(res, "\r\n", 2); - - // Headers - struct http_header *cur_header = resp->headers; - while (cur_header != NULL) - { - string_concat_str(res, cur_header->field->data, - cur_header->field->size); - - string_concat_str(res, ": ", 2); - - string_concat_str(res, cur_header->value->data, - cur_header->value->size); - - string_concat_str(res, "\r\n", 2); - - cur_header = cur_header->next; - } - - string_concat_str(res, "\r\n", 2); - - return res; -} - -void destroy_request(struct http_request *req) -{ - if (req != NULL) - { - if (req->target != NULL) - string_destroy(req->target); - if (req->protocol != NULL) - string_destroy(req->protocol); - - destroy_headers(req->headers); - - free(req); - } -} - -void destroy_response(struct http_response *resp) -{ - if (resp != NULL) - { - if (resp->status_msg != NULL) - string_destroy(resp->status_msg); - if (resp->protocol != NULL) - string_destroy(resp->protocol); - - destroy_headers(resp->headers); - - free(resp); - } -} diff --git a/src/http/http.h b/src/http/http.h deleted file mode 100644 index 8c3565c..0000000 --- a/src/http/http.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef HTTP_H -#define HTTP_H - -// === Definitions - -// #define _POSIX_C_SOURCE 200809L - -// Error codes -#define ERR_HTTP_INVALID_INPUT -1 -#define ERR_HTTP_NOT_IMPLEMENTED -2 -#define ERR_HTTP_OUT_OF_MEMORY -4 -#define ERR_HTTP_INTERNAL_ERROR -5 - -// === Includes - -#include "../config/config.h" -#include "../utils/string/string.h" - -// === Enums - -enum http_method -{ - INVALID_METHOD, - GET, - // POST, - // PUT, - // DELETE, - // PATCH, - HEAD, - // OPTIONS, - // CONNECT, - // TRACE -}; - -// === Structures - -struct http_header -{ - struct string *field; - struct string *value; - struct http_header *next; -}; - -struct http_request -{ - enum http_method method; - struct string *target; - struct string *queries; - struct string *protocol; - struct http_header *headers; // Headers linked list - int status_code; // Set by program -}; - -struct http_response -{ - struct string *protocol; - int status_code; - struct string *status_msg; - struct http_header *headers; // Headers linked list -}; - -// === Functions - -/* @brief Initializes the HTTP module with the given configuration - * - * @warn Do no use any other function of this module before calling that one - * - * @param cfg - */ -void http_init(struct config *cfg); - -/* @brief Reads, parses the request and responds adequately all-in-one - * - * @param client_fd - */ -void handle_request(int client_fd, char *client_ip); - -/* @brief Parses the HTTP request and splits it into a request structure - * - * @param req - * - * @return A pointer to the structure containing the request infos on success, - * NULL otherwise - */ -struct http_request *parse_request(struct string *req); - -/* @brief Generates a response to the given request - * - * @param req - * - * @return A pointer to the generated response struct on success, - * NULL otherwise - */ -struct http_response *generate_response(struct http_request *req); - -/* @brief Formats the given response structure into a valid HTTP response - * string - * - * @param resp - * - * @return A pointer to the string containing the response on success, - * NULL otherwise - */ -struct string *format_response(struct http_response *resp); - -/* @brief Free all allocated memory inside req and req itself - * - * @param req - */ -void destroy_request(struct http_request *req); - -/* @brief Free all allocated memory inside resp and resp itself - * - * @param resp - */ -void destroy_response(struct http_response *resp); - -#endif // ! HTTP_H diff --git a/src/logger/errors.c b/src/logger/errors.c deleted file mode 100644 index 4bbb369..0000000 --- a/src/logger/errors.c +++ /dev/null @@ -1,61 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "errors.h" - -#include -#include -#include -#include -#include - -#include "../utils/time/fmt_time.h" -#include "logs.h" - -// === Static variable - -static struct logs_config config; - -// === Functions - -void errlog_init(bool enabled, int logfile_fd, struct server_config *serv_cfg) -{ - config.enabled = enabled; - if (logfile_fd == STDOUT_FILENO) - config.logfile_fd = STDERR_FILENO; - config.logfile_fd = logfile_fd; - config.server_cfg = serv_cfg; -} - -void print_err(void) -{ - print_log_err("%s", get_err()); -} - -void print_log_err(char *format, ...) -{ - if (!config.enabled) - return; - - // Log prefix (time and server name) - char *time = get_time(); - dprintf(config.logfile_fd, "%s [%s] ERROR ", time, - config.server_cfg->server_name); - free(time); - - // Print actual log - va_list args; - va_start(args, format); - vdprintf(config.logfile_fd, format, args); - va_end(args); - - // New line - dprintf(config.logfile_fd, "\n"); - - // Print to stderr - fprintf(stderr, "Error: %s", get_err()); -} - -char *get_err(void) -{ - return strerror(errno); -} diff --git a/src/logger/errors.h b/src/logger/errors.h deleted file mode 100644 index 09b8be2..0000000 --- a/src/logger/errors.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef ERRORS_H -#define ERRORS_H - -#include -#include - -#include "../config/config.h" - -/* @brief Initialize the error logging submodule - * @warning Do not use 'as is', use log_init() instead - */ -void errlog_init(bool enabled, int logfile_fd, struct server_config *serv_cfg); - -/* @brief Retrieves the last error with errno and prints the corresponding - * error message in the logs and stderr - */ -void print_err(void); - -/* @brief Prints error logs, just like print_log() but for errors - */ -void print_log_err(char *format, ...); - -/* @brief Returns the string corresponding to the last error that happened - */ -char *get_err(void); - -#endif // ! ERRORS_H diff --git a/src/logger/logs.c b/src/logger/logs.c deleted file mode 100644 index f82f706..0000000 --- a/src/logger/logs.c +++ /dev/null @@ -1,79 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "logs.h" - -#include -#include -#include -#include - -#include "../utils/time/fmt_time.h" -#include "errors.h" - -// === Static variables - -static struct logs_config config; - -// === Functions - -int log_init(struct config *global_config) -{ - int return_value = 0; - config.enabled = global_config->log; - config.server_cfg = global_config->servers; - if (global_config->log_file != NULL) - { - config.logfile_fd = open(global_config->log_file, O_WRONLY); - if (config.logfile_fd <= 0) - { - config.enabled = false; - return_value = 1; - } - } - else - { - config.logfile_fd = STDOUT_FILENO; - } - - errlog_init(config.enabled, config.logfile_fd, config.server_cfg); - return return_value; -} - -void print_log(char *format, ...) -{ - if (!config.enabled) - return; - - // Log prefix (time and server name) - char *time = get_time(); - dprintf(config.logfile_fd, "%s [%s] ", time, - config.server_cfg->server_name); - free(time); - - // Print actual log - va_list args; - va_start(args, format); - vdprintf(config.logfile_fd, format, args); - va_end(args); - - // New line - dprintf(config.logfile_fd, "\n"); -} - -void print_log_request(char *request_type, char *target, char *client_ip) -{ - print_log("received %s, on '%s' from %s", request_type, target, client_ip); -} - -void print_log_response(int status_code, char *request_type, char *target, - char *client_ip) -{ - print_log("responding with %d to %s for %s on '%s'", status_code, client_ip, - request_type, target); -} - -void log_terminate(void) -{ - if (config.logfile_fd != 0 && config.logfile_fd != STDOUT_FILENO) - close(config.logfile_fd); -} diff --git a/src/logger/logs.h b/src/logger/logs.h deleted file mode 100644 index f18a6d4..0000000 --- a/src/logger/logs.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef LOGS_H -#define LOGS_H - -#include - -#include "../config/config.h" - -struct logs_config -{ - bool enabled; - int logfile_fd; - struct server_config *server_cfg; -}; - -/* @brief Initializes the logging module - * - * @param config - * - * @return 0 on success, an error code otherwise - */ -int log_init(struct config *config); - -/* @brief Prints logs (or not) conformly to the config given by the user. - * Works like printf (because it uses it under the hood) with a - * formatted string and variadic arguments. - * - * @param format - * @param ... - */ -void print_log(char *format, ...); - -/* @brief Prints request logs with the adequate format in the logfile - * - * @param request_type - * @param target - * @param client_ip - */ -void print_log_request(char *request_type, char *target, char *client_ip); - -/* @brief Prints response logs with the adequate format in the logfile - * - * @param status_code - * @param request_type - * @param target - * @param client_ip - */ -void print_log_response(int status_code, char *request_type, char *target, - char *client_ip); - -/* @brief Gracefully exits the logs module - */ -void log_terminate(void); - -#endif // ! LOGS_H diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 6f2c5b2..0000000 --- a/src/main.c +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include "config/config.h" -#include "daemon/daemon.h" -#include "http/http.h" -#include "logger/logs.h" -#include "server/server.h" - -#define ERR_ARG 2 - -int main(int argc, char **argv) -{ - // Parse config - struct config *config = parse_configuration(argc, argv); - if (config == NULL) - return ERR_ARG; - - // Initialize modules - log_init(config); // Ignore ret val - http_init(config); - daemon_init(config); - - // Start server - switch (config->daemon) - { - case NO_OPTION: - start_server(config); - break; - - case START: - start_daemon(); - break; - - case RESTART: - restart_daemon(); - break; - - case STOP: - stop_daemon(); - break; - - default: - return 2; - } - - config_destroy(config); - return 0; -} diff --git a/src/server/server.c b/src/server/server.c deleted file mode 100644 index 56c0c53..0000000 --- a/src/server/server.c +++ /dev/null @@ -1,184 +0,0 @@ -// === Definitions - -#define _POSIX_C_SOURCE 200112L - -#define BUFFER_SIZE 1024 - -// === Includes -#include "server.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../http/http.h" -#include "../logger/errors.h" -#include "../logger/logs.h" -// #include "../logger/logs.h" - -// === Static variables - -static int server_socket = 0; -static struct config *config; - -// === Static functions - -// Creates and bind the server communication socket -static int get_socket(const char *hostname, const char *port) -{ - // set lower-level protocols infos - struct addrinfo hints = { 0 }; - hints.ai_family = AF_INET; // IPv4 - hints.ai_socktype = SOCK_STREAM; // Socket type (necessary for TCP) - hints.ai_protocol = IPPROTO_TCP; // TCP - - // Retrieve node informations - struct addrinfo *client_addr = NULL; // Client addresses list - int err = getaddrinfo(hostname, port, &hints, &client_addr); - if (err != 0) - { - print_err(); - return -1; - } - - // Try each address - int sock_fd; - bool socket_bound = false; - struct addrinfo *cur_addr = client_addr; - while (cur_addr != NULL && !socket_bound) - { - // Create socket - sock_fd = socket(cur_addr->ai_family, cur_addr->ai_socktype, - cur_addr->ai_protocol); - if (sock_fd == -1) - { - cur_addr = cur_addr->ai_next; - continue; - } - - // ... Has something to do with not changing port - int sock_opt = -1; - setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt, - sizeof(sock_opt)); - - // Assign name to socket (bind) - err = bind(sock_fd, cur_addr->ai_addr, cur_addr->ai_addrlen); - if (err != -1) - socket_bound = true; - else - close(sock_fd); - - cur_addr = cur_addr->ai_next; - } - - freeaddrinfo(client_addr); - return sock_fd; -} - -// Reads and sends back received data to client -// For testing purposes -// static void send_back(int client_fd) -// { -// ssize_t nread = 0; -// char buffer[BUFFER_SIZE]; - -// while ((nread = recv(client_fd, buffer, BUFFER_SIZE, 0)) > 0) -// { -// ssize_t sent; -// while (nread > 0) -// { -// sent = send(client_fd, buffer, nread, 0); -// if (sent == -1) // Send failed -// break; - -// nread -= sent; -// } -// } -// } - -// Retrieves client ipv4 address and stores it in res -// WARNING: res must be of size INET_ADDRSTRLEN -static void get_ip(struct sockaddr *client_addr, char *res) -{ - // NOTE: exceptionally authorized cast - struct in_addr ipAddr = ((struct sockaddr_in *)client_addr)->sin_addr; - inet_ntop(AF_INET, &ipAddr, res, INET_ADDRSTRLEN); -} - -static void signal_handler(int signal) -{ - print_log("EVENT Signal received: %d", signal); - switch (signal) - { - case SIGINT: { - print_log("STOPPING Stopping server..."); - stop_server(); - config_destroy(config); - exit(0); - } - default: - return; - } -} - -// === Functions - -void start_server(struct config *cfg) -{ - config = cfg; - const char *host = config->servers->ip; - const char *port = config->servers->port; - - server_socket = get_socket(host, port); - if (server_socket == -1) - // TODO log that - return; - - int err = listen(server_socket, SOMAXCONN); - if (err == -1) - return; - - // Signal handling - struct sigaction siga; - siga.sa_flags = 0; - siga.sa_handler = signal_handler; - // initialize mask - if (sigemptyset(&siga.sa_mask) < 0) - // TODO log that - return; - if (sigaction(SIGINT, &siga, NULL) == -1 - || sigaction(SIGPIPE, &siga, NULL) == -1) - return; - - // Main loop - while (1) - { - struct sockaddr client_addr; - socklen_t client_addr_len = sizeof(struct sockaddr); - int client_fd = accept(server_socket, &client_addr, &client_addr_len); - if (client_fd == -1) - continue; - - // TODO handle signals to stop - - // Get ip - char client_ip[INET_ADDRSTRLEN]; - get_ip(&client_addr, client_ip); - - handle_request(client_fd, client_ip); - // send_back(client_fd); - close(client_fd); - } - - stop_server(); -} - -void stop_server(void) -{ - close(server_socket); -} diff --git a/src/server/server.h b/src/server/server.h deleted file mode 100644 index 0c441ac..0000000 --- a/src/server/server.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef SERVER_H -#define SERVER_H - -#include "../config/config.h" - -/* @brief Starts the HTTP server - * - * @warn Make sure to initialize modules before calling - * - * @param hostname - * @param port - */ -void start_server(struct config *cfg); - -/* @brief Stops the currently running HTTP server - */ -void stop_server(void); - -#endif // ! SERVER_H diff --git a/src/utils/files/files.c b/src/utils/files/files.c deleted file mode 100644 index 0f08c25..0000000 --- a/src/utils/files/files.c +++ /dev/null @@ -1,105 +0,0 @@ -#define _POSIX_C_SOURCE 200112L - -#include "files.h" - -#include -#include -#include - -// #include "../string/string.h" - -// int file_exists(const char *path) -// {} - -int is_directory(const char *path) -{ - struct stat path_stat; - if (lstat(path, &path_stat) == 0) - { - if (S_ISDIR(path_stat.st_mode)) // Directory - return FILES_DIR; - else if (S_ISREG(path_stat.st_mode)) // Regular file - return FILES_REG; - else - return FILES_OTHER; - } - else - { - if (errno == ENOENT) // File not found - return ERR_FILES_NOT_FOUND; - else // Other errors - return ERR_FILES_FORBIDDEN; - } -} - -// TODO handle logging -// struct string *get_file_content(const char *path) -// { -// // Open file -// FILE *stream = fopen(path, "r"); -// if (stream == NULL) -// return NULL; - -// // Alloc result -// char buf[BUFFER_SIZE]; -// struct string *res = string_create(NULL, 0); -// if (res == NULL) -// { -// return NULL; -// } - -// int nread; -// while ((fgets(buf, BUFFER_SIZE, stream))) -// } - -// TODO not implemented -bool check_filename(struct string *path) -{ - if (path == NULL || path->size <= 0) - return false; - - return true; -} - -ssize_t get_file_content_size(const char *path) -{ - FILE *stream = fopen(path, "r"); - if (stream == NULL) - return -2; - - fseek(stream, 0, SEEK_END); - ssize_t res = ftell(stream); - fclose(stream); - - return res; -} - -int write_to_file(const char *path, struct string *buf) -{ - FILE *stream = fopen(path, "w"); - if (stream == NULL) - return 1; - - fwrite(buf->data, sizeof(char), buf->size, stream); - - fclose(stream); - - return 0; -} - -int write_pid(const char *filepath, int pid) -{ - FILE *stream = fopen(filepath, "w"); - if (stream == NULL) - return 1; - - if (fprintf(stream, "%d", pid) <= 0) - { - fclose(stream); - return 1; - } - - fclose(stream); - - return 0; -} diff --git a/src/utils/files/files.h b/src/utils/files/files.h deleted file mode 100644 index 0c2cdcc..0000000 --- a/src/utils/files/files.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef FILES_H -#define FILES_H - -// === Definitions - -#define BUFFER_SIZE 1024 - -// Return codes -#define FILES_REG 0 -#define FILES_DIR 1 -#define FILES_OTHER 8 - -// Errors -#define ERR_FILES_NOT_FOUND -1 -#define ERR_FILES_FORBIDDEN -2 - -// === Includes - -#include -#include -#include - -#include "../string/string.h" - -// === Functions - -/* - * @brief - * - * @param path - * - * @return - */ -// bool file_exists(const char *path); - -/* - * @brief - * - * @param path - * - * @return Returns the corresponding return value / error code (see header - * definitions) - */ -int is_directory(const char *path); - -/* - * @brief - * - * @param path - * - * @return - */ -// char *get_file(const char *path); - -/* - * @brief - * - * @param path - * - * @return - */ -struct string *get_file_content(const char *path); - -/* - * @brief - * - * @param path - * - * @return - */ -bool check_filename(struct string *path); - -/* - * @brief - * - * @param filename - * - * @return - */ -// bool sanitize_filename(struct string *filename); - -/* - * @brief - * - * @param path - * - * @return - */ -ssize_t get_file_content_size(const char *path); - -/* - * @brief - * - * @param path - * - * @return 0 on success, the corresponding error code otherwise - */ -int write_to_file(const char *path, struct string *buf); - -/* - * @brief - * - * @param path - * - * @return 0 on success, the corresponding error code otherwise - */ -int write_pid(const char *filepath, int pid); - -#endif // ! FILES_H diff --git a/src/utils/parsing/blanks.c b/src/utils/parsing/blanks.c deleted file mode 100644 index ac7c20f..0000000 --- a/src/utils/parsing/blanks.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "blanks.h" - -bool is_space(char c) -{ - switch (c) - { - case ' ': - case '\t': - return true; - - default: - return false; - } -} - -bool is_blank(char c) -{ - switch (c) - { - case '\f': - case '\n': - case '\r': - case '\t': - case '\v': - case ' ': - return true; - - default: - return false; - } -} - -ssize_t skip_blanks(struct string *str, size_t offset) -{ - size_t i = offset; - while (i < str->size) - { - if (!is_space(str->data[i])) - return i; - - i++; - } - - // Reached EOL - return i; -} diff --git a/src/utils/parsing/blanks.h b/src/utils/parsing/blanks.h deleted file mode 100644 index 417dc0a..0000000 --- a/src/utils/parsing/blanks.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef BLANKS_H -#define BLANKS_H - -// === Definitions - -#define BLANKS_LIST "\f\n\r\t\v " - -// === Includes - -#include -#include -#include - -#include "../string/string.h" - -// === Functions -/* - * NOTE: - * As ispace(3) and isblank(3) functions from stdlib can be confusing and not - * always best suited for what I want to do, I decided to reimplement them. - * So be warned, they won't have the same behavior as their libc counterparts. - */ - -/* - * Doc: TODO - */ -bool is_space(char c); - -/* - * Doc: TODO - */ -bool is_blank(char c); - -/* - * Doc: TODO - */ -ssize_t skip_blanks(struct string *str, size_t offset); - -#endif // ! BLANKS_H diff --git a/src/utils/parsing/words.c b/src/utils/parsing/words.c deleted file mode 100644 index cea5a79..0000000 --- a/src/utils/parsing/words.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "words.h" - -#include "blanks.h" - -bool str_contains(const char *str, char c) -{ - int i = 0; - while (str[i] != '\0') - { - if (str[i] == c) - return true; - - i++; - } - - return false; -} - -ssize_t read_word(struct string *str, size_t offset, struct string **res) -{ - size_t i = offset; - while (i < str->size) - { - if (is_blank(str->data[i])) - break; - - i++; - } - - *res = string_create(str->data + offset, i - offset); - if (res == NULL) - return -1; - - return i - offset; -} - -ssize_t read_word_delim(struct string *str, size_t offset, struct string **res, - const char *delims) -{ - size_t i = offset; - bool found = false; - - while (i < str->size && !found) - { - if (str_contains(delims, str->data[i])) - { - found = true; - break; - } - - i++; - } - - *res = string_create(str->data + offset, i - offset); - if (res == NULL) - return -1; - - return i - offset; -} - -ssize_t read_word_restrict(struct string *str, size_t offset, - struct string **res, const char *restr) -{ - size_t i = offset; - bool found = false; - - while (i < str->size && !found) - { - if (!str_contains(restr, str->data[i])) - { - found = true; - break; - } - - i++; - } - - *res = string_create(str->data + offset, i - offset); - if (res == NULL) - return -1; - - return i - offset; -} - -ssize_t read_word_predicate(struct string *str, size_t offset, - struct string **res, bool (*predicate)(char c)) -{ - size_t i = offset; - bool found = false; - - while (i < str->size && !found) - { - if (predicate(str->data[i])) - { - found = true; - break; - } - - i++; - } - - *res = string_create(str->data + offset, i - offset); - if (res == NULL) - return -1; - - return i - offset; -} diff --git a/src/utils/parsing/words.h b/src/utils/parsing/words.h deleted file mode 100644 index d259bff..0000000 --- a/src/utils/parsing/words.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef WORDS_H -#define WORDS_H - -// === Includes - -#include -#include -#include - -#include "../string/string.h" - -// === Functions - -/* - * Doc: TODO - */ -bool str_contains(const char *str, char c); - -/* - * Doc: TODO - */ -ssize_t read_word(struct string *str, size_t offset, struct string **res); - -/* - * Doc: TODO - */ -ssize_t read_word_delim(struct string *str, size_t offset, struct string **res, - const char *delims); - -/* - * Doc: TODO - */ -ssize_t read_word_restrict(struct string *str, size_t offset, - struct string **res, const char *restr); - -/* - * Doc: TODO - */ -ssize_t read_word_predicate(struct string *str, size_t offset, - struct string **res, bool (*predicate)(char c)); - -#endif // ! WORDS_H diff --git a/src/utils/string/string.c b/src/utils/string/string.c deleted file mode 100644 index 5a28b5a..0000000 --- a/src/utils/string/string.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "string.h" - -#include -#include -#include - -struct string *string_create(const char *str, size_t size) -{ - struct string *res = calloc(1, sizeof(struct string)); - if (res == NULL) - return NULL; - - if (size > 0) - { - res->data = malloc(size * sizeof(char)); - if (res->data == NULL) - { - free(res); - return NULL; - } - - memcpy(res->data, str, size); - } - - res->size = size; - return res; -} - -int string_compare_n_str(const struct string *str1, const char *str2, size_t n) -{ - size_t i = 0; - int res = 0; - char reached_str2_end = 0; - while (i < n) - { - if (i < str1->size) - res += str1->data[i]; - - if (!reached_str2_end) - { - if (str2[i] == '\0') - reached_str2_end = 1; - else - res -= str2[i]; - } - - i++; - } - - return res; -} - -void string_concat_str(struct string *str, const char *to_concat, size_t size) -{ - size_t new_size = str->size + size; - size_t str_size = str->size; - - if (new_size == 0) - return; - - str->size = new_size; - if (str_size == 0) - { - str->data = malloc(new_size); - if (str->data == NULL) - return; // Handle ? - } - else - { - str->data = realloc(str->data, new_size * sizeof(char)); - if (str->data == NULL) - return; // Handle ? - } - - for (size_t i = 0; i < size; i++) - { - str->data[str_size + i] = to_concat[i]; - } -} - -void str_concat_string(const char *str, size_t size, struct string *to_concat) -{ - size_t new_size = to_concat->size + size; - size_t tmp_size = to_concat->size; - - if (new_size == 0) - return; - - to_concat->size = new_size; - if (tmp_size == 0) - { - to_concat->data = malloc(new_size); - if (to_concat->data == NULL) - return; // Handle ? - } - else - { - // Temporary buffer - char *tmp = malloc(tmp_size * sizeof(char)); - if (tmp == NULL) - return; // Handle ? - - // (Duplicate) - memcpy(tmp, to_concat->data, tmp_size); - - // Reallocate string - char *new_data = realloc(to_concat->data, new_size * sizeof(char)); - if (to_concat->data == NULL) - { - to_concat->size = tmp_size; // Restore (original ptr still valid) - return; // Handle ? - } - to_concat->data = new_data; - - memcpy(to_concat->data, str, size); - memcpy(to_concat->data + size, tmp, tmp_size); - free(tmp); - } -} - -void string_to_lowercase(struct string *str) -{ - for (size_t i = 0; i < str->size; i++) - { - if (isupper(str->data[i])) - str->data[i] += 'a' - 'A'; - } -} - -void string_destroy(struct string *str) -{ - if (str != NULL) - { - if (str->data != NULL) - { - free(str->data); - } - free(str); - } -} - -char *string_to_charptr(struct string *str) -{ - if (str == NULL || str->data == NULL) - return NULL; - - char *res = calloc(str->size + 1, sizeof(char)); - if (res == NULL) - return NULL; - - memcpy(res, str->data, str->size); - - res[str->size] = '\0'; - - return res; -} - -// WARNING takes n as valid, will not stop on '\0' -int string_compare_strictly_n_str(const struct string *str1, const char *str2, - size_t n) -{ - if (str1->size < n) - return -1; - - size_t i = 0; - int res = 0; - while (i < n) - { - res += str1->data[i]; - res -= str2[i]; - i++; - } - - return res; -} diff --git a/src/utils/time/fmt_time.c b/src/utils/time/fmt_time.c deleted file mode 100644 index cc37959..0000000 --- a/src/utils/time/fmt_time.c +++ /dev/null @@ -1,15 +0,0 @@ -#include "fmt_time.h" - -#include -#include - -char *get_time(void) -{ - char *buf = malloc(64 * sizeof(char)); // Oui, 64 - time_t local_ts = time(NULL); - struct tm *gmt_time = gmtime(&local_ts); - - // return asctime(gmt_time); - strftime(buf, 64, "%a, %d %b %Y %H:%M:%S %Z", gmt_time); - return buf; -} diff --git a/src/utils/time/fmt_time.h b/src/utils/time/fmt_time.h deleted file mode 100644 index 81d5bd8..0000000 --- a/src/utils/time/fmt_time.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef FMT_TIME_H -#define FMT_TIME_H - -/* @brief Calculates the GMT time based on machine's local time - * and returns it as a string - * - * @return A NULL-terminated string containing the fromatted GMT time - */ -char *get_time(); - -#endif // ! FMT_TIME_H diff --git a/tests/test_root_dir/index.html b/tests/test_root_dir/index.html deleted file mode 100644 index f5bf13f..0000000 --- a/tests/test_root_dir/index.html +++ /dev/null @@ -1 +0,0 @@ -

YEAAH

diff --git a/tests/test_suite.py b/tests/test_suite.py deleted file mode 100644 index 504f05a..0000000 --- a/tests/test_suite.py +++ /dev/null @@ -1,178 +0,0 @@ -import subprocess as sp -import http -import requests -import socket -import pytest -import time - -host = "127.0.0.1" -port = "6994" - -executable = "./httpd" - -def spawn_httpd(stdout_filename, args=[]): - with open(stdout_filename,"w") as f: - httpd_proc = sp.Popen([executable,"--pid_file","/tmp/HTTPd.pid","--ip",host,"--port", port, "--root_dir","test_root_dir/","--server_name","httpd"] if args == [] else [executable] + args, stdout=f,stderr=sp.PIPE,bufsize=0) - time.sleep(0.2) - - return httpd_proc - -def kill_httpd(proc): - #proc.send_signal(sp.SIGINT) - proc.kill() - -# @pytest.mark.timeout(2) -def test_bad_config(): - proc = spawn_httpd("out.log", ["hello","world"]) - proc.wait(1) - try: - assert proc.returncode == 2 - finally: - kill_httpd(proc) - -# @pytest.mark.timeout(2) -def test_get_index(): - proc = spawn_httpd("out.log") - req = requests.get(f"http://{host}:{port}/index.html") - assert req.status_code == 200 - with open("./test_root_dir/index.html","r") as f: - try: - assert f.read() == req.text - finally: - kill_httpd(proc) - -# @pytest.mark.timeout(2) -def test_get_default(): - proc = spawn_httpd("out.log") - req = requests.get(f"http://{host}:{port}/") - assert req.status_code == 200 - with open("./test_root_dir/index.html","r") as f: - try: - assert f.read() == req.text - finally: - kill_httpd(proc) - -# @pytest.mark.timeout(2) -def test_no_file(): - proc = spawn_httpd("out.log") - req = requests.get(f"http://{host}:{port}/notindex.html") - assert req.status_code == 404 - -# @pytest.mark.timeout(2) -def test_bad_request(): - proc = spawn_httpd("out.log") - sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - sock.connect((host,int(port))) - - request = f"GET /index.html FTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n" - - sock.sendall(request.encode()) - - resp = sock.recv(1024) - resp_decoded = resp.decode() - - try: - assert "400 Bad Request" in resp_decoded - finally: - kill_httpd(proc) - -# @pytest.mark.timeout(2) -def test_invalid_method(): - proc = spawn_httpd("out.log") - sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - sock.connect((host,int(port))) - - request = f"PUT /index.html HTTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n" - - sock.sendall(request.encode()) - - response = http.client.HTTPResponse(sock) - response.begin() - - try: - assert response.status == 405 - finally: - kill_httpd(proc) - - -# @pytest.mark.timeout(2) -def test_invalid_version(): - proc = spawn_httpd("out.log") - sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - sock.connect((host,int(port))) - - request = f"GET /index.html HTTP/1.2\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n" - - sock.sendall(request.encode()) - - response = http.client.HTTPResponse(sock) - response.begin() - - try: - assert response.status == 505 - finally: - kill_httpd(proc) - -# @pytest.mark.timeout(2) -def test_bad_request(): - proc = spawn_httpd("out.log") - sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - sock.connect((host,int(port))) - - request = f"GET /index.html FTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n" - - sock.sendall(request.encode()) - - response = http.client.HTTPResponse(sock) - response.begin() - - try: - assert response.status == 400 - finally: - kill_httpd(proc) - -@pytest.mark.timeout(2) -def test_head_index(): - proc = spawn_httpd("out.log") - try: - req = requests.head(f"http://{host}:{port}/index.html") - assert req.status_code == 200 - assert req.text == "" - finally: - kill_httpd(proc) - -@pytest.mark.timeout(2) -def test_missing_host(): - proc = spawn_httpd("out.log") - sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - sock.connect((host,int(port))) - - request = f"GET /index.html HTTP/1.1\r\nConnection: close\r\n\r\n" - - sock.sendall(request.encode()) - - response = http.client.HTTPResponse(sock) - response.begin() - - try: - assert response.status == 400 - finally: - kill_httpd(proc) - -@pytest.mark.timeout(2) -def test_directory_traversal(): - proc = spawn_httpd("out.log") - sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - sock.connect((host,int(port))) - - request = f"GET /../test_suite.py HTTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n" - - sock.sendall(request.encode()) - - response = http.client.HTTPResponse(sock) - response.begin() - - try: - assert response.status in [400, 403, 404] - finally: - kill_httpd(proc) diff --git a/tests/tests_mieux.sh b/tests/tests_mieux.sh deleted file mode 100755 index 6851190..0000000 --- a/tests/tests_mieux.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -# Simple test script for HTTP/1.1 Host header compliance -# Usage: ./test_host_compliance.sh [IP] [PORT] - -IP=${1:-"127.0.0.1"} -PORT=${2:-"6996"} - -echo "Targeting server at $IP:$PORT" - -test_req() { - NAME="$1" - PAYLOAD="$2" - EXPECTED="$3" - - echo -n "Test: $NAME ... " - # Send payload, wait max 1s for response - RESP=$(printf "$PAYLOAD" | nc -w 1 $IP $PORT 2>/dev/null | head -n 1) - - if echo "$RESP" | grep -q "$EXPECTED"; then - echo "PASS" - else - echo "FAIL (Expected '$EXPECTED', got '$RESP')" - fi -} - -# 1. Valid Request -test_req "Valid Request" "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" "200 OK" - -# 2. Missing Host Header -test_req "Missing Host" "GET / HTTP/1.1\r\n\r\n" "400 Bad Request" - -# 3. Empty Host Header -test_req "Empty Host" "GET / HTTP/1.1\r\nHost:\r\n\r\n" "400 Bad Request" - -# 4. Multiple Host Headers -test_req "Multiple Hosts" "GET / HTTP/1.1\r\nHost: a\r\nHost: b\r\n\r\n" "400 Bad Request" - -# 5. Bad Protocol Version (Should be 505 now with the fix) -test_req "Bad Protocol (HTTP/1.0)" "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n" "505" \ No newline at end of file