Le protocole HTTP

Les versions du protocole

Les versions les plus utilisées actuellement sont les versions 1.0 et 1.1, l'évolution suivante étant le XHTML qui apporte avant tout un peu plus de rigueur dans la syntaxe et un peu plus de cohérence avec le XML, mais le principe reste exactement le même.

La version 1.1 de HTML introduit quelques commandes supplémentaires, comme nous le verrons plus bas. La version 1.0 reste toutefois utilisable dans la plupart des cas actuellement.

Remarque préliminaire :

une page est en principe définie par un URI (Unified Ressource Identifier). On dit aussi URL (Unified Ressource Locator). Par exemple : http://www.debian.org/doc/manuals/reference/ch-preface.fr.html. Nous savons que dans cet URI, il y a trois morceaux :

  • http:
    qui indique quel protocole nous allons employer ;
  • //www.debian.org
    Qui est sensé représenter le serveur web contenant l'information que l'on recherche ;
  • /doc/manuals/reference/ch-preface.fr.html
    qui représente le chemin complet de la page visitée dans l'arborescence du serveur concerné.

Pourtant, si nous indiquons comme URI http://www.debian.org , il manque toute la partie correspondant au nom de la page. Cependant, nous arrivons bien à visualiser quelque chose… Tout simplement, parce que le serveur est paramétré pour ajouter  /index.html s'il n'y a pas le nom de la page. Ainsi, http://www.debian.org est équivalent à http://www.debian.org/index.html.

La démonstration

Nous allons voir tout ceci à travers quelques manipulations très simples.

Pour réaliser cette démonstration, un serveur web Apache est installé sur mon réseau privé. L'hôte s'appelle poétiquement : linux.maison.mrs

Il contient une unique page html nommée index.html. C'est la page qui est servie par défaut (Les manipulatons datent de 2002, sur un apache 1.3 et une distribution d'époque (Mandrake), mais ici, le temps ne fait rien à l'affaire).

1° On fait comme tout le monde...

Commençons par faire ce que tout le monde fait : Utiliser son navigateur favori pour visiter cette page. Voici l'allure de cette très belle page, telle qu'on l'obtient avec Internet Explorer :

A titre indicatif, en voici le code HTML, tel qu'on peut l'obtenir en affichant le source depuis le navigateur :

<html>
<head>
<title>Untitled Document</title>
<meta http-equiv= "Content-Type" content= "text/html; charset=iso-8859-1">
</head>

<body bgcolor= »#FFFFFF » text= »#000000 »>
<p>Hello world.</p>
<p><img src="images/tux.gif" width= "97" height= "115"></p>
</body>
</html>

Les puristes constateront que ce code n'est plus du tout conforme aux recommandations du W3C, mais la philosophie reste inchangée.

2° On fait avec Telnet...

Essayons d'aller plus loin en trouvant un moyen de faire « à la main » ce que le navigateur fait automatiquement.

Nous sommes sur un protocole applicatif basé sur TCP et, dans ce cas là, il est généralement possible d'utiliser Telnet pour étudier un peu les commandes…

La commande GET

Nous ouvrons une session telnet sur le serveur HTTP de linux.maison.mrs (Un serveur web écoute par convention sur le port 80).

telnet linux.maison.mrs 80

Trying 192.168.0.253...
Connected to linux.maison.mrs (192.168.0.253)
Escape character is '^]'.

La session est ouverte. Utilisons maintenant la commande GET…

GET / HTTP/1.0

Nous la faisons en protocole 1.0, c'est un peu plus simple. Deux « return » pour valider… Et voici qu'arrive la réponse du serveur. Cette partie constitue l'en-tête, systématiquement envoyée par le serveur :

HTTP/1.1 200 OK
Date: Wed, 08 May 2002 14:26:57 GMT
Server: Apache-AdvancedExtranetServer/1.3.23
(Mandrake Linux/4mdk) auth_ldap/1.6.0 mod_ssl/2.8.7 OpenSSL/0.9.6c PHP/4.1.2
Last-Modified: Sun, 14 Apr 2002 09:29:32 GMT
ETag: "57d44-116-3cb94bfc"
Accept-Ranges: bytes
Content-Length: 278
Connection: close
Content-Type: text/html

Le serveur sait faire du HTTP 1.1. C'est un Apache version 1.3.23. Il annonce également ce qu'il sait faire :

  • Authentification par annuaire LDAP
  • Secure Socket Layer en OpenSSL version 0.9.6c
  • Interprétation de code PHP version 4.1.2

Il indique également La date GMT de dernière modification du document demandé, qu'il mettra fin à la connexion TCP à la fin de l'envoi, et que le document fourni est au format MIME : text/html

Et voici le document proprement dit :

<html>
<head>
<title>Untitled Document</title>
<meta http-equiv= "Content-Type" content= "text/html; charset=iso-8859-1">
</head>

<body bgcolor= »#FFFFFF » text= »#000000 »>
<p>Hello world.</p>
<p><img src="images/tux.gif" width= "97" height= "115"></p>
</body>
</html>

Le message Connection closed by foreign host., issu de Telnet, indique que, par défaut, la connexion est interrompue par le serveur à la fin de l'envoi du document. L'option Connection: Keep-Alive aurait évité cette déconnexion. Entendons-nous bien sur ce point : Nous parlons de la connexion TCP et en aucune façon d'une « session » au niveau de l'application.

Bien ! Mais l'image ? Essayons de l'avoir…

telnet linux.maison.mrs 80

Trying 192.168.0.253...
Connected to linux.maison.mrs (192.168.0.253).
Escape character is '^]'.

GET /images/tux.gif HTTP/1.0
Trying 192.168.0.253...

Connected to linux.maison.mrs (192.168.0.253).
Escape character is '^]'.

HTTP/1.1 200 OK
Date: Wed, 08 May 2002 14:44:17 GMT
Server: Apache-AdvancedExtranetServer/1.3.23
(Mandrake Linux/4mdk) auth_ldap/1.6.0 mod_ssl/2.8.7 OpenSSL/0.9.6c PHP/4.1.2
Last-Modified: Sun, 14 Apr 2002 09:29:32 GMT
ETag: « cf98a-a20-3cb94bfc »
Accept-Ranges: bytes
Content-Length: 2592
Connection: close

Content-Type: image/gif
GIF89aasæ????…/IHG£HIíÈ?Å?2›i?º…??gp©:<…f?×¥?ÄHL§v?‹ˆèèü?ˆ”©?°››¡»¹ÅØ ?ÉÈ×rI?
(''}y|]?Ë8:µ}?–y}ÁX_À•!?‰ hnÎ??N4?è×?XXZò»?W????98;ýýûhgjºx‚¹™?ôÖ?«Y^„ZXbE?
©¶ÙÙëà³L„hhә?…NE?4ÜÌÀ•XYªy‚U<?nj?ã©??˜...
...
Connection closed by foreign host.

Là, il ne fallait tout de même pas s'attendre à des miracles, avec une console en mode texte. On a bien reçu l'image, le type MIME (image/gif) est bien signalé, mais il n'est pas possible de la visualiser avec telnet.

3° On espionne avec le sniffeur...

Comment travaille le navigateur ?

Il a fait exactement la même chose que nous :

  • Il appelle la page d'accueil GET / HTTP/1.0 (éventuellement HTTP/1.1, mais alors, suivant la définition de cette version HTTP, il devra au moins envoyer aussi  le nom d'hôte du serveur interrogé).
  • Une fois la page reçue, il va chercher dedans tous les URI, ici celui de l'image, et va les appeler avec un GET. Dans le cas de l'image, il devra la recomposer, ce qui lui est possible parce qu'il connaît le format d'encodage gif.
  • Il affiche alors la page, dans son intégralité.

Première observation de la conversation

Nous appelons la page avec Internet Explorer et le sniffeur va enregistrer ce qu'il se passe. Les trames surlignées sont celles qui sont propres à HTTP. Mais n'oublions pas que HTTP s'appuie sur TCP, raison pour laquelle les autres trames existent :

No. Time     Source        Destination   Proto Info
  1 0.000000 192.168.0.10  192.168.0.253 TCP   1282 > 80 [SYN] 
  2 0.000163 192.168.0.253 192.168.0.10  TCP   80 > 1282 [SYN, ACK]
  3 0.000565 192.168.0.10  192.168.0.253 TCP   1282 > 80 [ACK] 
  4 0.001410 192.168.0.10  192.168.0.253 HTTP  GET / HTTP/1.1
  5 0.001487 192.168.0.253 192.168.0.10  TCP   80 > 1282 [ACK] 
  6 0.068550 192.168.0.253 192.168.0.10  HTTP  HTTP/1.1 200 OK
  7 0.098435 192.168.0.10  192.168.0.253 HTTP  GET /images/tux.gif HTTP/1.1
  8 0.098593 192.168.0.253 192.168.0.10  TCP   80 > 1282 [ACK]
  9 0.099450 192.168.0.253 192.168.0.10  HTTP  HTTP/1.1 200 OK
 10 0.099724 192.168.0.253 192.168.0.10  HTTP  Continuation
 11 0.102794 192.168.0.10  192.168.0.253 TCP   1282 > 80 [ACK]
 12 0.102915 192.168.0.253 192.168.0.10  HTTP  Continuation
 13 0.280331 192.168.0.10  192.168.0.253 TCP   1282 > 80 [ACK]

  • La trame 4 représente la première requête du client
  • La trame 6 renvoie le document demandé, c'est à dire la page d'accueil du site.
  • La trame 7 indique une requête supplémentaire pour l'image
  • Les trames 9, 10 et 12 représentent l'envoi par le serveur de l'image demandée. Les données sont trop volumineuses pour entrer dans une seule trame. Ici, il en faut trois.

La première requête HTTP

Pour cette première analyse, je laisse volontairement la totalité de la trame, afin de bien montrer que HTTP est un protocole « application », qui s'appuie sur TCP/IP, lui-même s'appuyant sur Ethernet dans cet exemple.  Avec une connexion PPPoE, on aurait une couche supplémentaire introduite par PPP

Frame 4 (380 on wire, 380 captured)
    Arrival Time: Apr 13, 2002 16:07:10.266248000
    Time delta from previous packet: 0.000861000 seconds
    Time relative to first packet: 0.001408000 seconds
    Frame Number: 4
    Packet Length: 380 bytes
    Capture Length: 380 bytes

Ethernet II
    Destination: 00:00:b4:bb:5d:ee (00:00:b4:bb:5d:ee)
    Source: 00:20:18:b9:49:37 (00:20:18:b9:49:37)
    Type: IP (0x0800)


Internet Protocol, Src Addr: 192.168.0.10, Dst Addr: 192.168.0.253
    Version: 4
    Header length: 20 bytes
    Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00)
        0000 00.. = Differentiated Services Codepoint: Default (0x00)
        .... ..0. = ECN-Capable Transport (ECT): 0
        .... ...0 = ECN-CE: 0
    Total Length: 366
    Identification: 0xc99c
    Flags: 0x04
        .1.. = Don't fragment: Set
        ..0. = More fragments: Not set
    Fragment offset: 0
    Time to live: 128
    Protocol: TCP (0x06)
    Header checksum: 0xad95 (correct)
    Source: 192.168.0.10 (192.168.0.10)
    Destination: 192.168.0.253 (192.168.0.253)

Transmission Control Protocol, Src Port: 2752, Dst Port: 80
    Source port: 2752 (2752)
    Destination port: 80 (80)
    Sequence number: 1135843004
    Next sequence number: 1135843330
    Acknowledgement number: 2485285689
    Header length: 20 bytes
    Flags: 0x0018 (PSH, ACK)
        0... .... = Congestion Window Reduced (CWR): Not set
        .0.. .... = ECN-Echo: Not set
        ..0. .... = Urgent: Not set
        ...1 .... = Acknowledgment: Set
        .... 1... = Push: Set
        .... .0.. = Reset: Not set
        .... ..0. = Syn: Not set
        .... ...0 = Fin: Not set
    Window size: 17520
    Checksum: 0x6ec7 (correct)

Hypertext Transfer Protocol
    GET / HTTP/1.1\r\n                        La version du protocole HTTP utilisé.
                                              Suivent les informations supplémentaires qu'envoie le client au serveur...
    Accept: image/gif,                        Les images gif...
            image/x-xbitmap,                  Les images bitmap (bmp par exemple)
            image/jpeg,                       Les images jpeg...
            image/pjpeg,
            application/vnd.ms-powerpoint,    Les trois lignes qui suivent
            application/vnd.ms-excel,         Représentent des informations dont l'intérêt
            application/msword,               peut paraitre contestable...
            */*\r\n
    Accept-Language: fr\r\n                   Nous parlons français...
    Accept-Encoding: gzip, deflate\r\n
    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)\r\n
    Host: linux.maison.mrs\r\n                Celle-ci est indispensable au protocole v1.1
    Connection: Keep-Alive\r\n                Notez qu'IE demande à garder la connexion
    \r\n
Si IE se permet d'annoncer au monde entier que les composants principaux de MS Office sont présents sur ma machine, ce n'est pas dans le but de « moucharder ». Si vous désirez télécharger  des documents .doc, .xls ou .ppt, ces documents devront être transférés par le serveur au format MIME, comme une image gif ou jpg. Internet Explorer informe qu'il accepte ce type de documents. Dans la pratique, tous les navigateurs les acceptent, mais vous proposeront seulement de les enregistrer en tant que fichiers. Ici, IE indique qu'il est capable de les afficher lui-même.

La page d'accueil arrive

Frame 6 (710 on wire, 710 captured)
...
Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
    Date: Sun, 14 Apr 2002 09:30:12 GMT\r\n
    Server: Apache-AdvancedExtranetServer/1.3.23 (Mandrake Linux/4mdk) 
            auth_ldap/1.6.0 
            mod_ssl/2.8.7 
            OpenSSL/0.9.6c 
            PHP/4.1.2\r\n
    Last-Modified: Sun, 14 Apr 2002 09:29:32 GMT\r\n
    ETag: « 57d44-116-3cb94bfc »\r\n
    Accept-Ranges: bytes\r\n
    Content-Length: 278\r\n
    Keep-Alive: timeout=15, max=100\r\n
    Connection: Keep-Alive\r\n
**    Content-Type: text/html\r\n
**    \r\n

    Data (278 bytes)
0000  3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 0d 0a   <html>..<head>..
0010  3c 74 69 74 6c 65 3e 55 6e 74 69 74 6c 65 64 20   <title>Untitled 
0020  44 6f 63 75 6d 65 6e 74 3c 2f 74 69 74 6c 65 3e   Document</title>
0030  0d 0a 3c 6d 65 74 61 20 68 74 74 70 2d 65 71 75   ..<meta http-equ
0040  69 76 3d 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65   iv="Content-Type
0050  22 20 63 6f 6e 74 65 6e 74 3d 22 74 65 78 74 2f   "content="text/
0060  68 74 6d 6c 3b 20 63 68 61 72 73 65 74 3d 69 73   html; charset=is
0070  6f 2d 38 38 35 39 2d 31 22 3e 0d 0a 3c 2f 68 65   o-8859-1">..</he
0080  61 64 3e 0d 0a 0d 0a 3c 62 6f 64 79 20 62 67 63   ad>....<body bgc
0090  6f 6c 6f 72 3d 22 23 46 46 46 46 46 46 22 20 74   olor="#FFFFFF" t
00a0  65 78 74 3d 22 23 30 30 30 30 30 30 22 3e 0d 0a   ext="#000000">..
00b0  3c 70 3e 48 65 6c 6c 6f 20 77 6f 72 6c 64 2e 0d   <p>Hello world..
00c0  0a 3c 2f 70 3e 0d 0a 3c 70 3e 3c 69 6d 67 20 73   .</p>..<p><img s
00d0  72 63 3d 22 69 6d 61 67 65 73 2f 74 75 78 2e 67   rc="images/tux.g
00e0  69 66 22 20 77 69 64 74 68 3d 22 39 37 22 20 68   if" width="97" h
00f0  65 69 67 68 74 3d 22 31 31 35 22 3e 0d 0a 3c 2f   eight="115">..</
0100  70 3e 0d 0a 3c 2f 62 6f 64 79 3e 0d 0a 3c 2f 68   p>..</body>..</h
0110  74 6d 6c 3e 0d 0a                                 tml>..
C'est au navigateur de se débrouiller pour aller chercher les données de cette image, à partir des références fournies. Tous ceux qui pratiquent le HTML le savent bien…

Ceci justifie la présence de la requête de la trame 7 :

Frame 7 (298 on wire, 298 captured)
...
Hypertext Transfer Protocol
    GET /images/tux.gif HTTP/1.1\r\n     Appel d'une référence relative
    Accept: */*\r\n
    Referer: http://linux.maison.mrs\r\n Depuis la page indiquée, ce qui aboutit
                                         à la référence absolue:
                                         http://linux.maison.mrs/images/tux.gif
    Accept-Language: fr\r\n
    Accept-Encoding: gzip, deflate\r\n
    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)\r\n
    Host: linux.maison.mrs\r\n
    Connection: Keep-Alive\r\n
    \r\n

Le serveur envoie les données à partir de la trame 9 :

Frame 9 (1514 on wire, 1514 captured)
....
Hypertext Transfer Protocol
    HTTP/1.1 200 OK\r\n
    Date: Sun, 14 Apr 2002 09:30:12 GMT\r\n
    Server: Apache-AdvancedExtranetServer/1.3.23 (Mandrake Linux/4mdk) 
            auth_ldap/1.6.0 
            mod_ssl/2.8.7 
            OpenSSL/0.9.6c 
            PHP/4.1.2\r\n
    Last-Modified: Sun, 14 Apr 2002 09:29:32 GMT\r\n
    ETag: « cf98a-a20-3cb94bfc »\r\n
    Accept-Ranges: bytes\r\n
    Content-Length: 2592\r\n
    Keep-Alive: timeout=15, max=99\r\n
    Connection: Keep-Alive\r\n
    Content-Type: image/gif\r\n
    \r\n
     Data (1082 bytes)

0000  47 49 46 38 39 61 61 00 73 00 e6 00 00 04 04 05   GIF89aa.s.......
0010  a4 85 2f 49 48 47 a3 48 49 ed c8 10 c5 a4 32 9b   ../IHG.HI.....2.
0020  69 07 ba 85 0e be 67 70 a9 3a 3c 85 66 1d d7 a5   i.....gp.:<.f...
0030  15 c4 48 4c a7 76 0c 8b 88 8d e8 e8 fc b8 88 94   ..HL.v..........
...

Conclusions

Nous avons vu quelques choses intéressantes :

  • Le navigateur, en gros, fait ce que nous avons fait avec Telnet, à quelques détails près :
    • Il envoie un certain nombre d'informations que nous n'avons pas envoyées avec notre Telnet. Parmi celles-ci,  Host: linux.maison.mrs, c'est à dire le nom du serveur, qui est le seul paramètre obligatoire pour HTTP 1.1, facultatif en HTTP 1.0 ;
    • Il se débrouille tout seul pour appeler tous les documents nécessaires à l'affichage correct de cette page (ici l'image de Tux), et reconstitue correctement cette image pour l'afficher.

Le coup du cache...

Puisque nous y sommes, profitons en pour observer un comportement intéressant du navigateur :  La mise en cache des pages consultées.

L'internaute ferme son navigateur. Quelques instants plus tard, il l'ouvre à nouveau et réclame la même page. Que va-t-il se passer ?

No. Time     Source        Destination   Proto Info
  1 0.000000 192.168.0.10  192.168.0.253 TCP   2632 > 80 [SYN]
  2 0.000144 192.168.0.253 192.168.0.10  TCP   80 > 2632 [SYN, ACK]
  3 0.000540 192.168.0.10  192.168.0.253 TCP   2632 > 80 [ACK]
  4 0.001342 192.168.0.10  192.168.0.253 HTTP  GET / HTTP/1.1
  5 0.001461 192.168.0.253 192.168.0.10  TCP   80 > 2632 [ACK]
  6 0.004186 192.168.0.253 192.168.0.10  HTTP  HTTP/1.1 304 Not Modified
  7 0.200392 192.168.0.10  192.168.0.253 TCP   2632 > 80 [ACK]
La réponse n'est pas la même que dans le cas précédent. Voyons ceci de plus près…

La requête

Frame 4 (336 on wire, 336 captured)
...
Hypertext Transfer Protocol
    GET / HTTP/1.1\r\n
    Accept: */*\r\n
    Accept-Language: fr\r\n
    Accept-Encoding: gzip, deflate\r\n
    If-Modified-Since: Sat, 13 Apr 2002 13:40:06 GMT\r\n  Cette ligne n'apparaissait pas dans la requête précédente...

    If-None-Match: "57d44-d2-3cb83536"\r\n
    User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)\r\n
    Host: linux.maison.mrs\r\n
    Connection: Keep-Alive\r\n
    \r\n
Internet Exlorer demande au serveur la page, si elle a été modifiée depuis la date de son précédent chargement, tout simplement parce qu'il a conservé en cache cette page que nous avons déjà demandée il n'y a pas si longtemps.

La réponse

Frame 6 (327 on wire, 327 captured)
...
Hypertext Transfer Protocol
    HTTP/1.1 304 Not Modified\r\n
    Date: Sat, 13 Apr 2002 13:53:52 GMT\r\n
    Server: Apache-AdvancedExtranetServer/1.3.23 (Mandrake Linux/4mdk) 
            auth_ldap/1.6.0 mod_ssl/2.8.7 OpenSSL/0.9.6c PHP/4.1.2\r\n
    Connection: Keep-Alive\r\n
    Keep-Alive: timeout=15, max=100\r\n
    ETag:  "57d44-d2-3cb83536"\r\n
    \r\n
Le serveur s'est contenté de répondre que la page n'a pas été modifiée…

Le navigateur va donc réafficher la page qu'il a conservée en cache. Cette méthode de travail présente deux particularités:

  • Le temps d'affichage est considérablement raccourci lorsque l'on navigue dans un site, puisque les pages déjà chargées ne le sont généralement plus si l'on revient dessus.
  • L'espace requis pour le cache gonfle considérablement et peut occuper jusqu'à plusieurs dizaines de Mo sur votre disque…
Méfiez vous des effets de bord du cache local ! Il existe de nombreux cas où le contrôle sur la modification n'est pas efficace. Votre navigateur affichera alors une version obsolète de la page. Ceci est surtout gênant pour les développeurs de sites.

Un procédé analogue est également employé par les serveurs proxy.

Toutes les commandes de HTTP 1.0

La compréhension de la navigation ne nécessite pas la connaissance de toutes les commandes, loin de là. Aussi, nous ne les verrons pas en détail.

  • La commande HEAD. Cette commande, qui existe également en v1.0, permet de ne demander au serveur que l'en-tête, sans le document.
  • La commande GET. Nous l'avons déjà vue, ce n'est pas la peine d'y revenir, si ce n'est pour rappeler que l'option « Host: » doit impérativement figurer dans la commande, pour être acceptée par le serveur en version 1.1, mais pas en version 1.0.
  • La commande POST. C'est la commande qui permet d'envoyer des données au serveur, généralement par l'intermédiaire d'un formulaire.

Et HTTP 1.1 ?

Il propose quelques commandes supplémentaires, comme :

  • OPTIONS qui permet d'interroger le serveur sur les options disponibles.
  • TRACE qui est en quelque sorte une commande de débogage
  • DELETE qui permet de détruire un fichier sur le serveur (généralement désactivée, vous vous en doutez…)
  • PUT Qui permet d'écrire des fichiers sur le serveur, généralement désactivée également.

Ces deux dernières commandes sont introduites pour permettre une mise à jour des sites distants via HTTP.