From cdae291dd8b6146473e70dcf5c7e55b0868e18c7 Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Wed, 20 Nov 2024 00:04:49 +0100 Subject: [PATCH] Comments --- cgi/hello.py | 1 - http/http_request.c | 22 ++++++-- http/http_request.h | 8 +++ http/http_response.c | 1 + http/http_response.h | 30 ++++++----- http/http_server.c | 117 +++++++++++++++++++++++++++---------------- http/http_server.h | 20 ++++++-- main.c | 17 ++++--- 8 files changed, 145 insertions(+), 71 deletions(-) diff --git a/cgi/hello.py b/cgi/hello.py index db6708b..f19d84d 100644 --- a/cgi/hello.py +++ b/cgi/hello.py @@ -1,4 +1,3 @@ - code = 200 ct = "text/html" content = "

Bonjour!

" diff --git a/http/http_request.c b/http/http_request.c index dac7a76..e75b905 100644 --- a/http/http_request.c +++ b/http/http_request.c @@ -1,5 +1,4 @@ #include "http_request.h" -#include "http_method.h" #include #include #include @@ -57,18 +56,28 @@ void http_request_set(HttpRequest *__req, char *key, char *value) { } HttpRequest *handle_request(char *__req) { - printf("%s\n", __req); + // printf("%s\n", __req); HttpRequest *request = malloc(sizeof(HttpRequest)); unsigned int line_count = 0; char *line_start = __req; char *line_end; + // Nous allons de ligne en ligne + // line_end est une chaîne de caractères allant de la nouvelle ligne trouvée + // jusqu'à la fin de la requête. + // line_start est aussi une chaîne de caractères qui va du début de la + // requête (au fil de la boucle, elle deviendra le précédent line_end) + // jusqu'à line_end. while ((line_end = strchr(line_start, '\n')) != NULL) { line_count++; + // Nous pouvons récupérer la longueur la ligne que nous allons traiter + // en faisant cette simple opération. size_t l_length = line_end - line_start; char line[l_length]; strncpy(line, line_start, l_length); line[l_length - 1] = 0; + // La première ligne HTTP est toujours quelque chose suivant ce format: + // HTTP/ if (line_count == 1) { char method[10] = {0}; char *path = malloc(sizeof(char) * 1024); @@ -76,22 +85,29 @@ HttpRequest *handle_request(char *__req) { free(path); return NULL; } + // Transforme le string donné depuis la requête en int défini dans + // un enum, afin de se faciliter la vie plus tard. request->method = get_http_method(method); request->path = path; } else { + // Pour ce qui est du reste, les options fonctionnent comme un + // "key-value pair": : char key[100] = {0}; char *colon_pos = strchr(line, ':'); if (colon_pos != NULL) { size_t k_length = colon_pos - line; strncpy(key, line, k_length); + // Il faut enlever le ':' + ' ' + '\r' => 3 size_t v_length = l_length - k_length - 3; char *value = malloc(sizeof(char) * v_length); + // colon_pos comprend le ':' + ' ' qui sont indésirables, donc + // faut se décaler de 2, le '\r' ne sera pas pris en compte + // puisque la chaîne value est trop petite pour que ça rentre. strcpy(value, colon_pos + 2); http_request_set(request, key, value); } } - // TODO: Analyze line line_start = line_end + 1; } diff --git a/http/http_request.h b/http/http_request.h index 71fded7..37f9418 100644 --- a/http/http_request.h +++ b/http/http_request.h @@ -3,6 +3,9 @@ #include "http_content_type.h" #include "http_method.h" + +// Beaucoup d'options manques ou d'informations ne seront pas stockées. + typedef struct { HttpMethod method; char *path; @@ -18,8 +21,13 @@ typedef struct { char *connection; } HttpRequest; +// Parse la requête HTTP, afin de la manipuler facilement. HttpRequest *handle_request(char *__req); + +// Imprime dans le stdout la requête HTTP. void print_request(const HttpRequest *__req); + +// Libère tout ce qui est en rapport avec HttpRequest. void free_request(HttpRequest *__req); #endif diff --git a/http/http_response.c b/http/http_response.c index dcb473e..064ecf1 100644 --- a/http/http_response.c +++ b/http/http_response.c @@ -5,6 +5,7 @@ #include #include #include +#include #include bool construct_response(HttpResponse __res, char *out) { diff --git a/http/http_response.h b/http/http_response.h index a35de32..bf6eae7 100644 --- a/http/http_response.h +++ b/http/http_response.h @@ -6,28 +6,34 @@ #include "http_status.h" #include #include -#include typedef struct { - HttpStatus status_code; - HttpContentType content_type; - size_t content_length; - char *body; - bool body_in_heap; + HttpStatus status_code; // Code HTTP, ex. 200 + HttpContentType content_type; // ex. HTTP_CT_HTML => text/html + size_t content_length; // Taille du contenu en bytes + char *body; // Contenu de la réponse + bool body_in_heap; // Si le contenu est défini dans le heap, alors on pourra + // libérer sa mémoire } HttpResponse; -// It will create a string respecting the Hypertext Transfer Protocol. -// -// To then be sent to the client. +// Cela crée une chaîne de caractères respectant le protocole HTTP selon la +// réponse donnée. bool construct_response(HttpResponse __res, char *out); -// Respond a http response to the client (clientfd); +// Envoie la réponse au client. void http_respond(HttpResponse *__res, int clientfd); -// Read a file from a specified path + +// Lit un fichier et renvoie son contenu. char *read_file(const char *__path); -// Given a path will return a HttpResponse + +// Lit un fichier à un chemin donné et forme une réponse HTTP. HttpResponse *from_file(const char *__path); + +// Libère tout ce qui est nécessaire en rapport avec la structure HttpResponse. void free_response(HttpResponse *__res); + +// Execute un programme python à un chemin donné et envoie son STDOUT au socket +// client. HttpServerRunStatus cgi(const char *__path, int clientfd); #endif diff --git a/http/http_server.c b/http/http_server.c index 7c3e01b..6707e78 100644 --- a/http/http_server.c +++ b/http/http_server.c @@ -3,17 +3,15 @@ #include "http_request.h" #include "http_response.h" #include "http_status.h" -#include #include #include #include -#include -#include #include #include HttpServerRunStatus http_server_setup(HttpServer *s) { int opt = 1; + s->pid = 0; if ((s->server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) return HTTP_SRS_SOCKET_FAILED; @@ -33,7 +31,7 @@ HttpServerRunStatus http_server_setup(HttpServer *s) { 0) return HTTP_SRS_BIND_FAILED; - if (listen(s->server_fd, s->workers) < 0) + if (listen(s->server_fd, s->backlog) < 0) return HTTP_SRS_LISTEN_FAILED; return HTTP_SRS_SETUP; @@ -49,54 +47,85 @@ HttpServerRunStatus http_server_run(HttpServer *s) { accept(s->server_fd, (struct sockaddr *)s->address, &addrlen)) < 0) return HTTP_SRS_ACCEPT_FAILED; - char request[BUFSIZ] = {0}; - - if ((valread = recv(client_fd, request, 1024 - 1, MSG_PEEK)) < 0) - return HTTP_SRS_READ_FAILED; - - HttpRequest *req = handle_request(request); - if (req == NULL) - return HTTP_SRS_HANDLE_REQUEST_FAILED; - - HttpResponse *res; - - if (!strcmp(req->path, "/")) { - res = from_file("./" DEFAULT_HTML); - } else if (!strncmp(req->path, "/cgi/", strlen("/cgi/"))) { - return cgi(req->path, client_fd); - } else { - char path[] = "."; - strcat(path, req->path); - res = from_file(path); - } - // print_request(req); - // free_request(req); - if (res == NULL) { - char *body = malloc(sizeof(char) * 128); - body = "

404 Not " - "Found

"; - HttpResponse __res = { - .status_code = HTTP_NOT_FOUND, - .content_length = strlen(body), - .content_type = HTTP_CT_HTML, - .body = body, - .body_in_heap = false, // Lorsqu'on essaie de libérer le body - // sur cette instance, le server crash. - // Alors, on ne le libère pas et ça cause - // des memory leaks. - }; - res = malloc(sizeof(__res)); - *res = __res; + // Après chaque acceptation, nous créons un processus enfant pour gérer la + // requête, afin de pouvoir répondre à plusieurs requêtes simultanément. + s->pid = fork(); + if (s->pid < 0) { + return HTTP_SRS_FORK_FAILED; + } + + if (s->pid == 0) { + + char request[BUFSIZ] = {0}; + + if ((valread = recv(client_fd, request, 1024 - 1, MSG_PEEK)) < 0) + exit(HTTP_SRS_READ_FAILED); + + // L'objectif est de parser la requête dans une structure afin de non + // seulement récuperer les informations utiles, mais aussi pour les + // manipuler aisément. + HttpRequest *req = handle_request(request); + if (req == NULL) + exit(HTTP_SRS_HANDLE_REQUEST_FAILED); + + HttpResponse *res; + + // Si le chemin est la racine, nous retournons le fichier par défaut + // HTML. + if (!strcmp(req->path, "/")) { + res = from_file("./" DEFAULT_HTML); + } + // Si on cherche à faire tourner un script python, nous pouvons le faire + // en allant à http://:/cgi/