4.6 KiB
Web Crystal
WebCrystal est un serveur HTTP réalisé à l'occasion du défi de NaN.
Ce projet m'a permis d'apprendre Crystal, le code présente donc pas mal de bad-practice, de chose mal géré...
Consigne
Implémentation d'un serveur HTTP
Défi sur une semaine, à rendre au plus tard le lundi 5 avril à 6h à votre ambassadeur préféré. Ce défi est considéré plus difficile que d'ordinaire, vous avez une semaine pour le résoudre.
Le serveur
Le serveur tourne en HTTP/1.0 derrière le port 8080 et est compatible avec les principaux navigateurs. Le serveur comprend les requêtes HEAD, GET, POST, PUT et DELETE. Le serveur expose une application "compteur" à l'url http://compteur.notaname.fr/, les autres url doivent renvoyer une page d'erreur au format HTML avec un code 404. Le serveur doit être capable de répondre à plusieurs utilisateurs à la fois. Le serveur peut être accessible à l'extérieur de votre réseau domestique. Le serveur peut comprendre TLS, dans quel cas il écoute sur le port 8443.
Parce que le but de ce défi est d'implémenter un serveur web, il est bien entendu interdit d'utiliser un quelconque framework. Plus précisément, vous n'avez pas le droit d'utiliser une quelconque bibliothèque HTTP et vous vous contentez des bibliothèques réseaux et systèmes de votre langage. Le choix de votre stack système est libre, vous pouvez faire du multithreading, de l'asynchrone, etc.
L'application
L'application permet de manipuler des compteurs. Le serveur démarre avec deux compteurs : "carotte" et "etoile". Le compteur "carotte" est normal et est initialisé à 10. Un compteur "etoile" est le seul compteur spécial, il n'est pas possible de le modifier et il renvoie la somme de tous les autres compteurs. La valeur d'un compteur ne peut que croître. L'application compteur admet 5 routes (en plus de
HEAD):
GET /: retourne la liste de tous les compteurs.GET /<compteur>: retourne l'état du compteur .POST /: crée un nouveau compteur et le renvoie.PUT /<compteur>: met à jour la valeur du compteur .DELETE /<compteur>: supprime le compteur .Il est possible de communiquer en
x-www-form-urlencodedet enJSONavec l'application (requête). L'application est capable de répondre au formatHTML, au formatJSONou à défaut au format texte (réponse). Interessez vous aux headersAcceptetContent-Type.Pour simuler une application plus compliquée et mettre à mal votre implémentation réseau, chaque route simule des calculs et des requêtes à un service externe. Vous simulez les calculs en faisant tourner une boucle dans le vide à raison de 0.2 secondes. Vous simulez une requête externe en mettant votre processus en sleep pendant 0.3 secondes au milieu.
Exemple en Python, après des tests vous avez trouvé qu'itérer sur 1 millard d'éléments prend 200 millisecondes.
counters = {'carotte': 10}
def get(counter):
for i in range(1_000_000_000): pass
time.sleep(0.3) # remplacez par asyncio.sleep(0.3) si vous faites de l'async
return b"{'name': 'carotte', 'value': %d}" % counters[counter]
Considérations techniques
Pour faciliter l'implémentation, vous pouvez limiter la taille de la commande et des headers à maximum 1024 caractères par ligne. La RFC (de ce qu'on sait) ne précise rien à ce propos et la plupart des serveurs permettent 8k ou 16k caractères par ligne dans les headers.
Ressources
Voici une liste de ressource que nous vous conseillons de lire. Nous ajouterons à cette liste toutes les ressources que vous nous conseillerez.
Lancer le serveur
Pour lancer le projet simplement vous pouvez utiliser vagrant, ou alors installer crystal
vagrant ssh
cd /vagrant
clear && crystal run src/main.cr
Pour simuler un test de charge
vegeta attack -targets targets.txt -rate=20 -duration=30s
vegeta attack -targets=targets.txt -name=300qps -rate=300 -duration=25s > results.300qps.bin;cat results.300qps.bin | vegeta plot > plot.300qps.html
Pour compiler le serveur
crystal build ./src/main.cr
mv ./src/main ./web
TODO
- Ajouter le logger
- Ecrire des test
- Ecrire une CI, capable de crée les releases...
- Utiliser un analyser static comme ameba.
- CGI
- Configuration (json --> DSL)
- TLS