Objectif du TP

publicité
Serveur HTTP
Objectif du TP
L'objectif de ce TP est d'écrire un serveur HTTP multi-thread. Pour cela, vous devez :
 suivre la décomposition proposée ;
 implémenter les classes demandées ;
 mettre des commentaires pour la génération de la documentation avec javadoc ;
 utiliser un makefile pour compiler, générer la documentation et lancer le serveur.
Vous placerez l'ensemble de vos classes dans un package httpd. A travers ce TP, vous allez mettre en
œuvre différentes classes des packages :
java.lang
java.util
java.io
java.net
Structure générale du serveur
Le programme sera constitué dans un premier temps d'une classe. Cette classe contiendra une méthode
main() qui lira le fichier de configuration et démarrera le serveur HTTP.
Le serveur se lance par :
java httpd.Main config
Configuration du serveur
L'ensemble des paramètres de configuration du serveur est contenu dans un fichier texte qui est passé en
paramètre au programme. Pour lire et exploiter le contenu de ce fichier, on utilisera la classe
java.util.Properties (voir la méthode load()). Ce fichier contiendra, entre autre, le numéro du
port d'écoute du serveur (champ Port) et le répertoire racine des documents Internet (champ WebRoot).
Exemple de fichier :
# Numero de port du serveur
Port=8080
# Repertoire racine des documents web
WebRoot=www
Boucle de traitement des requêtes
Le protocole HTTP est un protocole sans état basé sur TCP. L'algorithme du serveur est le suivant :
Ouvrir un socket sur le port indiqué
Début boucle infinie
Attendre une requête
Récuperer le socket de travail
Lire le contenu de la requête
Analyser la requête
Construire la réponse adaptée
Renvoyer la réponse
Fermer le socket
Fin de boucle
Pour ouvrir le socket d'écoute du serveur, on utilisera la classe java.net.ServerSocket.
L'attente de requête se fait par la méthode accept() qui retourne à chaque nouvelle requête une
socket de travail (instance de la classe java.net.Socket). Pour récupérer le contenu de cette
socket, il suffit de lire sur le flux retourné par getInputStream()
Traitement d'une requête
Pour l'instant, le serveur ne traitera que les requêtes GET. Ces requêtes sont de la forme :
GET /ressource HTTP/1.0\n\n
Remarque : \n est ici utilisé pour symboliser un retour à la ligne i.e. appuyer sur la touche [Entrée].
Exemple de requête :
GET /rep1/index.html HTTP/1.0\n\n
Cette requête demande à obtenir le fichier index.html situé dans le répertoire rep1, lui-même situé dans le
répertoire racine des documents Internet. Le serveur lit la requête à l'aide de la classe
java.io.DataInputStream (méthode readLine()). L'analyse (l'utilisation de la classe
java.util.StringTokenizer est vivement conseillée) et renvoie une réponse (classe
java.io.PrintStream) de la forme :
Header\n
\n
Body
La partie Header se compose de plusieurs champs contenant des informations utiles pour le programme
client (status de la réponse, date, nom du serveur, type du document retourné, …) La partie Body
contient soit le document si la requête a pu être correctement traité, soit un message d'erreur pour
l'utilisateur.
Si la ressource est accessible, la réponse renvoyé est de la forme :
HTTP/1.0 200 OK
Date: Wed, 22 Oct 1997 21:49:50 GMT
Server: JavaHttp/1.0
Content-type: text/html
Puis le contenu du fichier…
Si la requête est mal formée, la réponse renvoyée est de la forme :
HTTP/1.0 400 Bad Request
Date: Wed, 22 Oct 1997 21:39:48 GMT
Server: JavaHttpd/1.0
Content-type: text/html
<HEAD><TITLE>Mauvaise requête</TITLE></HEAD>
<BODY><H1>Mauvaise requête</H1>
Votre navigateur Internet a envoyé une requête que ce serveur ne
peut pas traiter.
<P>
</BODY>
Si la requête est bien formée, le serveur vérifie que la ressource est bien accessible à partir du répertoire
racine précisé dans le fichier de configuration (champs WebRoot). Si ce n'est pas le cas, la réponse
renvoyée est de la forme :
HTTP/1.0 404 Not found
Date: Wed, 22 Oct 1997 21:41:59 GMT
Server: JavaHttpd/1.0
Content-type: text/html
<HEAD><TITLE>Fichier non trouvé</TITLE></HEAD>
<BODY><H1>Fichier non trouvé</H1>
La ressource /rep1/une_ressource n'est pas présente sur ce serveur.
<P>
</BODY>
Typage des ressources
Dans cette première version du serveur, on suppose que les documents renvoyés sont tous du type HTML
(ce qui est indiqué dans les headers des réponses par Content-type: text/html). Or les
documents peuvent être de nature variée (images GIF, JPEG, texte non-formaté, vidéo AVI, …). C'est au
serveur HTTP d'indiquer le type du document renvoyé.
Pour cela, il se base sur l'extension du nom du fichier pour établir le MIME type correspondant. Les mime
type sont des chaînes de caractères de la forme :
type/sous-type
Exemples :
Mime type
Type
text/html
Fichier HTML
text/plain
Fichier texte
image/gif
Image GIF
image/jpeg
Image JPEG
Modifiez le serveur pour qu'il effectue le typage des ressources. Pour cela, on utilisera le fichier
MIME Types (lisible comme le fichier de configuration par java.io.Properties.load())
contenant la liste des extensions possibles et les mime type associés. Le nom de ce fichier sera indiqué
dans le fichier de configuration (champ MimeTypeFile).
Si l'extension du fichierdemandé n'est pas connue, le serveur renverra un mime type par défaut (e.g.:
text/plain) indiqué dans le fichier de configuration (champ DefaultMimeType).
Traitement multi-thread
Actuellement, le serveur ne peut traiter qu'une requête à la fois. Pour pourvoir traiter plusieurs requêtes
simultanément, nous allons créer des objets capables de prendre en charge les requêtes et qui
s'exécuteront dans différentes threads . Ces objets seront des instances d'une nouvelle classe :
httpd.HttpConnection. Pour que ces instances soient threadables, cette classe doit implémenter
l'interface java.lang.Runnable. Une grande partie du code de la classe Httpd sera délocalisée
dans cette nouvelle classes (au sein de la méthode run()).
La boucle de traitement des requêtes (située dans la classe Httpd) devient alors :
Ouvrir une socket sur le port indiqué
Boucle infinie
Attendre une requête
Récuperer la socket de travail
Créer une instance de HttpConnection avec en parametre la socket
et la configuration
Fin de boucle
La méthode run() de la classe HttpConnection contient le reste du traitement :
Lire le contenu de la requête
Analyser la requête
Construire la réponse adaptée
Renvoyer la réponse
Fermer la socket
Journalisation des accés
On souhaite pouvoir suivre l'ensemble des requêtes auxquelles répond notre serveur. Pour cela, on
enregistre chaque requête traitée dans un fichier de journalisation. Les enregistrement sont de la forme :
[adresse IP du client] [date] [requête] [status de la reponse]
Exemple :
[127.0.0.1] [Sat Mar 08 14:51:47] [GET /icons/dir.gif HTTP/1.0] [200]
[128.93.7.60] [Sat Mar 08 14:53:44] [GET /docs/index.html HTTP/1.0] [404]
Ecrivez une classe httpd.HttpLog de la forme :
package httpd;
class HttpLog
{
HttpLog(String logFile)
{
...
}
add(String address, String request, int status)
{
...
}
}
Cette classe sera instanciée avant le démarrage du serveur (dans la classe Httpd) avec le nom du fichier
de journalisation (indiqué dans le fichier de configuration dans le champ LogFile) et sera fournie à
chaque instance de HttpConnection pour qu'elles puissent y ajouter leur requête une fois celle-ci
traitée.
Remarque : le serveur étant multi-thread, assurez vous que l'ajout d'une entrée (méthode add) soit
exclusif.
Gestion des repertoires
Renvoi d'un fichier d'index
Lorsqu'une requête demande une ressource correspondant à un répertoire, il est devenu conventionnel de
renvoyé, s'il est présent, un fichier particulier (traditionnellement nommé index.html). Par exemple la
requête :
GET /rep1 HTTP/1.0\n\n
renverra le fichier index.html contenu dans le répertoire rep1. Modifier la classe
HttpConnection pour prendre en compte cette fonctionnalité (le nom du fichier d'index sera indiqué
dans le fichier de configuration dans le champ DirectoryIndex). Pour tester si une ressource est un
répertoire, utilisez la méthode isDirectory() de la classe File.
Renvoi du contenu du répertoire
Il est aussi devenu traditionnel que si ce fichier d'index n'existe pas on retourne une page HTML
représentant le contenu du répertoire. Cette page indique généralement la nature des fichiers (au moyen
d'une icône), le nom du fichier (avec un lien pour y accéder) et diverses informations (taille du fichier,
date, ...). Modifiez la classe HttpConnection pour prendre en compte cette fonctionnalité.
Téléchargement