Un rélais HTTP concurrent filtrant
Par Daniele Raffo & Matteo Magnani
Projet de "Architecture et programmation réseaux
avec Java"
Gilles Roussel, Université de Marne la Vallée
Mai 2001
Présentation du projet
RAMM Proxy est un relais HTTP concurrent (multithreadé), filtrant (de coté client et de coté serveur), compatible avec la version 1.1 du protocôle, entierement écrit en Java 1.3 et dévéloppé sous Linux. Il est pleinement configurable au moyen des fichiers de configuration in.cfg, out.cfg et proxy.cfg. Un panneau de contrôle permet de verifier à chaque instant l'état du relais et d'y modifier dynamiquement certains paramétres, notamment le nombre de threads qui s'occupent des connections. Le relais effectue aussi une journalisation sur fichier (proxy.log) qui peut être examiné en fonction de différents critéres.
RAMM Proxy est sous le termes de la Licence Publique Générale GNU, version 2.
Le manuel d'utilisation est
fourni, en anglais, dans le fichier rammmanual.html. Ce rapport détaille
donc seulement les aspects téchniques du projet. Une documentation
en Javadoc a été aussi générée.
Structure de base du rélais
Le rélais est composé par trois classes principales, et des classes qui répresentent des services, comme démandé du paradigme objet [UML]. Afin d'obtenir un couplage faible et des composants qui s'occupent de taches spécialisées, on a décidé de diviser le rélais en trois modules, comme expliqué dans la figure suivante:
La classe ProxyMain s'occupe de l'interface avec l'usager: elle permet le démarrage du rélais, lit les paramétres eventuels passés en ligne de commande et appelle la classe Configuration éxaminante le fichier de configuration proxy.cfg (si un erreur est engendré à ce niveau, le rélais est demarré également avec des valeurs par défaut).
Gestion des connections - niveau TCP
Gestion des connections - niveau HTTP
Les connections ne travaillent que au niveau TCP. Pour traiter les données au niveau du protocole HTTP elles utilisent des services fournits par des classes qui héritent de la superclasse abstraite HTTPMessageReader. Les classes qui interprétent le flot représenté par une requête ou une réponse HTTP sont, respectivement, HTTPRequestReader et HTTPResponseReader; la méthode qui lit l'header du message HTTP reçoit tous les octets du flot jusqu'aux caractéres « \r\n », répétés deux fois, qui indiquent une ligne vide et donc la fin de l'entête ; ensuite, cette méthode fait un parsing pour récuperer la valeur du Content-Length (obligatoire en HTTP/1.1) et alloue un tampon de telle taille pour reçevoir le reste du flot (le corps du message). Ce dernier est lu au moyen de la méthode readFully(). Pour compatibilité avec HTTP/1.0, si Content-Length n'est pas indiqué, on alloue un tampon suffisament grand (1024 Kbytes) à stocker le flot ; dans ce cas, la fin du message est marquée par la fermeture de la connexion.
Les machines de l'UMLV sur lesquelles on a travaillé (10.x.x.x) étant derriére un proxy qui transforme le HTTP/1.1 en 1.0, on a testé le protocôle en se connectant à ryu.univ-mlv.fr - un serveur intérieur implémentant le 1.1. De toute façon, notre proxy est aussi compatible avec le protocole 1.0.
Filtrage
La supervision du proxy est effectué au moyen d'un panneau de contrôle control-panel.html, construit à la volée par la classe ControlPanel.
Ce panneau contient les informations
rélatives au proxy (serveur, port et horaire de démarrage),
aux threads (valeurs de MIN_THREADS, MAX_THREADS, MIN_SPARE_THREADS, MAX_SPARE_THREADS
et leur état) et des liens aux fichiers de configuration et au log
trié.
Configuration à distance et à la volée via HTTP
- parsing des informations obtenues par la méthode
POST dans la variable ENV
- vérification du bon format des données
- génération d'une page HTML de réponse
pour l'usager (positive ou negative)
- si les données sont valables, écriture
dans le fichier "proxy.tmp" des données
Voici le fichier "proxy.tmp" après l'éxécution de "form.cgi":
#----Config. file automatically generated by form.cgi----
MinThread 8
MaxThread 50
MinSpareThread 8
MaxSpareThread 10
Ce fichier constitue l'interface entre la CGI (donc le web) and le programme Java: un procéssus léger, nommé "timer" et lancé par la classe Proxy, contrôle si l'utilisateur a modifié le fichier (à partir de la date de dernière modification) et, dans ce cas là, change les valeurs des threads et demande si nécessaire l'arrêt du rélais.
Il faut noter que la CGI vérifie la cohérence des donnés, mais elle ne connait pas les impostations du rélais au moment de la requête via form, donc c'est par exemple possible de démander une valeur de max-threads = 20 pendant que le rélais est en train de servir 25 connections. Cela ne pose pas de problèmes, parce que la classe ThreadManager permet de gérer aussi des cas de ce type, et dans l'exemple tue le premières 5 connections qui se terminent, sans créer de nouvelles threads en attente.
A cause de l'impossibilité de tester la CGI à
l'interieur de l'université, ou elles sont interdites pour sécurité,
le code disponible dans le répertoire "src" a été
modifié en ce qui concerne:
- la lecture des données en entrée, qui
est fait sur l'entrée standard et n'utilise pas la variable ENV
- l'écriture de la page web de réponse,
qui n'est pas faite sur la sortie standard mais dans un fichier "output.html"
Cela permet de tester la CGI en local, en tapant sur
le prompt du shell:
$ form.cgi <input
où "input" est un fichier qui contienne les mêmes données contenues dans la variable ENV après envoi du form, dans l'exemple qu'on a utilisé:
MinThread=8&MaxThread=50&MinSpareThread=8&MaxSpareThread=10
On pourra lire la sortie dans le fichier output.html.
Dans le cas où l'arret est démandé, la thread "timer"
de la classe Proxy cause la fin du programme (i.d. la sortie d'une boucle
infinie d'attente) et donc aussi des threads qui gérent les connections,
qui se terminent tout de suite.
Conclusion
Le développement de ce projet nous a donné
un premier aperçu sur le protocôle HTTP, trés connu
à un niveau pratique (qui n'a jamais surfé sur le Web ?)
mais qui, à un niveau d'implementation, présente des problèmes
pas du tout triviaux et ouverts (nouvelles versions du protocôle,
compatibilité avec les différents clients tels que Netscape
ou IE...).
Ce projet nous a permis de nous confronter avec les problemes
qu'on incontre quand on doit implementer des concepts, parfois élémentaires
à un niveau theorique, mais qui présentent des difficultés
materielles. Notre simple proxy, même présentant certaines
failles et manquances d'optimisations, a pourtant un interêt didactique;
en fait, par l'examination des points non traités et des erreurs
découverts pendant la soutenance, nous avons appris des concepts
aussi importants que ceux que nous avons appris en écrivant le code.