Skip to content

This is the best fucking web server of all time.

Notifications You must be signed in to change notification settings

toukoum/42Cursus-webserv

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Webserv C++ HTTP

Webserv - Serveur Web en C++98 125/100 ✅

Ce projet est un serveur HTTP Ă©crit en C++98 (pour LINUX). Ce serveur est compatible avec le protocole HTTP/1.1.

Demonstration:

Webserv


Log of the Webserv

How to use

⚠ PrĂ©requis: Fonctionne uniquement sur Linux. Vous devez avoir make et g++ installĂ©s sur votre machine.

  1. Clonez le dépÎt avec
git clone https://github.com/lxup/42Cursus-webserv.git webserv
  1. Accédez au dépÎt avec
cd webserv
  1. Exécutez
make && ./webserv config/good/webserv.conf
  1. Ouvrez un navigateur et allez sur
http://localhost:3434

ou utilisez curl http://localhost:3434

Vous pouvez tester de modifier le fichier de configuration, en modifiant par exemple les ports d'ecoute ou les fichiers a servir.

Objectif

L'objectif est de comprendre et d'implĂ©menter les fonctionnalitĂ©s de base d'un serveur HTTP, de la rĂ©ception de requĂȘtes Ă  la rĂ©ponse appropriĂ©e, en passant par la gestion des diffĂ©rents codes de statut HTTP. Il doit etre NON BLOQUANT (en gros on doit pouvoir servir plusieurs clients en meme temps).

🔍 Qu'est-ce que HTTP? HTTP (Hypertext Transfer Protocol) est le protocole utilisĂ© pour la communication entre un client (navigateur web) et un serveur.
En gros quand tu te connectes a youtube.com, tu demandes plein de fichiers a un server avec des requete et le server te les renvoit

Schema de BlocServers

Liens Utiles

Pipeline de notre WEBSERV

0ïžâƒŁ Etape 0: Analyse du Fichier de Configuration

  • Analyser le fichier de configuration pour dĂ©terminer les blocs de serveurs et leurs directives.
  • CrĂ©er une structure de donnĂ©es pour stocker les informations du fichier de configuration.

Webserv

Sur ce schéma, on peut voir un exemple de fichier de configuration.
Le server va Ă©couter sur le port 80.
Le root du serveur est le dossier /www/site/.
Par défaut, le serveur va servir le fichier index.html.

1ïžâƒŁ Etape 1: Initialisation du Serveur

  • CrĂ©er un socket pour Ă©couter les connexions entrantes. (listen socket)
  • CrĂ©er une instance epoll pour surveiller les sockets.
  • Ajouter le socket d'Ă©coute Ă  l'instance epoll.
  • Attendre les Ă©vĂ©nements d'entrĂ©e/sortie sur les sockets. (Avec epoll_wait())

Webserv

Sur ce schéma, on peut voir un exemple de fichier de configuration.
Le server va Ă©couter sur le port 80.
Le root du serveur est le dossier /www/site/.
Par défaut, le serveur va servir le fichier index.html.

Sur ce schema, on peut voir le serveur qui ecoute sur le port 80 et qui attend des connexions entrantes. Le listener socket est ajoute a la Pool de epoll et a le numero 3.

2ïžâƒŁ Etape 2: New Client Connection

  • Accepter une nouvelle connexion entrante.
  • Ajouter le nouveau socket Ă  l'instance epoll.
  • Attendre les Ă©vĂ©nements d'entrĂ©e/sortie sur les sockets. (Avec epoll_wait())

Webserv

Sur ce schema, on peut voir les 3 etape de l'ajout d'un nouveau client.
1- Le client se connecte au serveur sur le listener socket
2- Le serveur accepte la connexion et cree un nouveau socket pour le client
3- Le nouveau socket est ajoute a la pool de epoll et le serveur attend des evenements sur ce socket (mais aussi toujours le listener socket)

3ïžâƒŁ Etape 3: RequĂȘte & Response HTTP

Derniere etape (mashalla tro bo le schema)

  • Un socket de connexion est present entre le client et le serveur.

Step 3

Sur ce schema, les 5 etape de la gestion de requete sont present.
1- Le client envoie une requete au serveur sur le socket de connexion (le socket 4)
2- Le serveur recoit la requete et comme il s'agit d'un client deja connu, il parse la requete
3- Le serveur traite la requete, verifiant la validite de la requete, si c'est une requete chunk, un cgi, ... puis il genere une reponse
4- Le genere la reponse en recherchant le fichier demander, en executant un cgi, ...
5- Le serveur envoie la reponse au client sur le socket de connexion

Youpii c'est fini, le client a recu sa reponse et le serveur attend une nouvelle requete.

Gestion des BlocServers du fichier de configuration

Le webserver doit ĂȘtre capable de gĂ©rer plusieurs blocs de configuration, chacun correspondant Ă  un serveur virtuel. Chaque bloc de configuration contient des directives spĂ©cifiques qui dĂ©finissent le comportement du serveur.

Schema de BlocServers

Le fichier config.conf montre que Webserv écoute sur 3 ports différents: 80, 1313 et 9090.

Pour chaque requĂȘte entrante, Webserv doit dĂ©terminer quel bloc de configuration utiliser. Voici quelques exemples :

  • http://jul.com:80 ➔ Bloc Bleu 🟩 (servername et port correspondants)
  • http://bob.com:1313 ➔ Bloc Rouge đŸŸ„
  • http://existepas.com:1313 ➔ Bloc Vert đŸŸ©(bloc par dĂ©faut)
  • http://nul.com:4321 ➔ Pas de rĂ©ponse âŹœïž (port non Ă©coutĂ©)

Pour repertorier tout ces blocs server, on a utiliser une map avec comme cle les couple ip:port et comme valeur un vecteur de blocs server.


Ressources

✅ Fichier de Configuration du Serveur Web

Ceci est la documentation et les rÚgles pour le fichier de configuration du serveur web. Ce sont des rÚgles grandement inspirées de nginx. Nous avons adapté quelques rÚgles à notre convenance.

RĂšgles GĂ©nĂ©rales ⚠

  • Les lignes commençant par # sont des commentaires. Les commentaires doivent ĂȘtre sur une ligne sĂ©parĂ©e et ne peuvent pas ĂȘtre mĂ©langĂ©s avec une directive.
  • Il est interdit d'avoir deux blocs location avec le mĂȘme chemin (path) dans un bloc server.
  • Un bloc server peut contenir plusieurs server_name et plusieurs listen (ip:port).
  • Deux blocs server ne peuvent pas avoir le mĂȘme server_name.
  • Deux blocs server peuvent partager le mĂȘme listen (ip:port).

Structure des Blocs

server {
    ...
    location {
        ...
    }
    ...
}

DĂ©tails des Directives ✅

Le tableau ci-dessous résume les directives disponibles dans le fichier de configuration, y compris leur duplicabilité, le nombre de paramÚtres autorisés, et leurs valeurs par défaut.

Directive Bloc Duplication Nb ParamĂštres Valeur par DĂ©faut Description Exemple
server N/A DUP 0 none DĂ©finit un bloc de configuration pour un serveur web virtuel. server { ... }
location server DUP 1 none Définit un bloc de configuration pour une URL spécifique. location / { ... }
listen server DUP 1 ip: 0.0.0.0 port: 80 DĂ©finit l'adresse IP et le port sur lequel le serveur web doit Ă©couter les requĂȘtes. listen 80;, listen 127.0.0.1:8080;
server_name server DUP -1 localhost Définit le(s) nom(s) de domaine (host) sur lequel le serveur web doit répondre. server_name louis.com;
error_page server DUP -1 /var/www/error.html Définit les pages d'erreur personnalisées. La syntaxe est stricte : error_page CODE /path/to/file;. error_page 404 /404.html;
root server/location NODUP 1 /var/www/html DĂ©finit le rĂ©pertoire racine du serveur web (ou du bloc location). Dans un bloc location, cette directive surcharge celle du bloc server et ne peut pas ĂȘtre utilisĂ©e avec alias. root /var/www/html;
index server/location DUP -1 index.html Définit les fichiers index par défaut (ou du bloc location). Dans un bloc location, cette directive surcharge celle du bloc server. index index.html index.htm;
client_max_body_size server NODUP 1 1 (Mo) Définit la taille maximale des données que le serveur web peut recevoir (en Mo). client_max_body_size 10;
alias location NODUP 1 none DĂ©finit un alias pour un rĂ©pertoire. Ne peut pas ĂȘtre utilisĂ© avec root. alias /var/www/images/;
return location DUP 2 none Définit une rÚgle de réécriture d'URL. return 301 https://github.com/toukoum;
autoindex location NODUP 1 off Active ou désactive l'indexation automatique des répertoires. Ne doit pas coexister avec la directive index dans le bloc location. autoindex on;
allow_methods location NODUP 0..3 GET DELETE POST Définit les méthodes HTTP autorisées. allow_methods GET DELETE POST;
cgi_extension location DUP 2 none Définit l'extension qui sera mappée à un script CGI. cgi_extension .php /var/www/cgi-bin/php-cgi;
upload_path location NODUP 1 /var/www/upload Définit le répertoire de destination des fichiers uploadés. upload_path /var/www/images;

Exemples de Configuration

server {
    listen 80;
    server_name louis.com;
    root /var/www/html;
    index index.html index.htm;
    error_page 404 /404.html;

    location / {
        root /var/www/html;
        index index.html index.htm;
    }

    location /upload {
        allow_methods POST;
        upload_path /var/www/upload;
        cgi_extension .php /var/www/cgi-bin/php-cgi;
    }
}
✅ Server HTTP Ultra simplifie Ces 50 lignes de code permettent de creer un serveur HTTP qui repond a une requete avec un message HTML. Attention, rien est protege dans ce code, il permet juste de montrer comment fonctionne les fonctions les plus importantes d'un serveur HTTP.

Pour essayer ce code:

  1. Rendez vous dans le dossier simpleServer
  2. Compilez le code avec g++ -o server simpleServer.cpp
  3. Lancez le serveur avec ./server
  4. Ouvrez un navigateur et allez sur http://localhost:1234 / sinon vous pouvez utiliser curl http://localhost:1234

Vous devrez obtenir quelque chose comme ceci: Serveur HTTP

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 1234
#define BUFFER_SIZE 4096

int server()
{

  int fdSocket = socket(AF_INET, SOCK_STREAM, 0);

  sockaddr_in address;
  address.sin_family = AF_INET;
  address.sin_port = htons(PORT);
  address.sin_addr.s_addr = htonl(INADDR_ANY);

  bind(fdSocket, (const sockaddr *)(&address), sizeof(address));

  listen(fdSocket, 10);

  bool active = true;
  int connection;
  while (active)
  {
    unsigned long resultLen = sizeof(sockaddr);
    std::cout << "Listening on Port: " << PORT << std::endl;
    connection = accept(fdSocket, (struct sockaddr *)(&address), (socklen_t *)&resultLen);

    char buffer[BUFFER_SIZE];
    ssize_t bytesRead = read(connection, buffer, BUFFER_SIZE);
    std::cout << "Le message fait: " << bytesRead << " characteres" << std::endl;
    std::cout << buffer << std::endl;

    std::string content = "<h1>Bonjour, je suis un serveur HTTP tout simple!</h1>";
    std::string response = "HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length: " + std::to_string(content.length()) + "\n\n" + content;
    send(connection, response.c_str(), response.size(), 0);
    close(connection);
  }

  close(fdSocket);

  return (EXIT_SUCCESS);
}

int main()
{
  server();
  return 0;
}

Ce code permet de comprendre les principales fonctions d'un serveur HTTP. Il crĂ©e un serveur qui Ă©coute sur le port 1234 et renvoie un message HTML simple Ă  chaque requĂȘte. Il utilise notamment les fonction socket, bind, listen, accept, read et send pour gĂ©rer les connexions entrantes et sortantes.

✅ Structure d'une RequĂȘte HTTP

Une requĂȘte HTTP permet au client de demander une ressource au serveur.

  1. Ligne de RequĂȘte

    • MĂ©thode: Action Ă  rĂ©aliser sur le serveur (GET, POST, DELETE, etc.)
    • URI: Adresse de la ressource demandĂ©e sur le serveur.
    • Version HTTP: Version du protocole HTTP utilisĂ©e (HTTP/1.1, HTTP/1.0).

    Exemple:

    GET /img/logo.jpg HTTP/1.0
  2. En-tĂȘtes de la RequĂȘte
    Paires clĂ©-valeur fournissant des informations sur la requĂȘte ou le client.

    Exemple:

    Host: abc.com
    Accept: text/html
    Cookie: _ga=GA1.2.407.167011
  3. Corps de la RequĂȘte (pour POST et PUT uniquement)
    Contient les données que le client souhaite transmettre au serveur.

    Exemple:

    name=John+Doe&age=30&city=New+York

    au format query string

✅ Structure d'une RĂ©ponse HTTP

La rĂ©ponse HTTP est ce que le serveur renvoie aprĂšs avoir reçu une requĂȘte.

  1. Ligne de Statut

    • Version HTTP
    • Code de Statut: Exemples courants :
      • 200 OK : RequĂȘte traitĂ©e avec succĂšs.
      • 404 Not Found : Ressource introuvable.
      • 500 Internal Server Error : Erreur interne du serveur.
    • Message: Phrase associĂ©e au code de statut.

    Exemple:

    HTTP/1.1 200 OK
  2. En-tĂȘtes de RĂ©ponse
    Paires clé-valeur fournissant des informations sur la réponse ou le serveur.

    Exemple:

    Content-Encoding: gzip
    Content-Length: 342
    Date: Sat, 08 Jan 2022 10:52:28 GMT
  3. Corps de la RĂ©ponse
    Contient la rĂ©ponse elle-mĂȘme, telle que la page HTML demandĂ©e.

    <html>
      <h1>
       Ceci est un page html
     </h1>
    </html>
    
    
✅ EPOLL fonction()
Pour notre Webserv, nous avons choisie d’utiliser la fonction epoll(). Les autres choix qui s'offrait a nous etait select() et poll() mais epoll() est de loin la plus performante (voir le medium que j'ai link en haut de la page).

💡 fonction pour surveiller les des fd (sockets) afin de savoir quand les operation de Input/Output peuvent etre effectuees sans bloquer le programme.

  • Comment l’utiliser ?

    int epoll_create(int size); // pour creer une instance et retourne le
    // fd associe
    
    int epoll_create1(int flags); // flags utile comme EPOLL_CLOEXEC 
    // qui ferme automatiquement le descripteur de epoll lors de l'execution d'un processus enfant, new version de epoll_create

    Une fois l’instance creer, on peut ajouter, modifier ou supprimer des descripteurs a surveiller

    int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    
    // epfd : Le descripteur de fichier de l'instance epoll.
    // op : L'opération à effectuer (EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL).
    // fd : Le descripteur de fichier Ă  surveiller.
    // event : Une structure epoll_event qui spĂ©cifie les Ă©vĂ©nements Ă  surveiller, comme EPOLLIN (donnĂ©es disponibles en lecture), EPOLLOUT (prĂȘt Ă  Ă©crire), etc.

    la structure d’event a ecouter se defini comme cela:

    struct epoll_event ev;
    ev.events = EPOLLIN; // ou EPOLLOUT out 
    ev.data.fd = server_fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, server_fd, &ev);
    
    • Flag d’evenements

      EPOLLIN ⇒ ****quand un fd est pret pour la lecture (ya des truc a lire sur le socket)

      EPOLLOUT ⇒ quand un fd est pret pour l’ecriture (en gros tu peux envoyer ta requete http sans que l’appel de send() soit bloquant)

      EPOLLERR ⇒ quand un fd a une erreur (ex: connection reset)

      EPOLLHUP ⇒ quand un fd est ferme par l’autre cote (ex: le client ferme son navigateur)

      nous pour webserv on va utiliser la combinaison de EPOLLIN + EPOLLOUT

    Pour recevoir une “notification” quand un evenement arrive:

    int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);
    // evlist : Un tableau pour stocker les Ă©vĂ©nements prĂȘts.
    // maxevents : Le nombre maximal d'événements à traiter.
    // timeout : Temps en millisecondes pour bloquer l'attente.

    epoll_wait bloquera jusqu'à ce qu'un événement se produise ou jusqu'à ce que le délai expire. Si timeout est -1, il attend indéfiniment.

✅ RequĂȘtes Chunked et DĂ©limitation

Les requĂȘtes chunked permettent d'envoyer des donnĂ©es en plusieurs morceaux de taille variable.

Fonctionnement des RequĂȘtes Chunked Chaque chunk suit le format : `[taille du chunk en hexadĂ©cimal]\r\n[donnĂ©es du chunk]\r\n`. La fin de la transmission est indiquĂ©e par `0\r\n\r\n`. Exemple de chunk: ```http 4\r\nWiki\r\n 5\r\npedia\r\n 0\r\n\r\n ```

DĂ©limitation des RequĂȘtes HTTP

Une requĂȘte HTTP standard se termine soit :

  • Par une ligne vide aprĂšs les en-tĂȘtes (si aucun corps n'est prĂ©sent).
  • Par la rĂ©ception de l'intĂ©gralitĂ© des donnĂ©es spĂ©cifiĂ©es par Content-Length.
  • Par le chunk de fin (pour les requĂȘtes chunked).
✅ Codes de Statut HTTP

Les codes de statut HTTP indiquent le rĂ©sultat d'une requĂȘte HTTP.

  • đŸ”” 1xx : Informational - RequĂȘte reçue, traitement en cours.
  • 🟱 2xx : Success - RequĂȘte reçue, comprise et acceptĂ©e avec succĂšs.
  • 🟡 3xx : Redirect - Une action supplĂ©mentaire doit ĂȘtre effectuĂ©e pour complĂ©ter la requĂȘte.
  • 🔮 4xx : Client Error - La requĂȘte contient une erreur qui empĂȘche le serveur de la traiter.
  • ⚫ 5xx : Server Error - Le serveur a Ă©chouĂ© Ă  traiter une requĂȘte apparemment valide.

About

This is the best fucking web server of all time.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 42.7%
  • JavaScript 15.4%
  • HTML 13.8%
  • CSS 12.6%
  • Python 12.5%
  • Makefile 1.1%
  • Other 1.9%