Comments
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
code = 200
|
code = 200
|
||||||
ct = "text/html"
|
ct = "text/html"
|
||||||
content = "<!DOCTYPE html><html><body><h1>Bonjour!</h1></body></html>"
|
content = "<!DOCTYPE html><html><body><h1>Bonjour!</h1></body></html>"
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "http_request.h"
|
#include "http_request.h"
|
||||||
#include "http_method.h"
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -57,18 +56,28 @@ void http_request_set(HttpRequest *__req, char *key, char *value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HttpRequest *handle_request(char *__req) {
|
HttpRequest *handle_request(char *__req) {
|
||||||
printf("%s\n", __req);
|
// printf("%s\n", __req);
|
||||||
HttpRequest *request = malloc(sizeof(HttpRequest));
|
HttpRequest *request = malloc(sizeof(HttpRequest));
|
||||||
unsigned int line_count = 0;
|
unsigned int line_count = 0;
|
||||||
char *line_start = __req;
|
char *line_start = __req;
|
||||||
char *line_end;
|
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) {
|
while ((line_end = strchr(line_start, '\n')) != NULL) {
|
||||||
line_count++;
|
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;
|
size_t l_length = line_end - line_start;
|
||||||
char line[l_length];
|
char line[l_length];
|
||||||
|
|
||||||
strncpy(line, line_start, l_length);
|
strncpy(line, line_start, l_length);
|
||||||
line[l_length - 1] = 0;
|
line[l_length - 1] = 0;
|
||||||
|
// La première ligne HTTP est toujours quelque chose suivant ce format:
|
||||||
|
// <METHOD> <PATH> HTTP/<VERSION>
|
||||||
if (line_count == 1) {
|
if (line_count == 1) {
|
||||||
char method[10] = {0};
|
char method[10] = {0};
|
||||||
char *path = malloc(sizeof(char) * 1024);
|
char *path = malloc(sizeof(char) * 1024);
|
||||||
@@ -76,22 +85,29 @@ HttpRequest *handle_request(char *__req) {
|
|||||||
free(path);
|
free(path);
|
||||||
return NULL;
|
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->method = get_http_method(method);
|
||||||
request->path = path;
|
request->path = path;
|
||||||
} else {
|
} else {
|
||||||
|
// Pour ce qui est du reste, les options fonctionnent comme un
|
||||||
|
// "key-value pair": <KEY>: <VALUE>
|
||||||
char key[100] = {0};
|
char key[100] = {0};
|
||||||
char *colon_pos = strchr(line, ':');
|
char *colon_pos = strchr(line, ':');
|
||||||
if (colon_pos != NULL) {
|
if (colon_pos != NULL) {
|
||||||
size_t k_length = colon_pos - line;
|
size_t k_length = colon_pos - line;
|
||||||
strncpy(key, line, k_length);
|
strncpy(key, line, k_length);
|
||||||
|
|
||||||
|
// Il faut enlever le ':' + ' ' + '\r' => 3
|
||||||
size_t v_length = l_length - k_length - 3;
|
size_t v_length = l_length - k_length - 3;
|
||||||
char *value = malloc(sizeof(char) * v_length);
|
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);
|
strcpy(value, colon_pos + 2);
|
||||||
http_request_set(request, key, value);
|
http_request_set(request, key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Analyze line
|
|
||||||
line_start = line_end + 1;
|
line_start = line_end + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#include "http_content_type.h"
|
#include "http_content_type.h"
|
||||||
#include "http_method.h"
|
#include "http_method.h"
|
||||||
|
|
||||||
|
// Beaucoup d'options manques ou d'informations ne seront pas stockées.
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HttpMethod method;
|
HttpMethod method;
|
||||||
char *path;
|
char *path;
|
||||||
@@ -18,8 +21,13 @@ typedef struct {
|
|||||||
char *connection;
|
char *connection;
|
||||||
} HttpRequest;
|
} HttpRequest;
|
||||||
|
|
||||||
|
// Parse la requête HTTP, afin de la manipuler facilement.
|
||||||
HttpRequest *handle_request(char *__req);
|
HttpRequest *handle_request(char *__req);
|
||||||
|
|
||||||
|
// Imprime dans le stdout la requête HTTP.
|
||||||
void print_request(const HttpRequest *__req);
|
void print_request(const HttpRequest *__req);
|
||||||
|
|
||||||
|
// Libère tout ce qui est en rapport avec HttpRequest.
|
||||||
void free_request(HttpRequest *__req);
|
void free_request(HttpRequest *__req);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
bool construct_response(HttpResponse __res, char *out) {
|
bool construct_response(HttpResponse __res, char *out) {
|
||||||
|
|||||||
@@ -6,28 +6,34 @@
|
|||||||
#include "http_status.h"
|
#include "http_status.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HttpStatus status_code;
|
HttpStatus status_code; // Code HTTP, ex. 200
|
||||||
HttpContentType content_type;
|
HttpContentType content_type; // ex. HTTP_CT_HTML => text/html
|
||||||
size_t content_length;
|
size_t content_length; // Taille du contenu en bytes
|
||||||
char *body;
|
char *body; // Contenu de la réponse
|
||||||
bool body_in_heap;
|
bool body_in_heap; // Si le contenu est défini dans le heap, alors on pourra
|
||||||
|
// libérer sa mémoire
|
||||||
} HttpResponse;
|
} HttpResponse;
|
||||||
|
|
||||||
// It will create a string respecting the Hypertext Transfer Protocol.
|
// Cela crée une chaîne de caractères respectant le protocole HTTP selon la
|
||||||
//
|
// réponse donnée.
|
||||||
// To then be sent to the client.
|
|
||||||
bool construct_response(HttpResponse __res, char *out);
|
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);
|
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);
|
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);
|
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);
|
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);
|
HttpServerRunStatus cgi(const char *__path, int clientfd);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,17 +3,15 @@
|
|||||||
#include "http_request.h"
|
#include "http_request.h"
|
||||||
#include "http_response.h"
|
#include "http_response.h"
|
||||||
#include "http_status.h"
|
#include "http_status.h"
|
||||||
#include <asm-generic/socket.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
HttpServerRunStatus http_server_setup(HttpServer *s) {
|
HttpServerRunStatus http_server_setup(HttpServer *s) {
|
||||||
int opt = 1;
|
int opt = 1;
|
||||||
|
s->pid = 0;
|
||||||
|
|
||||||
if ((s->server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
if ((s->server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||||
return HTTP_SRS_SOCKET_FAILED;
|
return HTTP_SRS_SOCKET_FAILED;
|
||||||
@@ -33,7 +31,7 @@ HttpServerRunStatus http_server_setup(HttpServer *s) {
|
|||||||
0)
|
0)
|
||||||
return HTTP_SRS_BIND_FAILED;
|
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_LISTEN_FAILED;
|
||||||
|
|
||||||
return HTTP_SRS_SETUP;
|
return HTTP_SRS_SETUP;
|
||||||
@@ -49,54 +47,85 @@ HttpServerRunStatus http_server_run(HttpServer *s) {
|
|||||||
accept(s->server_fd, (struct sockaddr *)s->address, &addrlen)) < 0)
|
accept(s->server_fd, (struct sockaddr *)s->address, &addrlen)) < 0)
|
||||||
return HTTP_SRS_ACCEPT_FAILED;
|
return HTTP_SRS_ACCEPT_FAILED;
|
||||||
|
|
||||||
char request[BUFSIZ] = {0};
|
// 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.
|
||||||
if ((valread = recv(client_fd, request, 1024 - 1, MSG_PEEK)) < 0)
|
s->pid = fork();
|
||||||
return HTTP_SRS_READ_FAILED;
|
if (s->pid < 0) {
|
||||||
|
return HTTP_SRS_FORK_FAILED;
|
||||||
HttpRequest *req = handle_request(request);
|
}
|
||||||
if (req == NULL)
|
|
||||||
return HTTP_SRS_HANDLE_REQUEST_FAILED;
|
if (s->pid == 0) {
|
||||||
|
|
||||||
HttpResponse *res;
|
char request[BUFSIZ] = {0};
|
||||||
|
|
||||||
if (!strcmp(req->path, "/")) {
|
if ((valread = recv(client_fd, request, 1024 - 1, MSG_PEEK)) < 0)
|
||||||
res = from_file("./" DEFAULT_HTML);
|
exit(HTTP_SRS_READ_FAILED);
|
||||||
} else if (!strncmp(req->path, "/cgi/", strlen("/cgi/"))) {
|
|
||||||
return cgi(req->path, client_fd);
|
// L'objectif est de parser la requête dans une structure afin de non
|
||||||
} else {
|
// seulement récuperer les informations utiles, mais aussi pour les
|
||||||
char path[] = ".";
|
// manipuler aisément.
|
||||||
strcat(path, req->path);
|
HttpRequest *req = handle_request(request);
|
||||||
res = from_file(path);
|
if (req == NULL)
|
||||||
}
|
exit(HTTP_SRS_HANDLE_REQUEST_FAILED);
|
||||||
// print_request(req);
|
|
||||||
// free_request(req);
|
HttpResponse *res;
|
||||||
if (res == NULL) {
|
|
||||||
char *body = malloc(sizeof(char) * 128);
|
// Si le chemin est la racine, nous retournons le fichier par défaut
|
||||||
body = "<!DOCTYPE html><html><body><h1>404 Not "
|
// HTML.
|
||||||
"Found</h1></body></html>";
|
if (!strcmp(req->path, "/")) {
|
||||||
HttpResponse __res = {
|
res = from_file("./" DEFAULT_HTML);
|
||||||
.status_code = HTTP_NOT_FOUND,
|
}
|
||||||
.content_length = strlen(body),
|
// Si on cherche à faire tourner un script python, nous pouvons le faire
|
||||||
.content_type = HTTP_CT_HTML,
|
// en allant à http://<host>:<port>/cgi/<script>.py
|
||||||
.body = body,
|
else if (!strncmp(req->path, "/cgi/", strlen("/cgi/"))) {
|
||||||
.body_in_heap = false, // Lorsqu'on essaie de libérer le body
|
exit(cgi(req->path, client_fd));
|
||||||
// sur cette instance, le server crash.
|
}
|
||||||
// Alors, on ne le libère pas et ça cause
|
// Sinon l'url donnée peut correspondre à un fichier se trouvant dans
|
||||||
// des memory leaks.
|
// l'arborescence du "current working directory".
|
||||||
};
|
else {
|
||||||
res = malloc(sizeof(__res));
|
char path[] = ".";
|
||||||
*res = __res;
|
strcat(path, req->path);
|
||||||
|
res = from_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si jamais le fichier n'a pas été trouvé, alors nous retournons un 404
|
||||||
|
// NOT FOUND.
|
||||||
|
if (res == NULL) {
|
||||||
|
char *body = malloc(sizeof(char) * 128);
|
||||||
|
body = "<!DOCTYPE html><html><body><h1>404 Not "
|
||||||
|
"Found</h1></body></html>";
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nous envoyons la réponse au client.
|
||||||
|
http_respond(res, client_fd);
|
||||||
|
free_response(res);
|
||||||
|
exit(HTTP_SRS_RUNNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
http_respond(res, client_fd);
|
|
||||||
free_response(res);
|
|
||||||
close(client_fd);
|
close(client_fd);
|
||||||
return HTTP_SRS_RUNNING;
|
return HTTP_SRS_RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpServerRunStatus http_server_stop(HttpServer *s) {
|
HttpServerRunStatus http_server_stop(HttpServer *s) {
|
||||||
|
|
||||||
|
// Nous attendons que tous les processus enfant se termine avant de quitter
|
||||||
|
// le programme.
|
||||||
|
if (s->pid > 0) {
|
||||||
|
wait(NULL);
|
||||||
|
}
|
||||||
|
// Puis on ferme tout ce qui est nécessaire de fermer.
|
||||||
close(s->server_fd);
|
close(s->server_fd);
|
||||||
|
|
||||||
if (s->address != NULL)
|
if (s->address != NULL)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#ifndef HTTP_SERVER_H
|
#ifndef HTTP_SERVER_H
|
||||||
#define HTTP_SERVER_H
|
#define HTTP_SERVER_H
|
||||||
|
|
||||||
// #include <netinet/in.h>
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#ifndef DEFAULT_HTML
|
#ifndef DEFAULT_HTML
|
||||||
#define DEFAULT_HTML "index.html"
|
#define DEFAULT_HTML "index.html"
|
||||||
@@ -24,15 +24,25 @@ typedef enum {
|
|||||||
HTTP_SRS_WONT_HANDLE,
|
HTTP_SRS_WONT_HANDLE,
|
||||||
} HttpServerRunStatus;
|
} HttpServerRunStatus;
|
||||||
|
|
||||||
|
// Structure ayant pour objectif de stocker tout ce qui est nécessaire au bon
|
||||||
|
// fonctionnement du serveur.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int port;
|
int port; // Port du host
|
||||||
int server_fd;
|
int server_fd; // File descriptor du server
|
||||||
unsigned int workers;
|
unsigned int backlog; // Le nombre de clients possibles pouvant être en
|
||||||
struct sockaddr_in *address;
|
// attente.
|
||||||
|
struct sockaddr_in *address; // L'adresse décrivant une socket internet.
|
||||||
|
pid_t pid; // Le pid du parent en attente de requêtes.
|
||||||
} HttpServer;
|
} HttpServer;
|
||||||
|
|
||||||
|
// On setup le server, avec son file descriptor, ses options, le bind et le
|
||||||
|
// listen.
|
||||||
HttpServerRunStatus http_server_setup(HttpServer *s);
|
HttpServerRunStatus http_server_setup(HttpServer *s);
|
||||||
|
// Si tout est ok, on peut lancer le server, tant qu'il tourne, il peut
|
||||||
|
// recevoir des clients.
|
||||||
HttpServerRunStatus http_server_run(HttpServer *s);
|
HttpServerRunStatus http_server_run(HttpServer *s);
|
||||||
|
// Si pour quelques raisons il s'arrête, on free tout ce qu'il doit être
|
||||||
|
// free.
|
||||||
HttpServerRunStatus http_server_stop(HttpServer *s);
|
HttpServerRunStatus http_server_stop(HttpServer *s);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
17
main.c
17
main.c
@@ -1,18 +1,23 @@
|
|||||||
#include "http/http_server.h"
|
#include "http/http_server.h"
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
HttpServer server = {.port = 8080, .workers = 3};
|
// Nous créons une variable server contenant certaines informations
|
||||||
if (http_server_setup(&server) != HTTP_SRS_SETUP)
|
// sur le server HTTP que nous voulons utiliser, notamment son port.
|
||||||
exit(1);
|
HttpServer server = {.port = 8080, .backlog = 3};
|
||||||
|
// On setup le server, avec son file descriptor, ses options, le bind et le
|
||||||
|
// listen.
|
||||||
HttpServerRunStatus status;
|
HttpServerRunStatus status;
|
||||||
|
if ((status = http_server_setup(&server)) != HTTP_SRS_SETUP)
|
||||||
|
exit(status);
|
||||||
|
|
||||||
|
// Si tout est ok, on peut lancer le server, tant qu'il tourne, il peut
|
||||||
|
// recevoir des clients.
|
||||||
while ((status = http_server_run(&server)) == HTTP_SRS_RUNNING)
|
while ((status = http_server_run(&server)) == HTTP_SRS_RUNNING)
|
||||||
;
|
;
|
||||||
printf("Status: %d\n", status);
|
|
||||||
|
|
||||||
|
// Si pour quelques raisons il s'arrête, on free tout ce qu'il doit être
|
||||||
|
// free.
|
||||||
http_server_stop(&server);
|
http_server_stop(&server);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user