136 lines
3.6 KiB
C
136 lines
3.6 KiB
C
#include "http_server.h"
|
|
#include "http_content_type.h"
|
|
#include "http_request.h"
|
|
#include "http_response.h"
|
|
#include "http_status.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
|
|
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;
|
|
|
|
if (setsockopt(s->server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
|
|
return HTTP_SRS_SETSOCKOPT_FAILED;
|
|
|
|
if (s->address == NULL) {
|
|
printf("Setting server address\n");
|
|
s->address = malloc(sizeof(*s->address));
|
|
s->address->sin_family = AF_INET;
|
|
s->address->sin_addr.s_addr = INADDR_ANY;
|
|
s->address->sin_port = htons(s->port);
|
|
}
|
|
|
|
if (bind(s->server_fd, (struct sockaddr *)s->address, sizeof(*s->address)) <
|
|
0)
|
|
return HTTP_SRS_BIND_FAILED;
|
|
|
|
if (listen(s->server_fd, s->backlog) < 0)
|
|
return HTTP_SRS_LISTEN_FAILED;
|
|
|
|
return HTTP_SRS_SETUP;
|
|
}
|
|
|
|
HttpServerRunStatus http_server_run(HttpServer *s) {
|
|
int client_fd;
|
|
ssize_t valread;
|
|
|
|
socklen_t addrlen = sizeof(*s->address);
|
|
|
|
if ((client_fd =
|
|
accept(s->server_fd, (struct sockaddr *)s->address, &addrlen)) < 0)
|
|
return HTTP_SRS_ACCEPT_FAILED;
|
|
|
|
// 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://<host>:<port>/cgi/<script>.py
|
|
else if (!strncmp(req->path, "/cgi/", strlen("/cgi/"))) {
|
|
exit(cgi(req->path, client_fd));
|
|
}
|
|
// Sinon l'url donnée peut correspondre à un fichier se trouvant dans
|
|
// l'arborescence du "current working directory".
|
|
else {
|
|
char path[] = ".";
|
|
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);
|
|
}
|
|
|
|
close(client_fd);
|
|
return HTTP_SRS_RUNNING;
|
|
}
|
|
|
|
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);
|
|
|
|
if (s->address != NULL)
|
|
free(s->address);
|
|
|
|
return HTTP_SRS_STOPPED;
|
|
}
|