:: Enseignements :: Master :: M1 :: 2015-2016 :: Programmation d'applications réseaux ::
[LOGO]

Client HTTP


Exercice 1 - Client HTTP

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'en afficher le corps de la réponse si c'est du HTML décodé dans le Charset spécifié dans l'entête.

Une requête GET en HTTP 1.1 est de la forme:
GET / HTTP/1.1
Host: www.w3.org

Chaque ligne est encodée en ASCII et se termine par CRLF (Carriage Return, Line Feed; "\r\n" en java). La requête se termine par une ligne vide. La requête ci-dessus est faite au serveur www.w3.org sur le port 80 et demande la ressource racine / qui est en général la page d'accueil du site.

La réponse du serveur est quelque chose du genre:
HTTP/1.1 200 OK
Date: Thu, 05 Mar 2015 20:03:16 GMT
Server: Apache/2
Content-Location: Home.html
Vary: negotiate,accept
TCN: choice
Last-Modified: Thu, 05 Mar 2015 14:35:13 GMT
ETag: "96fb-5108b79c4ea40;89-3f26bd17a2f00"
Accept-Ranges: bytes
Content-Length: 38651
Cache-Control: max-age=600
Expires: Thu, 05 Mar 2015 20:13:16 GMT
P3P: policyref="http://www.w3.org/2014/08/p3p.xml"
Content-Type: text/html; charset=utf-8

...
Chaque ligne de l'entête est encodée en ASCII et est terminée par CRLF. L'entête se termine par une ligne vide. La première ligne:
HTTP/1.1 200 OK
donne la version du protocole utilisée pour la réponse (ici HTTP/1.1), le code de la réponse (200) et un message décrivant le code de retour (OK). Les lignes suivantes de l'entête sont de la forme:
clé: valeur
Parmi 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 38651 octets. Cette information est nécessaire car en HTTP/1.1 le serveur ne ferme pas la connection après avoir répondu. Le champ Content-Type indique que la ressource est du HTML et est encodée en UTF-8.

Il existe un autre mode de transfert appelé chunked. Ce mode est signalé par le champ suivant:
Transfer-Encoding: chunked
Le principe est décrit ici. Pour l'instant, il n'est pas nécessaire de regarder ce mode de fonctionnement.

Le traitement de la réponse se fera au moyen d'une classe HTTPReader que vous allez implémenter. Un objet de cette classe contient deux champs : la SocketChannel sur laquelle la réponse est lue et son buffer de lecture.

Toutes les méthodes de la classe doivent respecter le comportement suivant:
  • Le buffer de lecture est en mode écriture avant et après l'appel à la méthode (son contenu n'est pas "prêt" à être lu, mais prêt à être complété s'il reste de la place).
  • Aucune lecture sur la socket ne peut être effectuée tant que le buffer de lecture contient des éléments (on doit donc "consommer" tout le contenu du buffer avant de lire à nouveau des données).

  1. Récupérez les fichiers HTTPReader.java et HTTPException.java qui vous serviront de base.
  2. Ecrivez la méthode readLineCRLF. Vous pouvez dans un premier temps la tester avec l'exemple du main. Ensuite vous vérifirez que votre fonction passe les tests JUnit du fichier HTTPReaderTest.java (il vous faudra aussi le fichier FakeHTTPServer.java).
  3. Ecrivez la méthode readHeader. Vous devez renvoyer un objet de la classe HTTPHeader. Pour créer l'objet, vous utiliserez la factory create qui prend en paramètre:
    • une String contenant la première ligne de la réponse,
    • une map associant à chaque champ sa valeur.
      Attention, le header peut contenir plusieurs fois le même champs. Par exemple,
      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 GMT
      
      C'est équivalent à ne mettre qu'un seul champ Set-Cokie avec la concatenation des 3 chaînes séparées par des point-virgules.
  4. Compléter la méthode readBytes.
  5. Ecrivez 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.
  6. Faire en sorte de pouvoir traiter les réponses dans le mode de transfert chunked. Pour cela implémentez, la methode readChunks dans la classe HTTPReader.
  7. Modifiez votre client pour prendre en compte les codes de réponse 301 et 302.