Thèse de Roy T. Fielding - Traduction du Chapitre 5 : REST

Statut du document traduit

Ceci est une traduction du chapitre 5 de la thèse de Roy T. Fielding.

Il ne s'agit pas de la version officielle mais d'une traduction destinée aux francophones afin de mieux comprendre l'original. Le document complet original,

«Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation, University of California, Irvine, 2000.»

est disponible à l'adresse http://www.ics.uci.edu/%7Efielding/pubs/dissertation/rest_arch_style.htm.

La traduction a été réalisée en respectant les droits de reproduction définis par auteur, consultables dans la FAQ du document d'origine.

Avertissement

Des erreurs ont pu survenir malgré le soin apporté à ce travail. Si vous décelez une erreur, n'hésitez pas à me contacter.

Notes sur la traduction

Certains concepts sont difficiles à exprimer en français, ou peuvent bénéficier d'une explication. Par endroits, les expressions originales en anglais viennent en renfort dans le texte sous cette forme :
ex. traduction [ndt. translation]

Representational State Transfer (REST)

Ce chapitre présente le modèle d’architecture REST (Representational State Transfer) pour les systèmes d’hypermedia répartis. Il décrit les principes logiciels sur lesquels s’appuie REST et les contraintes, liées aux interactions, retenues pour maintenir ces principes. Par ailleurs, ces contraintes sont confrontées à celles d'autres modèles d’architecture. REST est un modèle hybride dérivé de plusieurs modèles basés sur les concepts réseau, décrits dans le chapitre 3, et combiné avec des contraintes supplémentaires qui définissent une interface uniforme de connecteur. Le framework d'architecture logicielle décrit au chapitre 1 est utilisé pour définir les éléments de REST et pour examiner des exemples de processus, de connecteurs et de vues de données des architectures prototypées.

5.1 Dériver REST

Le principe de conception de l'architecture du Web peut être décrit par un modèle comprenant l'ensemble des contraintes appliquées aux éléments de cette architecture. En examinant l'impact de chaque contrainte lors de son ajout au modèle, nous pouvons identifier les propriétés induites par les contraintes du Web. Des contraintes complémentaires peuvent alors être appliquées pour former un nouveau modèle d’architecture. Celui-ci reflétera mieux les propriétés que l’on souhaite qu’une architecture moderne du Web possède. Cette section donne une vue d'ensemble de REST en déroulant un processus qui va le dériver comme un modèle d’architecture. Les sections suivantes décriront plus en détail les contraintes spécifiques qui composent le modèle REST.

5.1.1 Commençons par le modèle vide [ndt. null]

Il existe deux façons classiques d’aborder un processus de conception d’une architecture, qu’il soit appliqué au bâtiment ou au logiciel. La première est qu'un concepteur parte de zéro -- une feuille blanche, un tableau blanc -- et construise petit à petit une architecture à base de composants connus jusqu'à ce que cette architecture satisfasse les besoins du système envisagé. La seconde approche consiste à commencer par les besoins du système dans son ensemble, sans contraintes. Puis, de façon incrémentale, les contraintes sont identifiées et appliquées aux éléments du système afin de différencier l'espace de conception et de permettre aux forces qui influencent son comportement de s’arranger naturellement, en harmonie avec le système. Le premier procédé met en avant la créativité et une imagination sans limite. Le second met l’accent sur les contraintes et la compréhension du contexte du système. REST a été développé en utilisant le second processus. Les schémas 5-1 à 5-8 l’illustrent graphiquement, en montrant comment les contraintes appliquées au fur et à mesure pourraient différencier la vue processus de l'architecture.

Le modèle vide [ndt.null] (schéma 5-1) représente simplement un ensemble vide de contraintes. D'un point de vue architectural, le modèle vide décrit un système dans lequel aucune frontière distincte n’existe entre les composants. C'est le point de départ pour notre description de REST.

Schéma 5-1: Le modèle videSchéma 5-1: Le modèle vide

5.1.2 Client-Serveur

Les premières contraintes que l’on ajoute à notre modèle hybride sont celles du modèle architectural client/serveur (schéma 5-2), décrit dans la section 3.4.1. La séparation des concepts est le principe sous-jacent du client/serveur. En séparant les problématiques d'interface utilisateur de celles du stockage de données, nous améliorons la portabilité de l'interface utilisateur à travers les diverses plates-formes. Nous améliorons par ailleurs la possibilité de montée en charge en simplifiant les composants du serveur. Mais l’aspect sans doute le plus important pour le Web est que cette séparation permet aux composants d'évoluer indépendamment. De ce fait, ce modèle s’adapte aux larges besoins (à l’échelle d’Internet) des multiples domaines organisationnels.

Schéma 5-2: Le modèle client-serveurSchéma 5-2: Le modèle client/serveur

5.1.3 Sans état (Stateless)

Nous ajoutons ensuite une contrainte à l'interaction entre le client et le serveur : la communication doit être par nature sans état, comme dans le modèle «client/serveur sans état» [ndt. CSS pour client-stateles-server] de la section 3.4.3 (schéma 5-3). Ainsi chaque requête du client vers le serveur doit contenir toutes les informations nécessaires pour que cette demande soit comprise, et elle ne peut tirer profit d'aucun contexte stocké sur le serveur. L'état de la session est donc entièrement détenu par le client.

Schéma 5-3: Le modèle client-serveur-sans étatSchéma 5-3: Le modèle client-serveur-sans état

Cette contrainte induit les propriétés suivantes : visibilité, fiabilité et faculté de montée en charge. La visibilité est améliorée car un système de supervision n’a pas besoin de regarder au delà des simples informations de la requête afin d’en déterminer sa nature complète. La fiabilité est améliorée car il est plus simple de faire face (recovery) à des échecs partiels [133]. Enfin, la possibilité de montée en charge est meilleure, car le fait de ne pas stocker l’état entre les requêtes permet aux composants du serveur de libérer rapidement les ressources. De plus, la mise en œuvre est grandement simplifiée car le serveur n’a pas besoin de gérer l'utilisation des ressources à travers les différentes requêtes.

Comme la plupart des choix d’architecture, la contrainte sans état fait apparaître un compromis quant à la conception. Son inconvénient est qu'elle peut diminuer les performances réseau en répétant les données (surplus par interaction) envoyées dans une série de requêtes, puisqu’elles ne peuvent pas être conservées sur le serveur dans un contexte partagé. En outre, laisser l'état de l’application côté client réduit le contrôle qu’a le serveur sur le comportement cohérent de l’application, puisqu'elle devient dépendante de la mise en oeuvre correcte de la sémantique au travers des multiples versions de client.

5.1.4 Cache

Afin d'améliorer les performances réseau, nous ajoutons des contraintes de cache afin de former le modèle «client/serveur sans état avec cache» de la section 3.4.4 (schéma 5-4). Les contraintes de cache imposent que les données d’une réponse à une requête soient marquées, implicitement ou explicitement, comme pouvant être mises ou non en cache. Si une réponse peut être mise en cache [ndt. «cacheable»], alors le cache du client obtient le droit de réutiliser ces données de réponse pour des demandes ultérieures équivalentes.

Schéma 5-4: Le modèle client/serveur sans état avec cacheSchéma 5-4: Le modèle client/serveur sans état avec cache

L’ajout de contraintes de cache a l’avantage d’offrir la possibilité d’éliminer tout ou partie de certaines interactions, améliorant ainsi l'efficacité, la montée en charge et la perception de performance qu’a l’utilisateur, en réduisant la latence moyenne dans une série d'interactions. Le compromis est qu'un cache peut diminuer la fiabilité si les données obsolètes qui y sont présentes diffèrent de manière significative des données qui auraient été obtenues par une requête envoyée directement au serveur.

L'architecture première du Web, comme elle est présentée dans le schéma 5-5 [11], a été définie par l'ensemble des contraintes «client/serveur sans état avec cache». En effet, le principe de conception présenté pour l'architecture du Web avant 1994 se concentrait sur des interactions client/serveur sans état pour l'échange de documents statiques via Internet. Les protocoles pour des interactions communicantes avaient certes un support basique de caches non partagés, mais ne contraignaient pas l'interface à un ensemble cohérent de définitions sémantiques pour toutes les ressources. Au lieu de cela, le Web s’est appuyé sur l'utilisation d'une mise en œuvre commune d’une librairie client/serveur (libwww du CERN) afin de maintenir la consistance à travers les applications du Web.

Schéma 5-5:Architecture première du WWWSchéma 5-5:Architecture première du WWW

Les développeurs d’implémentations Web étaient déjà allés plus loin que cette conception première. En plus des documents statiques, les requêtes pouvaient identifier des services produisant dynamiquement des réponses, telles que les «imagemap» [Kevin Hughes] et les scripts côté serveur [rob McCool]. Des travaux avaient également commencé sur des composants intermédiaires, notamment sur des mandataires [ndt. proxy] [79] et sur des caches partagés [59]. Cependant des extensions aux protocoles étaient nécessaires afin de les faire communiquer d’une façon sûre. Les sections suivantes décrivent les contraintes complémentaires ajoutées au modèle architectural du Web afin de guider les extensions conduisant à l'architecture moderne du Web.

5.1.5 Interface uniforme

Le point fondamental qui distingue le modèle d’architecture REST des autres modèles basés sur les concepts réseau est la mise en exergue d’une interface uniforme entre les composants (schéma 5-6). En appliquant le principe logiciel de généralisation à l'interface du composant, l'architecture globale du système est simplifiée et la visibilité des interactions est améliorée. Les mises en œuvre sont découplées des services qu'elles fournissent, ce qui favorise les indépendances des évolutions. Bien sûr, la contrepartie est qu'une interface uniforme pénalise l'efficacité, puisque l'information est transmise dans une forme normalisée plutôt que dans une forme spécifique aux besoins d'une application. L'interface REST est conçue pour être efficace pour le transfert de données hypermédia de granularité importante, optimum pour le cas classique du Web. Mais il en résulte une interface qui n'est pas optimale pour d'autres formes d'interaction architecturale.

Schéma 5-6: Le modèle client/serveur avec interface uniforme, cache et sans étatSchéma 5-6: Le modèle client/serveur avec interface uniforme, cache et sans état

Afin d'obtenir une interface uniforme, de multiples contraintes d’architecture sont nécessaires pour guider le comportement des composants. REST est défini par quatre contraintes d'interface : identification des ressources ; manipulation des ressources par des représentations ; messages auto-descriptifs et l'hypermédia comme moteur de l'état de l’application. Ces contraintes seront abordées dans la section 5.2.

5.1.6 Système en couches

Afin d’améliorer le comportement de l’architecture face aux besoins de grande échelle (internet), nous ajoutons des contraintes de système en couches (le schéma 5-7). Le modèle de système en couches, décrit dans la section 3.4.2, permet à une architecture de se composer de couches hiérarchiques en contraignant le comportement des composants. Chaque composant ne peut pas «voir» au delà de la couche immédiate avec laquelle il interagit. En limitant la connaissance du système à une seule couche, nous mettons une limite sur la complexité du système global et favorisons l'indépendance des couches. Les couches peuvent être employées pour encapsuler des services déjà en place et protéger les nouveaux services des clients existants, en simplifiant les composants par le déplacement de fonctionnalités rarement utilisées dans un espace intermédiaire partagé. Des intermédiaires peuvent également être employés pour améliorer la montée en charge du système en offrant un équilibrage de charges pour les services et ce à travers de multiples réseaux et processeurs.

Schéma 5-7: Le modèle client/serveur en couches avec interface uniforme, cache et sans étatSchéma 5-7: Le modèle client/serveur en couches avec interface uniforme, cache et sans état

Le principal inconvénient des systèmes en couche est qu'ils ajoutent un surcoût et une latence supplémentaire au traitement des données, réduisant ainsi la perception de performance pour l’utilisateur [32]. Pour un système basé sur les concepts réseau qui supporte les contraintes de cache, ce peut être compensé par les bénéfices apportés par du cache partagé aux niveaux intermédiaires. Ajouter des caches partagés aux frontières du domaine d’une organisation peut avoir comme conséquence des gains de performance significatifs [136]. De plus, de telles couches permettent également de renforcer les politiques de sécurité sur les données aux frontières des organisations, comme les pare-feu l’exigent [79].

La combinaison des contraintes amenées par les systèmes en couches et l’unification d’interface conduisent à des propriétés architecturales semblables à celles du modèle «tube + filtre» uniforme (section 3.2.2). Même si les interactions REST sont bidirectionnelles, chaque flux de données grosse maille peut être traité comme un réseau de flux de données, avec des composants de filtrage appliqués de façon sélective à ces flots de données afin de transformer le contenu au fil de l’eau [26]. Dans le contexte REST, les composants intermédiaires peuvent transformer le contenu des messages car ils se décrivent eux-mêmes et leur sémantique est visible par les intermédiaires.

5.1.7 Code à la demande

Le dernier ajout à notre ensemble de contraintes pour REST provient du modèle de code-à-la-demande de la section 3.5.3 (schéma 5-8). REST permet l’extension des fonctionnalités d’un client par le biais de téléchargement et d’exécution de code sous forme d'applet ou de scripts. Cela simplifie les clients en réduisant le nombre de fonctionnalités qu’ils doivent mettre en œuvre par défaut. La possibilité de télécharger des fonctionnalités après le déploiement améliore l'extensibilité du système. Elle réduit cependant la visibilité. De ce fait, elle constitue une contrainte facultative dans REST.

La notion de contrainte facultative peut ressembler à un oxymore. Cependant, elle a un sens dans la conception d’architecture de système qui englobe des frontières d'organisation multiples. Cela signifie que l'architecture ne bénéficie (et souffre des inconvénients) des contraintes optionnelles que lorsqu’elles ont un effet réel pour une certaine partie du système global. Par exemple, s’il est avéré que tous les postes clients d’une organisation supportent les applets Java [45], alors les services de cette organisation peuvent être construits de manière à tirer profit de fonctionnalités étendues apportées par des classes Java téléchargeables. Le pare-feu de l'organisation peut en même temps empêcher le transfert d’applets Java depuis des sources extérieures. Ainsi du point de vue du reste du Web, il sera clair que ces clients ne supportent pas le «code à la demande». Une contrainte optionnelle nous permet de concevoir une architecture qui supporte le comportement désiré dans le cas général, mais avec la possibilité de le désactiver dans certains contextes.

Schéma 5-8: Le modèle RESTSchéma 5-8: Le modèle REST

5.1.8 Résumé de la dérivation de modèle

REST se compose d'un ensemble de contraintes architecturales choisies pour les propriétés qu'elles induisent sur les architectures cibles. Même si chacune de ces contraintes peut être considérée en tant que telle, une description globale de leur dérivation depuis des modèles d’architecture communs facilite la compréhension de la logique derrière leur choix. Le schéma 5-9 montre graphiquement la dérivation des contraintes de REST en se référant aux modèles d’architecture basés sur le réseau, décrits dans le chapitre 3.

Schéma 5-9: La dérivation REST par l'application de contraintesSchéma 5-9: La dérivation REST par l'application de contraintes

5.2 Eléments architecturaux de REST

Le modèle REST (Representational State Transfer) est une abstraction des éléments architecturaux d’un système réparti d’hypermédias. REST est indépendant des détails de mise en œuvre des composants et de syntaxe de protocole. Il se concentre ainsi sur les rôles des composants, les contraintes sur leur interaction avec d'autres composants, et leur interprétation des éléments de données significatives. Il englobe les contraintes fondamentales sur les composants, les connecteurs et les données qui définissent la base de l'architecture du Web et ainsi l'essence de leur comportement en tant qu’application réseau.

5.2.1 Eléments de données

À la différence du modèle objet distribué [31], dans lequel toutes les données sont encapsulées et cachées par les composants de traitement, la nature et l’état des éléments de données d'une architecture constituent un aspect fondamental de REST. La raison de cette conception provient de la nature des hypermédias distribués. Lorsqu’on choisit un lien, l'information doit être déplacée depuis l'endroit où elle est stockée jusqu’à l'endroit où elle sera utilisée, dans la plupart des cas, par un lecteur humain. Cela marque une différence par rapport aux nombreux autres paradigmes de traitement distribué [6, 50], où il est possible, et habituellement plus efficace, de déplacer «l'agent de traitement» (par exemple, du code mobile, une procédure stockée, une expression de recherche, etc.) vers les données plutôt que de déplacer les données au niveau du processeur.

Face à des hypermédias distribués, un architecte ne possède que trois options de base: 1) effectuer le rendu des données à l’endroit où elles sont localisées et envoyer au destinataire une image dans un format fixe; 2) encapsuler les données avec un moteur de rendu et envoyer les deux au destinataire ; ou, 3) envoyer les données brutes au destinataire avec des métadonnées décrivant leur type, afin qu’il puisse choisir son propre moteur de rendu.

Chaque option possède ses avantages et ses inconvénients. La première, reflétant le modèle traditionnel client/serveur [31], permet de cacher toutes les informations sur la véritable nature des données, empêchant de faire des suppositions quant à la structure des données et facilitant la mise en oeuvre côté client. Elle limite cependant de façon drastique les fonctionnalités du destinataire et implique que la plupart de la charge de traitement soit localisée sur l'expéditeur, conduisant ainsi à des problèmes de montée en charge. La deuxième option, le modèle de l'objet mobile [50], masque les informations tout en permettant des traitements spécialisés sur des données par l'intermédiaire de son moteur de rendu unique. Par contre, elle limite les fonctionnalités du destinataire à ce qui est prévu par le moteur et peut considérablement augmenter la quantité de données transférées. La dernière option permet à l'expéditeur de rester simple et de pouvoir faire face à la charge en réduisant au minimum les octets transférés. Elle perd les avantages du masquage de l’information et demande à l'expéditeur et au destinataire de comprendre les mêmes types de données.

REST propose une vision hybride de ces trois options en se basant sur une compréhension partagée des types de données à l’aide de métadonnées, tout en limitant ce qui est utile à une interface normalisée. Les composants REST communiquent en transférant une représentation d'une ressource dans un format appartenant à un ensemble évolutif de types de données standard. Elle est choisie dynamiquement en fonction des possibilités ou des souhaits du destinataire et de la nature de la ressource. Le fait que la représentation soit dans le même format que la source brute ou qu’elle en soit dérivée reste caché par le mécanisme d'interface. On approche ainsi les avantages du modèle mobile d'objet en envoyant une représentation se composant d’instructions dans le format de données standard sur le moteur de rendu encapsulé (par exemple, Java [45]). REST bénéficie donc de la séparation des concepts du modèle client/serveur sans être confronté au problème de montée en charge du serveur. REST permet de cacher l'information par une interface générique afin de permettre l'encapsulation et l'évolution des services. Ce modèle fournit également un ensemble de diverses fonctionnalités par le biais de moteurs téléchargeables.

Le tableau 5-1 résume les éléments de données de REST.

Tableau 5-1 : Éléments de REST
Éléments d'informations Exemples modernes de Web
Ressource la cible conceptuelle d'une référence hypertexte
Identifiant de ressource URL, URN
Représentation Document HTML, image JPEG
méta-données de la représentation type de média, date de dernière modification
méta-données de la ressource lien sur la source, liens alternatifs, variation
Données de contrôle if-modified-since, cache-control

5.2.1.1 Ressources et identifiants de ressource

L'abstraction principale de l'information dans REST est la ressource. Toute information pouvant être nommée peut être une ressource : un document ou une image, un service temporel (par exemple «le temps d'aujourd'hui à Marseille»), une collection d'autres ressources, un objet non-virtuel (par exemple une personne), ainsi de suite. En d'autres termes, tout concept pouvant être la cible d'une référence hypertexte d'un auteur doit entrer dans la définition d'une ressource. C'est une correspondance conceptuelle à un ensemble d'entités et ce n’est pas l'entité correspondant à cette association à un moment particulier dans le temps.

Plus précisément, une ressource R est une fonction MR(t) relationnelle [ndt. membership] variant dans le temps, dans laquelle R correspond à un instant t à un ensemble d'entités, ou de valeurs, équivalentes. Les valeurs dans cet ensemble peuvent être des représentations de ressource et/ou des identifiants de ressource. Une ressource peut correspondre à l'ensemble vide. Ceci permet de lier des références à un concept avant même qu’une réalisation de ce concept n’existe -- cette notion était étrangère à la plupart des systèmes hypertextes avant le Web [61]. Certaines ressources sont statiques dans le sens où, lorsqu'on les examine à n'importe quel instant après leur création, elles correspondent toujours au même ensemble de valeurs. D'autres au contraire ont un degré élevé de variabilité avec le temps. Pour une ressource, le seul critère nécessairement statique concerne la sémantique de correspondance. C'est en effet cette sémantique qui distingue une ressource d'une autre.

Par exemple, la «version plébiscitée par des auteurs» d'un papier d'universitaire est une association dont la valeur change avec le temps, alors que ce qui est associé au «papier publié lors de la conférence X» est statique. Ce sont deux ressources distinctes, même si elles peuvent correspondre toutes les deux à la même valeur à un instant donné. Cette distinction est nécessaire. Ainsi les deux ressources peuvent être identifiées et référencées indépendamment. L'identification distincte d'un fichier source issu d'un gestionnaire de configuration est un exemple similaire dans l'ingénierie logicielle. Cette identification peut se rapporter à la «dernière révision», à la «révision portant le numéro 1.2.7» ou encore à la «révision incluse avec la livraison orange.»

Cette définition abstraite d'une ressource fournit les concepts clés de l'architecture du Web. Tout d'abord, elle permet de généraliser en enveloppant de nombreuses sources d'information sans les distinguer artificiellement par leur type ou leur mise en oeuvre. Ensuite, elle permet de faire une liaison très tardive entre une référence et une représentation, permettant ainsi une négociation de contenu se basant sur les caractéristiques de la demande. Enfin, elle permet à un auteur de mettre en exergue un concept plutôt qu'une représentation donnée de ce concept. Ainsi, la nécessité de changer tous les liens existants à chaque fois que la représentation change n'existe plus (en partant du principe que l'auteur a utilisé le bon identifiant).

REST utilise un identifiant de ressource pour identifier la ressource particulière impliquée dans une interaction entre les composants. Les connecteurs REST fournissent une interface générique pour accéder et manipuler l'ensemble de valeurs d'une ressource, indépendamment de la façon dont la fonction relationnelle est définie ou du type de logiciel qui manipule la requête. L'autorité de nommage ayant assigné l’identifiant à la ressource, lui permettant d’être référencée, est responsable du maintien de la validité sémantique des correspondances dans le temps (c.-à-d. en s'assurant que la fonction définissant la relation ne change pas).

Les systèmes hypertextes traditionnels [61], fonctionnant typiquement dans un environnement fermé ou local, emploient des identifiants uniques de noeud ou de document qui évoluent au rythme du changement de l'information, en s’appuyant sur des serveurs de liens afin de maintenir les références indépendamment du contenu [135]. Les serveurs centralisés de liens se révèlent être une hérésie face aux exigences du web dictées par des domaines d’information à grande échelle et multi-organisationnels. REST s’appuie plutôt sur l'auteur qui choisit un identifiant de ressource qui caractérise au mieux la nature du concept identifié. Naturellement, la qualité d'un identifiant est souvent proportionnelle à la somme d'argent engagée pour maintenir sa validité, ce qui conduit à des liens cassés lorsqu’une information éphémère (ou mal supportée) est déplacée ou détruite dans le temps.

5.2.1.2 Représentations

Les composants REST effectuent des actions sur une ressource en utilisant une représentation pour capturer l’état courant ou prévu de cette ressource et en transférant cette représentation entre les composants. Une représentation est une séquence d’octets, plus des métadonnées qui les décrivent. Des noms comme document, fichier, entité de message HTTP, instance ou variante sont utilisés pour désigner une représentation de façon générale mais sont moins précis.

Une représentation se compose de données, de métadonnées décrivant les données, et occasionnellement, de métadonnées décrivant les métadonnées (en principe afin de vérifier l'intégrité des messages). Ces métadonnées sont sous forme de paires nom/valeur, où le nom correspond à un standard qui définit la structure et la sémantique de la valeur. Les messages de réponse peuvent inclure des métadonnées de représentation et des métadonnées de ressource : informations sur la ressource qui ne sont pas spécifiques à la représentation fournie.

Les données de contrôle définissent la nature d'un message entre les composants, comme l'action demandée ou la signification d'une réponse. Ils sont également utilisés pour paramétrer les requêtes et surcharger le comportement par défaut de certains éléments communicants. Par exemple, le comportement du cache peut être modifié par des donnés de contrôle incluses dans le message de la requête ou de la réponse.

Selon ces données de contrôle du message, une représentation peut indiquer l'état courant ou l'état désiré de la ressource demandée, ou la valeur d'autres ressources, comme la représentation des données d'entrée du formulaire ou encore une représentation d'une certaine condition d'erreur pour une réponse. Par exemple, la modification à distance d'une ressource exige que l'auteur envoie une représentation au serveur, établissant de ce fait une valeur pour cette ressource qui peut être recherchée via des requêtes postérieures. Si l'ensemble des valeurs d'une ressource se compose à un instant donné de représentations multiples, la négociation de contenu peut être utilisée pour choisir la meilleure représentation afin de l'inclure dans un message donné.

Le format de données d'une représentation est connu comme étant un type de média [48]. Une représentation peut être incluse dans un message et être traitée par le destinataire en fonction des donnés de contrôle du message et de la nature du type de média. Certains types de média sont destinés au traitement automatisé, d’autres sont prévus pour un rendu pour un utilisateur. Seul un petit nombre sont capables des deux. Des types de média composés peuvent alors être utilisés pour rassembler des représentations multiples dans un message simple.

La conception d'un type de média peut avoir un impact direct sur la perception de performance d'un système réparti d’hypermédia que peut avoir l’utilisateur. Chaque donnée qui doit être reçue avant que le destinataire puisse commencer le processus de rendu de la représentation s'ajoute à la latence de l’interaction. Un format de données qui place l'information de rendu la plus importante au tout début, de telle sorte que l'information initiale puisse être rendue de façon incrémentale tandis que le reste de l'information continue d’arriver, a pour résultat une meilleure perception de performance par l’utilisateur par rapport à un format de données devant être entièrement reçues avant de commencer le rendu.

Par exemple, un navigateur web qui peut mettre en forme progressivement un document HTML volumineux alors qu'il continue de le recevoir fournit une meilleure perception de performance à l’utilisateur par rapport à un autre qui attend que le document entier soit complètement reçu avant de l’afficher, et cela même si les performances du réseau sont les mêmes. Notez que la capacité de rendu d'une représentation peut également être impactée par le choix du contenu. Si les dimensions des tables calculées dynamiquement et des objets inclus doivent être déterminées avant qu'elles puissent être rendues, leur occurrence dans la zone d’affichage d'une page hypermédia augmentera sa latence.

5.2.2 Connecteurs

REST emploie divers types de connecteur, récapitulés dans le tableau 5-2, afin d’encapsuler les activités d’accès aux ressources et de transfert des représentations de ressource. Les connecteurs offrent une interface abstraite pour la communication entre composants, amenant de la simplicité en fournissant une séparation claire des problèmes et en cachant la mise en œuvre sous-jacente des ressources et des mécanismes de communication. La généralisation du concept d'interface permet également la substituabilité : si, pour les utilisateurs, le seul accès au système se fait par l'intermédiaire d'une interface abstraite, alors la mise en œuvre peut être remplacée sans les affecter. Comme un connecteur gère la communication réseau pour un composant, l'information peut être partagée au travers de multiples interactions afin d'améliorer l'efficacité et les temps de réponse.

Tableau 5-2 : Connecteurs REST
Connecteur Exemples Web
client libwww, libwww-perl
serveur libwww, Apache API, NSAPI
cache cache du navigateur, réseau de cache d'Akamai
résolveur bind (bibliothèque de consultation DNS)
tunnel SOCKS, SSL après HTTP CONNECT

Toutes les interactions REST sont sans état. C'est-à-dire que chaque requête contient toutes les informations nécessaires pour qu’un connecteur puisse comprendre la demande et ce indépendamment de toutes les requêtes qui ont pu l'avoir précédées. Cette restriction accomplit quatre fonctions : 1) elle enlève tout besoin pour les connecteurs de maintenir l'état de l’application entre les requêtes, réduisant ainsi la consommation de ressources physiques et améliorant la montée en charge; 2) elle permet à des interactions d'être traitées en parallèle sans exiger de compréhension sémantique par le mécanisme de traitement; 3) elle permet à un intermédiaire de regarder et de comprendre une requête de façon isolée, ce qui peut être nécessaire quand des services sont modifiés dynamiquement; et, 4) elle force toutes les informations qui pourraient être factorisées dans la réutilisation d'une réponse en cache à être présente dans chaque requête.

L'interface des connecteurs est semblable à une invocation procédurale, mais avec des différences importantes dans le passage des paramètres et des résultats. Les paramètres en entrée se composent de données de contrôle de la requête, d'un identifiant de ressource indiquant la cible de la requête et d'une représentation facultative. Les paramètres en sortie se composent de données de contrôle de la réponse, de métadonnées optionnelles sur la ressource et d'une représentation facultative. D'un point de vue abstrait l'invocation est synchrone, mais les paramètres en entrée et en sortie peuvent être passés comme des flux de données. En d'autres termes, le traitement peut être invoqué avant que la valeur des paramètres ne soit complètement connue, évitant ainsi la latence du traitement par lot d’un gros volume de données transférées.

Les premiers types de connecteur sont les connecteurs client et serveur. La différence essentielle entre les deux est qu'un client initie la communication en faisant une demande, tandis qu'un serveur est à l'écoute de connexions et répond aux requêtes afin d'assurer l'accès à ses services. Un composant peut inclure des connecteurs à la fois client et serveur.

Un troisième type de connecteur, le connecteur de cache, peut être situé sur l'interface d'un connecteur client ou serveur afin de conserver des réponses, pouvant être mises en cache, relatives aux interactions actuelles, de sorte qu'elles puissent être réutilisées pour des requêtes ultérieures. Un cache peut être employé par un client pour éviter la répétition de communication réseau, ou par un serveur pour éviter de répéter le processus de production d'une réponse. Dans les deux cas, il sert à réduire la latence des interactions. Un cache est typiquement mis en oeuvre dans l'espace d'adresse du connecteur qui l'utilise.

Certains connecteurs de cache sont partagés. Cela signifie que des réponses en cache peuvent être utilisées par un client autre que celui pour lequel la réponse initiale a été obtenue. Un cache partagé peut être efficace pour réduire l'impact «d'accès massifs instantanés» sur la charge d'un serveur populaire, en particulier lorsque le cache est organisé hiérarchiquement afin de couvrir un grand nombre de groupes d'utilisateurs, comme c'est le cas avec l'Intranet d'une entreprise, avec des clients d'un fournisseur de services sur internet (Internet Service Provider), ou encore avec des universités partageant un réseau fédérateur national. Cependant, un cache partagé peut également aboutir à des erreurs si la réponse en cache ne correspond pas à celle qui aurait été obtenue par une nouvelle requête. REST essaie d'équilibrer le souhait de transparence dans le comportement d'un cache avec le celui d'un usage efficace du réseau, plutôt que de supposer qu'un usage transparent est systématiquement nécessaire.

Un cache peut déterminer la possibilité de garder une réponse en cache car l'interface est générique et non spécifique à chaque ressource. Par défaut, la réponse à une requête de récupération peut être mise en cache et les réponses aux autres demandes peuvent ne pas l’être. Si une certaine forme d'authentification de l'utilisateur fait partie de la demande, ou si la réponse indique qu'elle ne devrait pas être partagée, alors la réponse peut être mise en cache uniquement dans un cache non-partagé. Un composant peut surcharger ce comportement par défaut en incluant des données de contrôle marquant l'interaction comme pouvant être mise ou non en cache ou encore seulement pendant une durée limitée.

Un résolveur traduit les identifiants partiels ou complets de ressource en information d'adresse réseau nécessaire pour établir une connexion entre composants. Par exemple, la plupart des URI considèrent qu'un nom d'hôte DNS est le mécanisme pour identifier l'autorité de nommage de la ressource. Afin d'initier une demande, un navigateur web extraira le nom de l'hôte à partir de l'URI et se servira d'un résolveur DNS pour obtenir l'adresse du Protocole Internet (IP) pour cette autorité. D'autres schémas d'identification (par exemple, URN [124]) ont besoin d'un intermédiaire pour traduire un identifiant permanent en une adresse plus volatile afin d'accéder à la ressource identifiée. L'utilisation d'un ou plusieurs mécanismes intermédiaires de résolution peut améliorer la longévité des références de ressources grâce à l'adressage indirect même si cela ajoute des temps de latence à la requête.

Le tunnel est la dernière forme de connecteur. Il relaie simplement la communication à travers une connexion ayant des limites, comme un pare-feu ou une passerelle réseau de bas niveau. La seule raison qu'il soit un élément de REST et non abstrait en tant qu'élément de l'infrastructure réseau est que certains composants REST peuvent dynamiquement commuter du comportement de composant actif à celui de tunnel. L'exemple principal est un serveur HTTP mandataire [ndt. proxy] qui bascule en un tunnel en réponse à une requête CONNECT [71]. Cela permet ainsi à son client de communiquer directement avec le serveur distant en utilisant un protocole différent, comme TLS, qui lui n'autorise pas de serveur mandataire. Le tunnel disparaît lorsque les deux extrémités terminent leur communication.

5.2.3 Composants

Les composants REST, récapitulés dans le tableau 5-3, sont typés par leur rôle dans une action globale d'application.

Tableau 5-3 : Composants REST
Composant Exemples modernes de Web
serveur d'origine httpd d'Apache, Microsoft IIS
passerelle Squid, CGI, Reverse Proxy
serveur mandataire proxy CERN, proxy Netscape, Gauntlet
agent utilisateur Navigateur Netscape, Lynx, MOMspider

Un agent utilisateur utilise un connecteur client pour initier une requête et devient le destinataire final de la réponse. L'exemple le plus commun est un navigateur web, qui permet d'accéder aux services d'information et fournit les réponses du service selon les besoins de l'application.

Un serveur d'origine utilise un connecteur serveur pour régir l'espace de noms pour une ressource demandée. C'est la source définitive de représentations de ses ressources et il doit être le destinataire final de toute requête qui prévoit de modifier la valeur de ses ressources. Chaque serveur d'origine fournit une interface générique à ses services via une hiérarchie de ressources. Les détails de mise en oeuvre de la ressource sont cachés derrière l'interface.

Les composants intermédiaires agissent en tant que client et serveur afin de faire suivre, avec une traduction éventuelle, des requêtes et des réponses. Un composant mandataire est un intermédiaire choisi par un client pour fournir une encapsulation d'interface à des autres services, traduction de données, amélioration de performance ou protection de sécurité. Un composant passerelle (c'est-à-dire un serveur mandataire inverse) est un intermédiaire imposé par le réseau ou le serveur d'origine pour fournir une encapsulation d'interface à d'autres services, pour la traduction de données, l'amélioration des performances ou l'application de la sécurité. Notez que la différence entre un serveur mandataire et une passerelle réside dans le fait qu'un client détermine quand il utilisera un serveur mandataire.

5.3 Vues architecturales de REST

Maintenant que nous avons une compréhension unitaire des éléments d'architecture composant REST, nous pouvons utiliser des vues architecturales [105] pour décrire comment les éléments fonctionnent ensemble pour former une architecture. Trois types de vue -- processus, connecteur et données -- sont utiles pour éclairer les principes de conception REST.

5.3.1 Vue Processus

La vue processus d'une architecture est intéressante principalement pour obtenir les relations d'interaction entre les composants en indiquant le chemin que parcourent les données à travers le système. Malheureusement, l'interaction d'un vrai système implique habituellement un nombre très important de composants, produisant une vue globale croulant sous les détails. Le schéma 5-10 montre un exemple d'une vue processus d'une architecture basée sur REST lors du traitement de trois requêtes parallèles.

Schéma 5-10: Vue processus d'une architecture RESTSchéma 5-10: Vue processus d'une architecture REST

La séparation des concepts client/serveur, amenée par REST, simplifie la mise en oeuvre des composants, réduit la complexité de la sémantique des connecteurs, améliore l'efficacité des réglages d'amélioration des performances et augmente la possibilité de montée en charge des composants purement serveur. Les contraintes qu'amène un système en couches autorisent l'utilisation d'intermédiaires -- serveurs mandataires, passerelles et pare-feu -- pouvant être introduits à divers endroits dans la communication sans pour autant changer les interfaces entre les composants. Ainsi, ils peuvent aider à la traduction dans la communication ou améliorer les performances par l'intermédiaire de vastes caches partagés. REST permet les traitements intermédiaires en contraignant les messages à être auto-descriptifs : l'interaction est sans état entre les requêtes, des méthodes standard et des types de média sont utilisés pour indiquer la sémantique et l'échange d'information, et les réponses indiquent explicitement la possibilité de résider en cache.

Les composants étant connectés dynamiquement, leur arrangement et leur fonctionnement pour une action particulière d'une application possède des caractéristiques semblables à un modèle «tube + filtre» [ndt. pipe-and-filter]. Bien que les composants REST communiquent par l'intermédiaire de flux bidirectionnels, le traitement de chaque direction est indépendant et donc modifiable par des transducteurs de flux (filtres). L'interface générique des connecteurs permet à des composants d'être placés sur le flux en se basant sur les propriétés de chaque requête ou réponse.

Les services peuvent être mis en oeuvre en utilisant une hiérarchie complexe d’intermédiaires et de multiples serveurs distribués. La nature sans état de REST permet à chaque interaction d'être indépendante des autres, supprimant le besoin de connaissance de la topologie globale des composants, ce qui constitue une tâche impossible pour une architecture à l’échelle d’Internet. Elle permet aussi à des composants d'agir en tant que destinataires ou intermédiaires, ce choix étant déterminé dynamiquement par la cible de chaque requête. Les connecteurs doivent seulement se connaître mutuellement pendant la durée de leur communication, même s'ils peuvent cacher l'existence et les possibilités d'autres composants pour des raisons de performance.

5.3.2 Vue connecteur

Une vue connecteur d'une architecture se concentre sur les mécanismes de communication entre les composants. Dans le cas d’une architecture basée sur REST, nous nous intéressons particulièrement aux contraintes qui définissent l'interface générique de ressource.

Les connecteurs client examinent l’identifiant de ressource afin de choisir un mécanisme de communication appropriée pour chaque requête. Par exemple, un client peut être configuré pour se connecter à un serveur mandataire spécifique, peut-être à celui qui agit en tant que filtre d'annotation, lorsque l’identifiant indique que c'est une ressource locale. De même, un client peut être configuré pour rejeter des demandes d'un certain sous-ensemble d’identifiants.

REST ne limite pas la communication à un protocole particulier, mais il contraint l'interface entre les composants, et par conséquent la portée des interactions et les hypothèses de mise en œuvre qui pourraient être faites entre les composants. Par exemple, le protocole Web de transfert principal est HTTP, mais l'architecture inclut également l'accès sans échec aux ressources présentes sur les serveurs réseau préexistants, comme FTP [107], Gopher [7] et WAIS [36]. L'interaction avec ces services est limitée à la sémantique d'un connecteur REST. Cette contrainte sacrifie certains des avantages d'autres architectures, comme l'interaction avec état d'un protocole avec feedback comme WAIS, afin de maintenir les avantages d'une interface simple et générique pour la sémantique des connecteurs. En retour, l'interface générique permet d’accéder à une multitude de services via un mandataire simple. Si une application a besoin de possibilités complémentaires d'une autre architecture, elle peut les mettre en oeuvre et les appeler comme un système séparé fonctionnant en parallèle, de manière similaire à la façon dont l'architecture Web s’interface avec des ressources «telnet» et «mailto».

5.3.3 Vue orientée données

La vue orientée données d'une architecture indique l'état d'une application lorsque l'information traverse les composants. REST étant spécifiquement destiné aux systèmes d'informations répartis, il envisage une application comme une structure cohésive d'informations et d’alternatives dans laquelle un utilisateur peut exécuter une tâche. Par exemple, la recherche d’un mot dans un dictionnaire en ligne est une application, de même qu’un voyage dans un musée virtuel, ou la révision d’un ensemble de notes scolaires en vue de passer un examen. Chaque application définit des buts pour le système sous-jacent, qui serviront à mesurer la performance du système.

Les interactions entre composants se produisent sous forme de messages taillés dynamiquement. Les messages fins ou de granularité moyenne sont employés pour la sémantique des commandes, mais la grosse partie du travail d'une application est accomplie par l'intermédiaire de messages à granularité importante, messages qui contiennent une représentation complète des ressources. La forme la plus fréquente de sémantique de requête est celle permettant d’obtenir une représentation d'une ressource (par exemple, la méthode «GET» dans HTTP), qui peut souvent être mise en cache pour une réutilisation ultérieure.

REST concentre tous les états de contrôle dans les représentations reçues en réponse aux interactions. Le but est d'améliorer la montée en charge du serveur en éliminant tout besoin pour le serveur de maintenir une connaissance de l'état du client au delà de la requête courante. L'état d'une application est donc défini par ses requêtes en suspens, la topologie des composants connectés (certains peuvent filtrer des données bufferisées), les requêtes actives sur ces connecteurs, les flux de données des représentations en réponse à ces demandes et le traitement de ces représentations lorsqu’elles sont reçues par l'agent utilisateur.

Une application atteint un état stable chaque fois qu'elle n'a aucune requête en attente ; c’est-à-dire qu’elle n'a aucune demande en suspens et que toutes les réponses à son ensemble courant de requêtes ont été complètement reçues ou reçues au point où elles peuvent être traitées comme un flux de données de représentation. Pour un navigateur, cet état correspond à une «page Web», qui comprend la représentation principale ainsi que les représentations auxiliaires, comme les images intégrées, les applets embarquées et les feuilles de style. L’importance des états stables des applications a des impacts visibles sur la performance perçue par l’utilisateur et le volume du trafic réseau.

La perception de performance d’un navigateur du point de vue de l'utilisateur est déterminée par la latence entre les états stables : le moment entre la sélection d'un lien hypermédia sur une page Web et celui à partir duquel l'information utilisable a été rendue pour la prochaine page Web. L'optimisation des performances d’un navigateur est donc centrée sur la réduction de cette latence de communication.

Les architectures basées sur REST communiquant principalement par le transfert de représentations de ressources, la conception des protocoles de transmission et la conception des formats de données de représentation peuvent toutes deux avoir un impact sur cette latence. La capacité de rendre de façon incrémentale les données de la réponse alors qu'elle est reçue est déterminée par la conception du type de média et par la disponibilité d'information de rendu (dimensions visuelles des objets intégrés) de chaque représentation.

Une observation intéressante est que la requête réseau la plus efficace est celle qui n'emploie pas le réseau. En d'autres termes, la capacité de réutiliser une réponse depuis un cache a pour conséquence une amélioration considérable des performances de l'application. Même si l'utilisation d'un cache ajoute de la latence à chaque requête individuelle à cause du surcoût de la recherche, la latence moyenne de la demande est sensiblement réduite même si seulement un petit pourcentage des requêtes n’est éligible au cache.

Le prochain état de contrôle d'une application réside dans la représentation de la première ressource demandée. Ainsi l’obtention de cette première représentation est une priorité. L'interaction REST est donc améliorée par les protocoles qui «répondent d'abord et pensent plus tard.» Dit autrement, un protocole qui exige de multiples interactions pour chaque action d'un utilisateur, afin d’entreprendre des négociations sur les possibilités du dispositif avant d'envoyer un contenu de réponse, sera perçu comme étant plus lent qu'un protocole qui envoie ce qui est susceptible d'être le plus optimal dans un premier temps, puis fournit une liste de solutions alternatives dans laquelle le client peut rechercher si la première réponse est insuffisante.

L'état de l'application est contrôlé et stocké par l'agent utilisateur et peut se composer de représentations provenant de multiples serveurs. En plus de libérer le serveur des problèmes de monter en charge pour le stockage de l'état, cela permet à l'utilisateur de manipuler directement cet état (historique d'un navigateur web, par exemple), de prévoir des changements de cet état (par exemple, des cartes de liens et pré remplissage de représentations) et de sauter d'une application à une autre (signets et dialogues avec des entrées contenant des URI par exemple).

L'application modèle est donc un moteur qui navigue d'un état vers le suivant en examinant et en choisissant parmi les transitions d'état alternatives dans l'ensemble courant des représentations. Il n’est pas surprenant de constater que cela correspond exactement à l'interface utilisateur d'un navigateur d’hypermédia. Cependant, le modèle ne suppose pas que toutes les applications sont des navigateurs. En fait, les détails de l'application sont cachés du serveur par l'interface générique du connecteur. Un agent utilisateur pourrait ainsi être de façon équivalente un robot automatisé effectuant de la récupération documentaire pour un service d'indexation, un agent personnel recherchant des données correspondant à certains critères, ou encore un logiciel de maintenance occupé à parcourir l'information à la recherche de références cassées ou de contenu modifié [39].

5.4 Autres travaux abordant le sujet

Bass, et al. [9] ont consacré un chapitre sur l'architecture du World Wide Web, mais leur description ne couvre que la mise en œuvre de l'architecture développée par le CERN/W3C à travers libwww (bibliothèques client et serveur) et le logiciel Jigsaw. Bien que ces réalisations reflètent plusieurs des contraintes de conception de REST et qu’elles ont été développées par des personnes familières de la conception architecturale et de la logique du Web, la véritable architecture WWW est indépendante de toute mise en oeuvre. Le Web moderne est défini par ses interfaces et des protocoles standard, et non par la façon dont ces interfaces et protocoles sont mis en application dans un morceau donné de logiciel.

Le modèle REST se base sur beaucoup de paradigmes de processus distribués déjà existants [6, 50], sur les protocoles de communication et sur le champ des logiciels. Les interactions de composants REST sont structurées en un modèle client/serveur en couches, mais les contraintes supplémentaires de l'interface générique de ressource créent l’opportunité pour la substituabilité et l'inspection par des intermédiaires. Les requêtes et les réponses ont l'aspect d'un modèle d'invocation à distance, mais les messages REST visent une ressource conceptuelle plutôt qu'un identifiant de mise en oeuvre particulière.

Plusieurs tentatives ont essayé de modéliser l'architecture du Web comme une forme de système de fichiers répartis (par exemple, WebNFS) ou comme un système d'objets répartis [83]. Cependant, elles excluent divers types de ressource du Web ou certaines stratégies de mise en œuvre qui sont «non intéressantes» quand leur présence invalide les hypothèses à la base de tels modèles. REST fonctionne bien parce qu'il ne limite pas la mise en œuvre de ressources à certains modèles prédéfinis, permettant à chaque application d’en choisir une qui correspond au mieux à ses propres besoins. REST permet par ailleurs le remplacement d’une réalisation sans impacter l'utilisateur.

La méthode d'interaction consistant à envoyer des représentations de ressources aux composants les consommant a quelques parallèles avec des modèles d'intégration basés sur les événements [ndt. EBI pour event-based integration]. La différence principale est que les modèles EBI sont basés sur les technologies «push». Le composant contenant les états (équivalent à un serveur d'origine dans REST) envoie un événement à chaque changement d'état, même si aucun composant n’est réellement intéressé par tel événement. Dans le modèle REST, les composants consomment habituellement les représentations en allant les quérir. Bien que ce soit moins efficace du point de vue d’un simple client souhaitant surveiller une simple ressource, la portée du Web rend infaisable un modèle «push».

L'utilisation de principe du modèle REST dans le Web, avec sa notion claire de composants, de connecteurs et de représentations, se rapproche étroitement du modèle architectural C2 [128]. Ce modèle supporte le développement d’applications réparties et dynamiques en se concentrant sur l'utilisation structurée des connecteurs afin d'obtenir l'indépendance de substrat. Les applications C2 se basent sur des notifications asynchrones de changements d'état et sur des messages de requête. Comme avec d'autres schémas basés sur les événements, C2 est basé de façon nominale sur une technologie «push», même si l'architecture C2 pourrait fonctionner dans le modèle REST pull en émettant seulement un avis à la réception d'une demande. Cependant, le modèle C2 ne tient pas compte des contraintes relatives aux intermédiaires de REST, comme l'interface générique de ressource, les interactions garanties sans état et le support intrinsèque du cache.

5.5 Résumé

Ce chapitre a présenté le modèle architectural REST (Representational State Transfer) pour les systèmes répartis d’hypermédias. REST fournit un ensemble de contraintes d’architecture qui, appliquées comme un tout, met en exergue la montée en charge des interactions de composants, la généralisation des interfaces, le déploiement indépendant des composants et l'utilisation de composants intermédiaires afin de réduire la latence d'interaction, d’imposer la sécurité et d’encapsuler les systèmes existants. J'ai décrit les principes d’ingénierie logicielle guidant REST et les contraintes d'interaction choisies pour garantir ces principes, tout en les comparant aux contraintes des autres modèles d’architecture.

Le prochain chapitre présente une évaluation de l'architecture REST au travers de l'expérience et des leçons retenues après avoir appliqué REST à la spécification, à la conception et au déploiement de l'architecture moderne du Web. Ce travail a permis d’écrire les spécifications actuelles des standards d’Internet : le protocole HTTP/1.1 (Hypertext Transfer Protocol) et les URI (Uniform Resource Identifiers). Il a également débouché sur la mise en œuvre de cette architecture par le biais de la bibliothèque libwww-Perl et du serveur HTTP d'Apache.