Le but de ce TP est de programmer un client qui se connecte à un serveur WEB par le protocole HTTP.

On utilisera le langage Perl pour ses capacités de traitement de textes (expressions rationnelles, etc.).

Il s'agit du TP proposé l'année dernière par Antoine Miné. La correction est disponible sur la page d'Antoine.

Le protocole HTTP

Le protocole HTTP est utilisé pour transférer des pages WEB entre un serveur (distant) et un client (votre programme). On utilisera la version 1.1 du protocole (c'est la dernière en date). La description présentée ici est très partielle mais devrait suffire pour le TP. Voir la partie documentations de référence pour tout complément d'information.

Requête HTTP

Pour demander une page http://www.di.ens.fr/~vergnaud/TP_20080515_Systemes.html, le client doit ouvrir une connection TCP sur le port 80 vers www.di.ens.fr et envoyer les lignes de texte suivantes:

GET /~vergnaud/TP_20080515_Systemes.html HTTP/1.1
Host: www.di.ens.fr
User-Agent: Mon Super Script v0.01
Connection: close
ligne vide

Ces lignes ont la signification suivante:

  • la première ligne indique le type de requête (GET), l'adresse absolue sur le serveur de la page WEB demandée (/~vergnaud/TP_20080515_Systemes.html) et la version du protocole utilisée (HTTP/1.1),
  • la deuxième ligne rappelle le nom du serveur contacté (www.di.ens.fr),
  • la troisième ligne permet à l'application cliente de s'identifier (facultatif mais poli; généralement, on y trouve quelque chose comme Mozilla/5.0 (X11; U; Linux x86_64; fr; rv:1.8.1.3) Gecko/20070321 BonEcho/2.0.0.3),
  • la quatrième ligne indique que c'est au serveur de fermer la connection quand il a fini d'envoyer la page (sinon, c'est au client de fermer la connection),
  • la dernière ligne est toujours vide; elle indique la fin de l'en-tête.

En réalité, la ligne de requête peut être suivie de nombreuses autres lignes d'en-tête, toujours sous la forme XXX: YYY. Notez que le nom de l'en-tête XXX est insensible à la casse. Seule l'en-tête Host: est obligatoire. (Voir la partie 14 de la RFC 2616 pour la liste des en-têtes.)

Note importante: dans tous les protocoles textes d'Internet, les fins de ligne sont matérialisées par la séquence \r\n.

Réponse positive

Une réponse du serveur à notre requête ressemblerait à ceci:

HTTP/1.1 200 OK
Content-type: text/html
ligne vide
<html><head>
...
</body></html>

Cette réponse consiste en trois parties:

  • une ligne de statu commençant par le numéro de version du protocole (HTTP/1.1), suivi d'un code de retour numérique (200 signifie que tout s'est bien passé) puis d'un texte (OK) explicitant la signification du code d'erreur,
  • un nombre arbitraire de lignes d'en-têtes, terminées par une ligne vide (ici, on n'en a montré qu'une seule: Content-type: text/html qui indique le type MIME du document envoyé par le serveur: du texte au format HTML),
  • le contenu de la page WEB demandée.

La fin de la page WEB est indiquée par la fermeture de la connection de la part du serveur.

Réponse d'erreur

Si la page WEB n'existe pas, la réponse ressemblera à:

HTTP/1.1 404 Not Found
Content-type: text/html
ligne vide
<html><head><title>Not Found</title></head><body>
Sorry, the object you requested was not found.
</body><html>

Cette fois, le code est 404 qui signifie: page non trouvée. Notez que le serveur nous envoie tout de même une page WEB qu'un navigateur pourra choisir d'afficher. Ceci permet à l'administrateur du site de configurer la manière dont le message d'erreur est présenté à l'utilisateur. (La liste complète des codes de statu se trouve dans la partie 10 de la RFC 2616.)

Autres réponses intéressantes

Dans certains cas, le serveur indique que la page a été déplacée temporairement ou définitivement (codes 301, 302, 303, 305 et 307) à une autre adresse. Celle-ci est indiquée par une en-tête de la forme Location: nouvelle adresse. top

Exercice 1 - Récupérer une page WEB en Perl

Faites un script Perl httpget qui télécharge une page WEB (dont l'adresse est donnée en argument) et affiche son contenu (hors en-têtes) à l'écran.

L'adresse de la page web est donnée sous la forme d'une URL protocole://serveur:port/cheminprotocole vaut toujours http, serveur est une adresse texte ou numérique, la partie :port est facultative (la valeur par défaut 80 sera utilisée si le port est omis) et chemin indique le chemin de la page sur le serveur (le séparateur de répertoire est /). Votre script devra donc commencer par décomposer l'URL afin de déterminer le serveur, le port et le chemin, avant de contacter le serveur.

Prenez soin à rapporter à l'utilisateur les erreurs rencontrées (serveur inexistant, connection refusée, page non trouvée, etc.).

Les fonctions Perl suivantes seront utiles (voir le manuel de référence Perl sur les fonctions et les modules standards):

  • socket permet de créer une socket,
  • connect permet de connecter une socket TCP à un serveur,
  • sockaddr_in (du module Socket) permet de convertir une paire (adresse IP, port) au format interne utilisé par connect,
  • gethostbyname permet de transformer une adresse textuelle en adresse IP numérique (DNS),
  • shutdown permet de fermer la socket seulement en lecture ou en écriture (utile si on n'utilise pas le mode Connection: close),
  • autoflush (du module IO::Handle) permet de s'assurer que le tampon de sortie de la socket est vidé automatiquement après chaque appel à print (important!). top

Exercice 2 - Vérificateur de liens

Une page WEB peut contenir des liens vers d'autres pages grâce à la construction href="lien" (trouvée, par exemple, dans les balises <a>) ou src="lien" (balises <img>). Un tel lien peut prendre une des formes suivantes:

  • une URL complète protocole://serveur:port/chemin (où :port est optionnel),
  • le protocole http: peut être omis, ce qui donne //serveur:port/chemin ou //serveur/chemin,
  • on peut omettre le nom du serveur si c'est le même que celui de la page contenant le lien, ce qui donne /chemin,
  • enfin, si le chemin ne commence pas par /, il s'agit d'une adresse relative au répertoire de la page contenant le lien.

On note de plus que . et .. ont la même signification que dans les chemins Unix.

Enfin, un lien peut contenir un suffixe #ancre indiquant une position particulière dans la page référencée (ce suffixe, utile uniquement pour le navigateur, ne doit donc pas être inclus dans la requête au serveur).

  1. Écrivez une fonction Perl urlcat qui prend en argument l'URL complète d'une page WEB ainsi qu'un lien et les 'concatène' pour retourner l'URL complète de la page liée. Par exemple:

    • urlcat( "http://x.y.com:8080/toto/titi.html", "//a.b/tata.html" ) donne http://a.b/tata.html,
    • urlcat( "http://x.y.com/toto/titi.html", "/tutu/tata.html#bli" ) donne http://x.y.com/tutu/tata.html,
    • urlcat( "http://x.y.com/toto/titi.html", "x/truc.html?w=32" ) donne http://x.y.com/toto/x/truc.html?x=32.

    Essayez de rendre l'URL la plus canonique possible (e.g., supprimez les morceaux de chemins inutiles de la forme /toto/../ ou /./).

  2. Faites un script Perl qui, en partant d'une URL donnée en argument, suit tous les liens de la page, récursivement, et donne la liste des pages (ou images ou autres) liées mais introuvables. On prendra particulièrement soin à:

    • inspecter uniquement le contenu des pages HTML, pas celui des fichiers images, sons, etc. (on pourra se baser sur l'extension de fichier ou, mieux, sur l'en-tête Content-type: dans la réponse du serveur),
    • suivre les redirections HTTP (en-tête Location: dans le réponse du serveur),
    • limiter la profondeur de la recherche (à 5 liens, par exemple),
    • se limiter à un domaine Internet (tel que .ens.fr),
    • éviter d'examiner deux fois la même page.
top

Bibliothèques Perl prédéfinies

Normalement, pour réaliser une application WEB, on ne programme pas tout à la main comme nous venons de le faire. On utilise plutôt une des nombreuses bibliothèques Perl prédéfinies. Elles utilisent souvent le système d'objets de Perl. Citons, entre autres:

  • IO::Socket::INET pour ouvrir des socket Internet facilement,
  • URI::Escape pour gérer les URLs,
  • la bibliothèque LWP comprend de nombreux objets dont, en particulier:
    • LWP::UserAgent pour créer une instance de client,
    • HTTP::Request pour créer une requête,
    • HTML::Form pour remplir les formulaires de page WEB,
    • HTTP::Cookies pour gérer les cookies demandés par le serveur.

Il n'est toutefois pas mauvais de tout reprogrammer une fois dans sa vie afin de bien comprendre comment tout cela fonctionne! top

Documentations de référence

Voici des pointeurs sur les documentations officielles qui décrivent tous les aspects de HTTP et du HTML.