diff --git a/httpd/src/config/config.c b/httpd/src/config/config.c index dfbe9a7..d910424 100644 --- a/httpd/src/config/config.c +++ b/httpd/src/config/config.c @@ -50,6 +50,10 @@ 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) diff --git a/httpd/src/config/config.h b/httpd/src/config/config.h index acbc26c..5caf4ac 100644 --- a/httpd/src/config/config.h +++ b/httpd/src/config/config.h @@ -2,6 +2,7 @@ #define CONFIG_H #define _XOPEN_SOURCE 500 +#define HTTP_VERSION "HTTP/1.1" #include @@ -32,6 +33,7 @@ struct config char *pid_file; char *log_file; bool log; + char* protocol_version; struct server_config *servers; enum daemon daemon; diff --git a/httpd/src/daemon/daemon.h b/httpd/src/daemon/daemon.h index f83ec6a..b916483 100644 --- a/httpd/src/daemon/daemon.h +++ b/httpd/src/daemon/daemon.h @@ -7,7 +7,7 @@ * * @param */ -void stop_daemon(int pid); +void stop_daemon(); /* @brief * @@ -21,6 +21,6 @@ int start_daemon(void); * * @return */ -int restart_daemon(int pid); +int restart_daemon(); #endif // ! DAEMON_H diff --git a/httpd/src/http/headers.c b/httpd/src/http/headers.c index 29ba0a2..8fefc1b 100644 --- a/httpd/src/http/headers.c +++ b/httpd/src/http/headers.c @@ -1,6 +1,8 @@ #include "headers.h" +#include #include +#include #include "../utils/parsing/words.h" @@ -84,3 +86,49 @@ ssize_t parse_headers(struct http_request *res, struct string *req, return i + 1; } + +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/httpd/src/http/headers.h b/httpd/src/http/headers.h index 9bbd0a5..59592de 100644 --- a/httpd/src/http/headers.h +++ b/httpd/src/http/headers.h @@ -59,4 +59,24 @@ ssize_t parse_headers(struct http_request *res, struct string *req, */ 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/httpd/src/http/http.c b/httpd/src/http/http.c index 281d132..011709e 100644 --- a/httpd/src/http/http.c +++ b/httpd/src/http/http.c @@ -1,11 +1,14 @@ #include "http.h" +#include +#include #include #include #include #include "../utils/files/files.h" #include "../utils/parsing/words.h" +#include "../utils/time/fmt_time.h" #include "headers.h" // === Static variables @@ -45,7 +48,7 @@ static ssize_t parse_reqline(struct http_request *res, struct string *req) return ERR_HTTP_INVALID_INPUT; // Target (path) - skipped += read_word(req, i, &res->path); + skipped += read_word(req, i, &res->target); if (skipped <= 0) return ERR_HTTP_INVALID_INPUT; @@ -58,29 +61,81 @@ static ssize_t parse_reqline(struct http_request *res, struct string *req) if (skipped <= 0) return ERR_HTTP_INVALID_INPUT; - // EOL - if (req->data[i++] != '\n') + // CRLF (EOL) + if (req->data[i++] != '\r' && req->data[i++] != '\n') return ERR_HTTP_INVALID_INPUT; return i; } +// Split target into path and queries static void split_target(struct http_request *req) { size_t i = 0; - while (i < req->path->size) + while (i < req->target->size) { - if (req->path->data[i] == '?') + if (req->target->data[i] == '?') { req->queries = - string_create(req->path->data + i, req->path->size - i); - req->path->size = i; + 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 +static bool find_target(struct http_request *req) +{ + // Check filename + if (!check_filename(req->target)) + return false; + int err = is_directory(req->target->data); + if (err == -1) + return false; + else if (err == 1) + { + 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)); + } + + return true; +} + +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)); +} + // === Functions void http_init(struct config *cfg) @@ -117,31 +172,84 @@ struct http_request *parse_request(struct string *req) // TODO handle logs and free adequately struct http_response *generate_response(struct http_request *req) { - // Path + // Malloc + struct http_response *res = calloc(1, sizeof(struct http_response)); + if (res == NULL) + return NULL; - // Check filename - if (!check_filename(req->path)) - return NULL; - int err = is_directory(req->path->data); - if (err == -1) - return NULL; - else if (err == 1) + // Protocol + char *protocol = "HTTP/1.1"; + res->protocol = string_create(protocol, strlen(protocol)); + + // Status code + if (req->status_code == 0) { - string_concat_str(req->path, "/", 1); - string_concat_str(req->path, config->servers->default_file, - strlen(config->servers->default_file)); + if (!find_target(req)) + res->status_code = 404; + else + res->status_code = 200; } + + // Status msg + res->status_msg = generate_status_message(res->status_code); + + // 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 }; // (21 ~= log10(2^64)) + 1 (null byte) + char *target = string_to_charptr(req->target); + sprintf(buf, "%lu", get_file_content_size(target)); + append_header(&res->headers, create_header("Content-Length", buf)); + } + append_header(&res->headers, create_header("Connection", "close")); + + return res; } -// struct string *format_response(struct http_response *resp) -// {} +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 + string_concat_str(res, buf, strlen(buf)); + + 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, "/r/n", 2); + } + + string_concat_str(res, "/r/n", 2); + + return res; +} void destroy_request(struct http_request *req) { if (req != NULL) { - if (req->path != NULL) - string_destroy(req->path); + if (req->target != NULL) + string_destroy(req->target); if (req->protocol != NULL) string_destroy(req->protocol); diff --git a/httpd/src/http/http.h b/httpd/src/http/http.h index a35fbbe..c518826 100644 --- a/httpd/src/http/http.h +++ b/httpd/src/http/http.h @@ -43,10 +43,11 @@ struct http_header struct http_request { enum http_method method; - struct string *path; + 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 @@ -61,7 +62,7 @@ struct http_response /* @brief Initializes the HTTP module with the given configuration * - * @warning Do no use any other function of this module before calling that one + * @warn Do no use any other function of this module before calling that one * * @param cfg */ diff --git a/httpd/src/main.c b/httpd/src/main.c index d36eda7..835fad9 100644 --- a/httpd/src/main.c +++ b/httpd/src/main.c @@ -1,7 +1,7 @@ #include -#include #include "config/config.h" +#include "http/http.h" #include "logger/logs.h" #include "server/server.h" @@ -9,12 +9,35 @@ 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); + http_init(config); + + // Start server + switch (config->daemon) + { + NO_OPTION: + start_server("localhost", config->servers->port); + break; + + START: + start_daemon(); + break; + + RESTART: + restart_daemon(); + + STOP: + stop_daemon(); + + default: + return 2; + } - start_server("localhost", config->servers->port); return 0; } diff --git a/httpd/src/utils/files/files.c b/httpd/src/utils/files/files.c index 3b1b756..0e84278 100644 --- a/httpd/src/utils/files/files.c +++ b/httpd/src/utils/files/files.c @@ -1,12 +1,12 @@ #include "files.h" -#include +// #include #include -#include "../string/string.h" +// #include "../string/string.h" -bool file_exists(const char *path) -{} +// bool file_exists(const char *path) +// {} int is_directory(const char *path) { @@ -18,21 +18,21 @@ int is_directory(const char *path) } // TODO handle logging -struct string *get_file_content(const char *path) -{ - // Open file - FILE *stream = fopen(path, "r"); - if (stream == NULL) - return NULL; +// 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; - } +// // 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))) -} +// int nread; +// while ((fgets(buf, BUFFER_SIZE, stream))) +// } diff --git a/httpd/src/utils/files/files.h b/httpd/src/utils/files/files.h index e338d91..7a40c2b 100644 --- a/httpd/src/utils/files/files.h +++ b/httpd/src/utils/files/files.h @@ -9,6 +9,7 @@ #include #include +#include // === Functions @@ -61,10 +62,19 @@ bool check_filename(struct string* path); /* * @brief * - * @param path + * @param filename * * @return */ bool sanitize_filename(struct string* filename); +/* + * @brief + * + * @param path + * + * @return + */ + ssize_t get_file_content_size(const char* path); + #endif // ! FILES_H diff --git a/httpd/src/utils/string/string.c b/httpd/src/utils/string/string.c index 2b62c90..b5fe69e 100644 --- a/httpd/src/utils/string/string.c +++ b/httpd/src/utils/string/string.c @@ -85,3 +85,17 @@ void string_destroy(struct string *str) free(str); } } + +char *string_to_charptr(struct string *str) +{ + if (str == NULL || str->data == NULL) + return NULL; + + char *res = realloc(str->data, str->size + 1); + if (res == NULL) + return NULL; + + res[str->size] = '\0'; + + return res; +} diff --git a/httpd/src/utils/string/string.h b/httpd/src/utils/string/string.h index 74572c0..62bca63 100644 --- a/httpd/src/utils/string/string.h +++ b/httpd/src/utils/string/string.h @@ -56,4 +56,13 @@ void string_to_lowercase(struct string *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); + #endif /* ! STRING_H */