====== 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 : {{ :http:hello_world.gif |}} 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 [[http://www.w3.org/|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íÈ?Å?2i?º
??gp©:<
f?×¥?ÄHL§v?èèü?©?°¡»¹ÅØ ?ÉÈ×rI?
(''}y|]?Ë8:µ}?y}ÁX_À!? hnÎ??N4?è×?XXZò»?W????98;ýýûhgjºx¹?ôÖ?«Y^ZXbE?
©¶ÙÙëà³LhhÓ?
NE?4ÜÌÀXYªyU<?Ç?ã©??...
...
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\nSi 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\nLe 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\nInternet 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...