Date de publication du RFC : Janvier 2005
Auteur(s) du RFC : Tim Berners-Lee (World Wide Web Consortium), Roy T. Fielding (Day Software), Larry Masinter (Adobe Systems Incorporated)
Chemin des normes
Première rédaction de cet article le 15 janvier 2008
Le Web repose sur trois piliers : le format HTML, le protocole HTTP et, surtout, le concept d'URI, une famille d'identificateurs à la fois compréhensibles par les humains et automatiquement analysables par les programmes. La syntaxe des URI est normalisée dans ce RFC.
Il y a des URI partout aujourd'hui. Sur le
côté des autobus, sur les cartes de visite, dans les documentations
techniques, dans les articles de la presse papier. Ils sont une des
marques les plus spectaculaires du succès du Web. Peu de gens il y a
quinze ans osaient dire qu'on verrait des URI à la devanture des
boulangeries et des teintureries. Le Minitel
n'avait pas d'équivalent (« Tapez 3615 JERAQUE puis choisissez le menu
Locations puis allez dans Dernières locations ») et même les
informaticiens connectés à Internet utilisaient souvent de telles
descriptions (avant les URI, il n'y avait pas de syntaxe standard et
analysable pour
un nom de fichier accessible en FTP, on disait
« ftp.sunet.se
, dans
/mirrors/macarchive/decoders/foobar.bin
». (Les URI ont été
normalisés pour la première fois dans le RFC 1630 en
1994.) Même si certains utilisateurs n'ont pas encore compris les URI
et donnent des instructions assez miniteliennes à leurs interlocuteurs
comme « Tapez
Locations en Bretagne dans Google » ou bien « Allez sur
www.exemple.fr
et choisissez Locations puis
Bretagne », ils sont largement diffusés, bien au-delà du cercle des
quelques informaticiens et physiciens qui les utilisaient en 1990.
Grâce aux URI, on a donc, pour la première fois, des identificateurs standards, à la syntaxe rigoureuse (donc pouvant être traités par un programme) mais décodables et mémorisables par des humains. Ils permettent de désigner sans ambiguïté une ressource (contrairement aux moteurs de recherche et aux systèmes de mots-clés, qui sont une référence floue et instable).
Aujourd'hui, qu'est-ce qu'un URI, du point de vue des normes ? URI
est le terme qui désigne toute la famille des identificateurs du
Web. Les deux membres les plus connus de cette famille sont les
URL (RFC 1738) et les URN mais
notre RFC préfère ne pas trop utiliser ces termes et ne parler que
d'URI (section 1.1.3, voir aussi RFC 3305), entre autres
parce que le statut de « localisateur » (locator)
n'est pas clair (un URI http
n'est pas forcément
un URL, voir section 1.2.2). La syntaxe qui est décrite dans le RFC est
générique, elle s'applique à tous les URI, quel
que soit leur plan (scheme), la partie de l'URI avant le
premier deux-points. Des
syntaxes plus précises et plus restrictives peuvent être spécifiées
par certains plans (le plus connu des plans est
http
). Notre RFC fournit, lui, ce qui est commun à tous
les plans.
Le « cahier des charges » avait été écrit dans les RFC 1736 et RFC 1737. Le premier RFC qui l'avait mis en œuvre était le RFC 2396, que notre RFC 3986 remplace. Les trois lettres du sigle URI indiquent bien la démarche (section 1.1) :
La section 1.1.1 décrit la syntaxe générique, détaillée en section 3 : un URI commence par un plan, suivi d'un deux-points, d'une autorité optionnelle (par exemple le nom de domaine d'une machine), puis d'un chemin (path), dont la syntaxe dépend du plan, d'une requête optionnelle et d'un fragment optionnel. Voici quelques exemples d'URI, inspirés du RFC :
http://fr.wikipedia.org/wiki/Jacques_Brel ldap://[2001:DB8::7:23A]/dc=example,dc=org?objectClass?one tel:+1-816-555-1212 urn:oasis:names:specification:docbook:dtd:xml:4.1.2
Ainsi,
droite://example.org:8042/par/ici?label=ChezMoi#note
se décompose en :
droite
: le plan,example.org:8042
: l'autorité,/par/ici
: le chemin,label=ChezMoi
: la requête,note
: l'identificateur d'un fragment.En Python, on peut facilement analyser un URI avec le module urlparse :
>>> from urlparse import urlparse >>> scheme,authority,path,params,query,fragment = urlparse("http://fr.wikipedia.org/wiki/Jacques_Brel#Discographie") >>> scheme 'http' >>> path '/wiki/Jacques_Brel' >>> fragment 'Discographie'
La section 1.2 revient sur les choix de conception. Elle part d'un scénario où deux personnes échangent des URI en les notant sur une nappe en papier dans un restaurant, ce qui nécessite des URI parlants, mais écrits dans un alphabet répandu (demande qui peut être contradictoire avec la précédente). D'une manière générale, il est préférable d'utiliser de beaux URI. C'est également dans cette section qu'est posé le principe que les URI n'utilisent qu'un jeu de caractères très limité, un sous-ensemble d'ASCII. Cette limite très contraignante est levée avec les IRI du RFC 3987.
La suite de la section parle du problème important de la
récupération (retrieval) d'une
ressource. Une des utilisations les plus courantes d'un URI est pour
accéder à une ressource. Cela nécessite que l'URI soit
résolvable (ce qui n'est pas, en général, le cas
des URN). Mais tous les URI ne sont pas forcément résolvables, ne
serait-ce que parce qu'ils peuvent n'être utilisés que comme
identificateurs (c'est le cas, par exemple, des URI identifiant un
espace de noms XML). Le RFC insiste aussi sur
le fait que le plan n'est pas, en général, un protocole réseau. Même quand
cela semble être le cas (URI http
), divers
mécanismes font que la ressource peut être récupérée par d'autres
moyens que le protocole indiqué (par exemple, pour les documents XML,
on utilise typiquement un catalogue qui indique
une correspondance entre un URI et un fichier local, de sorte que le
processeur XML n'aie pas besoin d'aller sur le réseau).
Puis cette section 1.2 discute le caractère hiérarchique des URI, avec ses composants allant du plus significatif, le plan, tout à fait à gauche, au moins significatif, à droite (à noter que les séparateurs des composants varient, ce que n'auraient pas apprécié Rob Pike et Peter J. Weinberger).
La section 2 est consacrée aux caractères utilisés pour former un
URI, uniquement ASCII, on l'a vu. La section 2.2 donne une liste des
caractères qui, quoique présents dans ASCII, ne peuvent pas être
utilisés pour un URI car ils ont une signification spéciale. C'est le
cas par exemple du # qui introduit
l'identificateur d'un
fragment de la ressource. La section 2.1 normalise le codage
« pour cent » où les caractères non-ASCII sont remplacés par le signe
% suivi de leur valeur en
hexadécimal. (C'est hélas la valeur de chaque octet, pas le point de
code Unicode.) Malheureusement, la section 2 précise qu'aucun
encodage particulier n'est choisi. Il est donc
très difficile de faire des URI utilisant des caractères non-ASCII,
même en utilisant le codage « pour cent » puisqu'il n'y a pas de moyen
d'indiquer l'encodage utilisé. Ainsi,
http://example.org/caf%E9
est sans doute du
Latin-1 mais sans qu'on puisse en être sûr. On
limite les risques en supposant que l'encodage est
UTF-8 comme dans l'URI
http://fr.wikipedia.org/wiki/caf%C3%A9
(C3 A9
est l'encodage en UTF-8 du e accent aigu).
L'encodage d'une chaîne de caractères pour en faire un URI peut être effectuée en XSLT par la fonction EXSLT str:encode-uri, par exemple ainsi :
<xsl:value-of select="str:encode-uri($word, true())"/>
ou bien en Python avec la fonction quote
de la bibliothèque
urllib :
>>> from urllib import quote >>> quote("foo # bar ?") 'foo%20%23%20bar%20%3F'
Voyons plus en détail les différents composants d'un URI. Le
plan, d'abord, décrit en section 3.1. Notre RFC ne contient pas de liste de
plans. Ils doivent être enregistrés à l'IANA,
comme indiqué dans le RFC 7595
(on peut aussi voir le RFC 2718). Les plans les plus connus
sont http
, mailto
et
urn
. tag
(RFC 4151) est moins connu mais est un de mes favoris.
L'autorité (section 3.2) indique quelle entité
va gérer le reste de l'URI. Pour un URN, c'est un simple nom (comme
isbn
pour les ISBN du RFC 8254).Pour le plan http
, l'autorité est
un nom de domaine ou une adresse IP. Dans ce cas, elle peut aussi inclure un nom
d'utilisateur, pour l'authentification. Si l'adresse IP est
IPv6, il faudra un échappement spécial (la
mettre entre crochets), décrit dans le RFC. Enfin, il peut y avoir un
numéro de port, bien que cela pose un problème
avec les enregistrements SRV
(RFC 2782) qui
peuvent aussi spécifier un port.
Le chemin (section 3.3) indique une ressource particulière
pour une autorité. Il est composé de plusieurs parties séparées par
une barre oblique (pour des raisons
historiques : c'est le séparateur de répertoires sur Unix). Si l'URI est de type http
et
que les pages Web sont des fichiers HTML
statiques, le chemin est un répertoire sur le disque dur, mais ce
n'est pas forcément le cas pour tous les URI, loin de là !
La requête (section 3.4), située après le ? est un composant optionnel de l'URI. Elle est utilisée typiquement pour les ressources générées dynamiquement, en réponse à la question que représente ce composant, mais rien dans la norme n'oblige à les utiliser pour cela.
Le fragment (section 3.5), qui se place après
le # désigne une partie
de la ressource. Si l'URI est résolvable, le fragment est interprété
par le client uniquement, le serveur ne le voit pas. L'exemple le plus
courant est un fragment composé d'un nom, par exemple, si la ressource
est en HTML, un attribut name
ou
id
d'un élément HTML.
La section 4 explique l'utilisation des URI par un programme. Elle
couvre le cas d'URI relatifs (la section 5
développe ce point) ou bien, dans la section
4.5, d'URI dont le début, par exemple le plan, manque. De tels URI sont
courants
dans la presse ou la publicité, par exemple
www.afnic.fr
au lieu de
http://www.afnic.fr/
. Le RFC note que cette
pratique, quoique courante, est dangereuse car elle dépend du contexte
pour une bonne résolution. Elle ne devrait pas être utilisé pour des
URI à longue durée de vie.
La section 6 s'attaque à un problème difficile, la
normalisation des URI. On a souvent besoin de
comparer deux URI. Par exemple, un navigateur
Web affiche les pages déjà visitées avec une couleur
différente. Pour cela, il doit comparer les URI présents dans la page
Web avec son historique des liens visités. Or, faire cette
comparaison octet par octet peut rater beaucoup d'égalités. Par
exemple, le plan est insensible à la casse donc
urn:bortzmeyer:exemple:1
est égal à
URN:bortzmeyer:exemple:1
. D'autres
canonicalisations dépendent du plan. Par exemple, avec les URI
http
, l'autorité est un nom de domaine et les
noms de domaine sont insensibles à la casse donc
http://www.demaziere.fr/eve/
est égal à
http://www.Demaziere.FR/eve/
(la section 6.2.2.1
détaille les comparaisons insensibles à la casse ; sauf indiqué
explicitement, les composants d'un URI sont sensibles à la casse). Le navigateur doit
donc normaliser les URI avant de les comparer.
Le RFC note qu'aucune normalisation ne sera parfaite. Après tout, deux URI différents peuvent parfaitement indiquer la même ressource, sans que le logiciel ne puisse le deviner.
Une section 7 détaille les questions de sécurité associées aux URI. Elle note que beaucoup de ces questions n'ont pas de solution technique et qu'elles dépendent d'une approche sociale. Par exemple, la persistence des URI, un sujet de recherche très actif, dépend plutôt de bonnes pratiques sociales (notamment d'organisations stables, et qui peuvent voir sur le long terme) que d'une technique particulière. Une autre question de sécurité est traitée dans la section 7.6, c'est celle du hameçonnage, pour les cas où l'auteur du site de hameçonnage tente de fabriquer des URI trompeurs (très peu d'utilisateurs vérifient les URI et la plupart des hameçonneurs ne se donnent même pas la peine d'en fabriquer de vraisemblables). La syntaxe des URI est suffisamment compliquée pour que certains URI soient difficiles à décoder par un humain.
Enfin, notons que l'appendice D expose toutes les différences entre ce RFC et son prédécesseur, le RFC 2396. La principale est la section sur la normalisation, qui a été complètement refaite, mais il y en a beaucoup d'autres, en général de faible importance.
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)