Stand proud Nginx, you are strong...
| Project | 42-webserv |
|---|---|
| Locked | 2026-01-19 08:55 CET:+0100 |
| Closed | 2026-01-19 09:00 CET:+0100 |
| Grade | 125% |
| Retries | 2 |
Le serveur implémente un modèle événementiel concurrent sans threads par connexion : il utilise epoll pour surveiller des événements sur des sockets et autres fds, et traite chaque événement de façon non bloquante. Cela permet de gérer un grand nombre de connexions un seul thread/processus.
- Epoll : mécanisme Linux pour surveiller efficacement un grand nombre de descripteurs et savoir lesquels sont prêts pour lecture/écriture.
- Non-bloquant : chaque socket est configurée en mode non-bloquant (
O_NONBLOCK) pour éviter que les appelsread/writene bloquent l'exécution principale. - FdContext : structure de contexte attachée à chaque fd surveillé par
epoll. Elle stocke les métadonnées nécessaires au traitement (type du fd, buffers, état, pointeur vers la structure plus haute (ex :HttpConnection), etc.).
Remarque : quelques suppositions raisonnables sont faites (exposées plus bas) car la définition exacte de
FdContextn'est pas fournie dans ce fichier. Le but ici est d'expliquer les usages et le pattern général.
- Le processus démarre et crée une socket d'écoute (listen socket).
- La socket d'écoute est mise en non-bloquant et enregistrée auprès d'un
epollviaepoll_ctl(généralement avecEPOLLIN). - La boucle principale appelle
epoll_waitpour obtenir les événements prêts. - Pour chaque événement retourné :
- le serveur récupère le
FdContextassocié (stocké dansepoll_event.data.ptr) ; - selon le type (LISTEN, CLIENT, CGI_IN, CGI_OUT, etc.), il appelle la logique correspondante : accepter la connexion, lire une requête, écrire une réponse, traiter des pipes CGI, etc.;
- les opérations de lecture/écriture sont faites en mode non-bloquant, et par la gestion d'evenement par epoll elles ne peuvent pas renvoyer EAGAIN/EWOULDBLOCK -> de ce fait une erreur sur un send ou write est forcement une erreur "grave" et non juste un "rien a lire pour le moment".
- le serveur récupère le
FdContext est la « petite fiche » attachée à chaque descripteur surveillé. Elle contient typiquement :
- le type/role (LISTEN, CLIENT, CGI_IN, CGI_OUT, etc.) ;
- le numéro de fd (dans le cas de listen ou d'un client) ;
- des pointeurs/indices vers une
HttpConnectionou une structure de haut niveau qui gère la session (pour une cgi) ;
Pourquoi c'est utile : quand epoll_wait fournit un événement pour un fd, le serveur doit connaître rapidement quoi faire avec ce fd (accepter une connexion, parser une requête, compléter une écriture, lire depuis un pipe CGI). Le FdContext rassemble ces informations et évite des recherches lentes.
-
Création et configuration :
- appeler
epoll_create(1)pour obtenir unepoll_fd; - configurer
listen_fdavecfcntl(listen_fd, F_SETFL, O_NONBLOCK); - préparer un
FdContextpour la socket d'écoute et l'ajouter à epoll avecEPOLLIN.
- appeler
-
Boucle d'événements :
while (running) { int n = epoll_wait(epoll_fd, events, maxevents, timeout); for (i=0..n-1) handle_event(events[i]); }
-
Traitement d'un événement :
- obtenir
FdContext *ctx = (FdContext*)events[i].data.ptr; - si ctx->type == LISTEN et event & EPOLLIN : faire
acceptdans une boucle jusqu'à EAGAIN (à cause du non-bloquant) et pour chaqueclient_fd:- mettre
client_fden non-bloquant ; - créer un
FdContext/HttpConnectioncorrespondant ; - ajouter
client_fdà epoll avecEPOLLIN(et éventuellementEPOLLETselon stratégie).
- mettre
- si ctx->type == CLIENT et event & EPOLLIN : lire autant que possible (loop read jusqu'à EAGAIN), parser la requête HTTP (garder données incomplètes dans le buffer du contexte). Si la requête est complète, préparer la réponse et changer les flags epoll pour écouter EPOLLOUT afin d'envoyer la réponse.
- si ctx->type == CLIENT et event & EPOLLOUT : écrire autant que possible depuis le buffer de sortie (loop write jusqu'à EAGAIN ou tout envoyé). Si tout est envoyé et que la connexion doit rester ouverte (keep-alive), repasser à EPOLLIN; sinon fermer et nettoyer.
- obtenir
- Le client se connecte (listen FD notifie EPOLLIN). Le serveur
accept()la connexion et ajoute leclient_fdà epoll. - Le client envoie une requête ;
epoll_waitrapporte EPOLLIN surclient_fd. - Le serveur lit toutes les données disponibles dans la connexion relié au fd ; si la requête est complète, il crée une
HttpRequest, puis uneHttpResponseet forme les headers et potentiellement le body adapté a la requete. - Lorsqu'EPOLLOUT arrive, le serveur écrit un petit morceau de reponse, puis attend un nouvel appel de epoll pour envoyer la suite.
- Ensuite, selon la connexion (Keep-Alive ou Close), il attend pour une nouvelle requête ou ferme la connexion.
Les CGI utilisent des pipes (descripteurs supplémentaires) pour communiquer avec le processus enfant. Ces fds sont aussi inscrits dans epoll et ont leur propre FdContext (par ex. CGI_IN, CGI_OUT) :
CGI_IN: on écrit dans l'entrée standard du CGI ; on surveille la possibilité d'écrire (EPOLLOUT) ;CGI_OUT: on lit la sortie du CGI (EPOLLIN) et on tasse les données dans le buffer de réponse à envoyer au client.
Le code présenté dans Server.hpp montre des méthodes pour ajouter/supprimer ces fds au monitoring (addCgiInFd, addCgiOutFd, removeCgiFd).
- Toujours mettre les sockets en non-bloquant avant de les ajouter à
epoll. - Ne pas supposer que
read/writetransfère toute la donnée d'un coup ; gérer offsets et buffers partiellement remplis. - Éviter les fuites : s'assurer de faire
close(fd)et de retirer l'inscription epoll lors d'erreurs ou de fermetures.
Résumé : ce serveur est structuré autour d'une boucle epoll + sockets non-bloquantes, et FdContext est la pièce centrale pour suivre l'état de chaque fd (connexion client, pipe CGI, ...).