Roy T. Fielding (Department of Information and Computer Science)James Gettys (World Wide Web Consortium)Jeffrey C. Mogul (Compaq Computer Corporation)Henrik Frystyk Nielsen (World Wide Web Consortium)Larry Masinter (Xerox Corporation)Paul J. Leach (Microsoft Corporation)Tim Berners-Lee (World Wide Web Consortium)June19992007-06-06
Pas besoin de présenter HTTP, un des
protocoles les plus importants de l'Internet,
en raison du succès du Web. Notre RFC était la
norme officielle de HTTP, depuis remplacé par une série de huit RFC.
Le Web a beaucoup évolué depuis la sortie du RFC mais son protocole
principal reste le même, HTTP
1.1. Contrairement aux normes portant sur le format des données, qui
sont en général réalisées par le W3C, HTTP est
une norme IETF. Le RFC est très long car, si HTTP
est très simple dans son principe (ce qui a beaucoup aidé à son
succès), il contient de nombreux détails (par exemple de nombreux
en-têtes peuvent être transmis entre serveurs et clients pour des options
variées) et je parie que peu de
logiciels mettent en œuvre la totalité du RFC.
On notera aussi que, si HTTP est surtout connu pour permettre à un
navigateur Web d'accéder à une page
HTML, il peut aussi servir de protocole pour
permettre à deux applications de communiquer, notamment en suivant
l'architecture REST (voir et
pour des exemples REST). Ainsi, des protocoles comme IPP () ou XCAP, XML Configuration Access
Protocol (), utilisent HTTP (pas forcément
en REST). Un RFC a même été écrit pour essayer de décrire dans quels
cas il était souhaitable de réutiliser HTTP pour un protocole
Internet, le .
Le protocole repose sur un mécanisme requête /
réponse très simple (section 1.4). Le client se connecte en
TCP au serveur, envoie une requête sur une
seule ligne, la requête incluant un URI, éventuellement des options sous forme d'en-têtes
analogues à ceux du courrier électronique (Accept:
text/*), éventuellement un contenu, et reçoit une réponse sous forme d'un code
numérique indiquant de manière non ambigüe le résultat de la requête,
d'en-têtes optionnels et d'un corps, la ressource demandée.
Le terme de ressource est défini dans la
section 1.3. Il est plus général que celui de page. Une ressource
n'est pas forcément une page HTML, ce n'est même pas toujours un
fichier.
Il existe plusieurs requêtes, définies en section 5.1.1. Les plus
connues sont GET (récupérer une ressource, sans
rien modifier), POST (modifier une ressource),
PUT (créer une nouvelle ressource) et
DELETE (comme son nom l'indique). Mais il faut
noter que, si les applications REST, par définition, utilisent les
différentes requêtes pour les différentes actions qu'elles peuvent
effectuer, le navigateur Web typique ne connait que GET et POST. Et
beaucoup d'applications ne sont pas REST, utilisant par exemple le
POST comme un fourre-tout, l'action étant exprimée par un paramètre du
POST et pas par la requête. C'est dommage, car la sémantique de chaque
requête est rigoureusement définie par le RFC (voir particulièrement
la section 9.1), notamment pour ce qui
concerne les caches, l'idempotence et les invariants comme
GET(PUT(x)) = x (en français : après un PUT, un GET du même
URI doit donner la ressource transmise lors du
PUT).
Les codes numériques de retour sont à trois chiffres, le premier
chiffre indiquant la catégorie (2 est un succès, 4 une erreur dûe au
client, 5 une erreur dûe au serveur, etc), comme avec
SMTP. L'erreur la plus célèbre est sans doute
la 404, « ressource non trouvée », décrite dans la section 10.4.5, qui se
produit par exemple lorsqu'un webmestre n'a pas lu l'article
Cool URIs
don't change et a changé ses fichiers de place.
Il existe évidemment une pléthore de logiciels clients et serveurs
HTTP, puisque les navigateurs Web et les serveurs comme Apache, parlent tous HTTP. Mais, pour apprendre le
protocole, les clients en ligne de commande
sont très pratiques. Ici, curl avec l'option
--verbose permet de voir les requêtes envoyées par le
client au serveur (elles sont précédées d'un signe >) et les
réponses du serveur (précédées d'un signe <) :
GET /2616.html HTTP/1.1
> User-Agent: curl/7.15.5 (i486-pc-linux-gnu) libcurl/7.15.5 OpenSSL/0.9.8c zlib/1.2.3 libidn/0.6.5
> Host: www.bortzmeyer.org
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Wed, 06 Jun 2007 13:20:03 GMT
< Server: Apache/1.3.26 Ben-SSL/1.48 (Unix) Debian GNU/Linux AuthMySQL/3.1 mod_throttle/3.1.2 mod_watch/3.17
< Connection: close
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=iso-8859-1
...
]]>
Ou bien, avec wget, son option --debug, et une ressource qui existe (dans le cas
ci-dessus, le fichier n'avait pas encore été copié sur le serveur,
d'où le résultat 404) :
% wget --debug http://www.bortzmeyer.org/1035.html
...
Connecting to www.bortzmeyer.org|80.67.170.20|:80... connected.
...
---request begin---
GET /1035.html HTTP/1.0
User-Agent: Wget/1.10.2
Accept: */*
Host: www.bortzmeyer.org
Connection: Keep-Alive
---request end---
...
---response begin---
HTTP/1.1 200 OK
Date: Wed, 06 Jun 2007 13:23:48 GMT
Server: Apache/1.3.26 Ben-SSL/1.48 (Unix) Debian GNU/Linux AuthMySQL/3.1 mod_throttle/3.1.2 mod_watch/3.17
Last-Modified: Wed, 30 May 2007 18:21:58 GMT
ETag: "2e6e69-19a6-465dc0c6"
Accept-Ranges: bytes
Content-Length: 6566
Connection: close
Content-Type: text/html; charset=utf-8
Afficher les en-têtes du dialogue entre le client et le serveur peut
aussi se faire, si on utilise le navigateur
Firefox, avec son extension « HTTP headers ».
Pour un exemple concret des options qui peuvent être passées au
serveur via les en-têtes, prenons
If-Modified-Since (section 14.25) qui permet au
client d'annoncer au serveur « J'ai déjà une version de cette
ressource, obtenue à telle date, ne me l'envoie que si tu as une
version plus récente ». Par exemple, sur le site des étiquettes de langue, on
a le registre dans divers formats. Ces formats sont produits
localement, après avoir transféré le registre original. Celui-ci étant
un gros fichier, on l'analyse pour trouver sa date et on utilise
l'option --time-cond de curl pour générer un
en-tête If-Modified-Since. Voici le script :
#!/bin/sh
MYURL=http://www.langtag.net/
LTR_URL=https://www.iana.org/assignments/language-subtag-registry
LTR_LOCAL=language-subtag-registry
if [ -e ${LTR_LOCAL} ]; then
ltr_date=`head -n 1 ${LTR_LOCAL} | cut -d" " -f2`
current_date=`date +"%Y%m%d" --date="${ltr_date} +1 day"`
else
# Trick to force a downloading
current_date="19700101"
fi
curl --verbose --output ${LTR_LOCAL}.TMP \
--compressed \
--referer ${MYURL} \
--proxy "" \
--time-cond "${current_date}" \
${LTR_URL}
...
Cela donne ceci, si le fichier n'existe pas (on a pris une date
arbitraire dans le passé) :
If-Modified-Since: Thu, 01 Jan 1970 00:00:00 GMT
>
< HTTP/1.1 200 OK
< Last-Modified: Fri, 04 May 2007 18:36:16 GMT
< Content-Length: 80572
]]>
Si par contre le fichier existe et on a la version du 5 mars 2007,
curl affiche :
If-Modified-Since: Sat, 05 May 2007 00:00:00 GMT
>
< HTTP/1.1 304 Not Modified
...
]]>
et le fichier n'est pas transféré.
Naturellement, si on veut développer un client HTTP, au delà de ce
que permet curl (qui permet déjà beaucoup de
choses), on dispose de nombreuses bibliothèques toutes faites comme
httplib
pour Python, libcurl (avec le programme du même nom) ou
neon pour C, etc. HTTP est suffisamment simple
pour qu'on puisse écrire rapidement un client, même sans ces
bibliothèques comme le montre cet exemple en
Python.
À noter que notre RFC utilise, pour décrire le protocole, une
variante de l'ABNF standard, décrite en section
2.1, ce qui a suscité
un certain nombre de problèmes.
Ce a été remplacé en juin 2014 par une série de huit RFC, commençant par le
. Le protocole HTTP 1.1 reste le
même.