Dans le protocole HTTP, une fois établie une connexion TCP avec
la machine hébergeant le serveur (par défaut, sur son port 80), le client envoie des requêtes
au serveur, les plus courantes étant GET
, HEAD
et POST
.
Une requête GET
demande au serveur de renvoyer une ressource (par exemple le
code HTML d'une page web).
Le serveur répond par une réponse qui contiendra la ressource demandée.
GET
Une requête GET
en HTTP 1.1 est de la forme :
GET /~carayol/ HTTP/1.1 Host: igm.univ-mlv.frChaque ligne est encodée en ASCII et se termine par deux caractères CR LF (Carriage Return, Line Feed; "\r\n" en java). Une requête commence par une ligne d'en-tête (avec le nom de la méthode de requête, ici
GET
). Elle peut contenir plusieurs lignes de
champs d'en-tête (comme le champ Host
ici) et se termine par une ligne vide. igm.univ-mlv.fr
, et elle demande la ressource /~carayol/
: comme il s'agit d'un répertoire, c'est en général le fichier index.html
contenu dans ce répertoire qui sera renvoyé.
La réponse du serveur est composée de deux parties :
La réponse du serveur est quelque chose du genre :
HTTP/1.1 200 OK Date: Thu, 01 Mar 2018 17:28:07 GMT Server: Apache Last-Modified: Thu, 15 Sep 2016 09:02:49 GMT ETag: "254441f-3d0a-53c881c25a040" Accept-Ranges: bytes Content-Length: 15626 Content-Type: text/html <!DOCTYPE html> <html> <head> ...Chaque ligne de l'en-tête est encodée en ASCII et est terminée par CRLF. La première ligne, dite ligne de statut :
HTTP/1.1 200 OKdonne la version du protocole utilisée pour la réponse (ici
HTTP/1.1
),
le code de la réponse (200
) et un message textuel correspondant à ce code (OK
).
Les lignes suivantes de l'en-tête sont de la forme :
clé: valeurParmi les informations pertinentes,
Content-Length
indique que le contenu
(c'est à dire le "corps" de la réponse, qui débute après l'en-tête qui se termine
par une line vide) fait 15626 octets. Cette information est
nécessaire car en HTTP/1.1 le serveur ne ferme pas la connexion après avoir répondu.
Le champ Content-Type
indique que la ressource est du HTML.
Il existe un autre mode de transfert appelé chunked
. Ce mode est signalé par
le champ suivant :
Transfer-Encoding: chunkedLe principe est décrit ici. Pour l'instant, il n'est pas nécessaire de regarder ce mode de fonctionnement.
Le but de cet exercice est de commencer la réalisation d'un petit client
HTTP. Notre client permettra de faire des requêtes GET
et d'afficher
le corps de la réponse si c'est du HTML décodé dans le Charset
spécifié dans l'en-tête. On utilisera la version 1.1 du protocole HTTP pour les requêtes.
La difficulté principale dans la réalisation d'un client HTTP est le traitement de la réponse du serveur. Le problème vient du fait qu'il n'y a pas, a priori, de borne sur la taille du header. Quand on fait une lecture sur la SocketChannel
, on ne peut donc pas savoir, a priori, si on arrivera à lire une ligne d'en-tête, toutes les lignes d'en-tête, ou bien également une partie du corps de la requête...
Pour résoudre ces difficultés, nous vous proposons de réaliser le traitement de la réponse au moyen d'une classe HTTPReader
que vous allez implémenter.
Récupérez les fichiers HTTPReader.java et HTTPException.java qui vous serviront de base.
Un objet de la classe HTTPReader
contient deux champs : la SocketChannel
sur laquelle la réponse est lue et son ByteBuffer
de lecture.
Écrivez la méthode readLineCRLF
dans la classe HTTPReader
. Dans un premier temps, vous pouvez la tester
avec l'exemple du main. Ensuite vous vérifierez que votre fonction passe les tests JUnit du
fichier HTTPReaderTest.java (il vous
faudra aussi le fichier FakeHTTPServer.java).
Écrivez la méthode readHeader
. Vous devez renvoyer un objet de la classe
HTTPHeader.java (qui vous est fournie).
create
qui prend en paramètre :
String
contenant la première ligne de la réponse,
map
associant à chaque champ sa valeur. Set-cookie: x-wl-uid=1smBggFQdYEUGLgg29x3Qr/zAwfq42jdGu0mYszL1+mrt/ABZ8xw43Ise90maJaHGuGvUKVQ+0gM=; path=/; domain=.amazon.fr; expires=Mon, 31-Dec-2035 23:00:01 GMT Set-cookie: session-id-time=2082754801l; path=/; domain=.amazon.fr; expires=Mon, 31-Dec-2035 23:00:01 GMT Set-cookie: session-id=276-2784413-9232431; path=/; domain=.amazon.fr; expires=Mon, 31-Dec-2035 23:00:01 GMTC'est équivalent à ne mettre qu'un seul champ
Set-cookie
avec la concaténation
des 3 chaînes séparées par des point-virgules.
Compléter la méthode readBytes
.
Écrivez un client HTTPClient
qui prend en argument l'adresse
d'un serveur et une ressource. Le client demande la ressource au serveur sur
son port 80, il affiche la ressource si c'est du HTML.
Pour l'instant, vous ne traiterez que le cas où la réponse contient un champ
Content-Length
.
Faire en sorte de pouvoir traiter les réponses dans le mode de transfert
chunked
. Pour cela implémentez, la méthode readChunks
dans la classe
HTTPReader
.
Modifiez votre client pour prendre en compte les codes de réponse 301 et 302.
Pour tester le code 302, vous pouvez demander la ressource /~carayol/redirect.php
au site www-igm.univ-mlv.fr
.
Pour parser le champ location du header, vous pouvez utiliser la classe URI. Vous pouvez
utiliser les méthodes d'instances getHost()
pour obtenir l'adresse du serveur et getPath()
pour obtenir le chemin de la ressource.