1 points par GN⁺ 4 시간 전 | 1 commentaires | Partager sur WhatsApp
  • La RFC 10008 définit la méthode HTTP QUERY, qui permet à la ressource cible de traiter de manière sûre et idempotente une requête contenue dans le corps de la requête, puis d’en renvoyer le résultat
  • QUERY combine le caractère safe/idempotent de GET avec le mode de transmission par corps de POST, afin de réduire les URI trop longues, le coût de l’encodage d’URI, l’exposition dans les logs et la nécessité de matérialiser une ressource distincte pour chaque combinaison de requête
  • Le serveur ne peut traiter une requête QUERY que si le Content-Type et le corps de la requête sont cohérents ; un type non pris en charge, une incohérence avec le corps ou une requête impossible à traiter peuvent chacun être distingués par des réponses 4xx différentes
  • Une réponse réussie peut indiquer, via Content-Location, la ressource correspondant au résultat de la requête, et via Location, une equivalent resource permettant de rejouer la même requête
  • Les réponses à QUERY sont cacheables, mais la clé de cache doit inclure le corps et les métadonnées ; en environnement CORS, QUERY n’étant pas une safelisted method, un preflight est nécessaire

Les schémas de requête HTTP que QUERY cherche à résoudre

  • La RFC 10008 est un document Internet Standards Track qui définit la méthode de requête HTTP QUERY
  • QUERY demande à la ressource cible de traiter le corps de la requête et de renvoyer le résultat dans la réponse
  • Comme POST, elle utilise un corps de requête, mais elle est définie comme safe et idempotent, ce qui permet les réessais automatiques ou la reprise
  • Les requêtes GET existantes transmettent généralement leurs entrées dans l’URI
    • GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
  • Mettre les données de requête dans l’URI devient plus contraignant à mesure que leur taille augmente
    • Comme elles traversent plusieurs systèmes indépendants, il est difficile de connaître à l’avance la limite réelle de taille d’URI
    • HTTP recommande que l’émetteur et le récepteur prennent en charge au minimum 8000 octets, sans garantie pour tous les systèmes intermédiaires du trajet
    • Certaines données ont un coût élevé pour être encodées dans un URI valide
    • L’URI de requête a davantage de chances d’être journalisé ou inclus dans un signet que le corps de la requête
    • Encoder directement la requête dans l’URI revient à traiter chaque combinaison d’entrées possible comme une ressource distincte

Une méthode qui clarifie la sémantique entre GET et POST

  • De nombreuses implémentations transmettent la requête dans le corps d’un POST au lieu d’un GET
    • POST /feed
    • Content-Type: application/x-www-form-urlencoded
    • Corps : q=foo&limit=10&sort=-published
  • Avec cette approche, on ne peut pas savoir si la requête est sûre et idempotente sans connaître la ressource et le serveur concernés
  • QUERY envoie les mêmes entrées dans le corps de la requête, tout en ayant une méthode explicitement sûre et idempotente
    • QUERY /feed
    • Content-Type: application/x-www-form-urlencoded
    • Corps : q=foo&limit=10&sort=-published
  • Cette sémantique explicite facilite l’application de fonctionnalités HTTP comme la mise en cache et les réessais automatiques
  • Le serveur peut attribuer un URI à la requête elle-même ou à un résultat particulier de cette requête pour une utilisation ultérieure via GET

Règles clés de la méthode QUERY

  • QUERY est utilisée pour initier une requête côté serveur
  • GET demande une représentation de la ressource identifiée par l’URI cible, tandis que QUERY demande l’exécution d’une opération de requête dans le périmètre de la ressource cible
  • Le corps de la requête et son type de média définissent la requête, et l’origin server détermine le périmètre de l’opération à partir de la ressource cible
  • Le serveur doit faire échouer la requête si le champ de requête Content-Type est absent ou ne correspond pas au corps de la requête
  • La query part de l’URI cible participe, comme pour toutes les méthodes HTTP, à l’identification de la ressource visée
    • Le fait qu’elle influence directement le résultat ou la manière dont elle l’influence relève du comportement propre à la ressource et sort du cadre de cette spécification
  • QUERY est safe du point de vue de la ressource cible
    • Le client ne demande ni n’attend de modification de l’état de la ressource cible
    • Il n’est pas interdit au serveur de créer des ressources HTTP permettant de consulter des informations supplémentaires
  • QUERY est idempotente, ce qui permet de la réessayer ou de la répéter si nécessaire après une défaillance de connexion
  • Une réponse 200 OK indique que la requête a été traitée avec succès et que le résultat est inclus dans le corps de la réponse

Types de média, négociation et gestion des erreurs

  • La sémantique d’une requête QUERY dépend du corps de la requête et des métadonnées associées, comme le type de média
  • Une requête dont le corps et les métadonnées sont incohérents doit généralement être rejetée avec une 4xx Client Error
  • La gestion des erreurs varie selon l’endroit où se situe le problème dans la requête
    • S’il n’y a pas d’information sur le type de média, la requête est invalide par définition et doit échouer avec un code 4xx comme 400
    • Si un type de média est indiqué mais n’est pas pris en charge par la ressource, 415 Unsupported Media Type est approprié
    • Même si le type de média est connu en principe, il peut aussi relever d’un 415 s’il n’a pas de sémantique QUERY pour la ressource cible
    • Si le type de média ne correspond pas au corps effectif de la requête, le serveur peut renvoyer 400 Bad Request
    • Le serveur ne peut pas faire de content sniffing pour déduire le type de média à partir du corps et remplacer une valeur absente ou erronée
    • Si le type et le corps correspondent mais que le contenu concret de la requête ne peut pas être traité, 422 Unprocessable Content peut être utilisé
    • Une requête SQL syntaxiquement valide qui pointe vers une table inexistante est un exemple de 422
    • Si la ressource ne prend pas en charge le type de média de réponse demandé par le client via Accept, 406 Not Acceptable est approprié
  • Le champ de réponse Accept-Query peut indiquer au client les types de média de requête pris en charge

Equivalent resource, Content-Location, Location

  • Une equivalent resource est une ressource qui représente une requête QUERY donnée et sa cible, et qui répond aux requêtes GET
  • Une equivalent resource prend en compte à la fois le corps de la requête et les métadonnées
    • Y compris les representation metadata comme le type de média du corps
  • Le serveur peut attribuer un URI à une equivalent resource, mais ce n’est pas obligatoire
  • Une réponse réussie peut inclure, dans l’en-tête Content-Location, l’identifiant de la ressource correspondant au résultat de la requête
    • Le client peut envoyer un GET vers l’URI indiqué pour récupérer le résultat de l’opération de requête qui vient d’être exécutée
    • Cette ressource peut être temporaire
  • Une réponse réussie peut inclure, dans l’en-tête Location, l’URI de l’equivalent resource de la requête QUERY
    • Le client peut envoyer un GET à l’URI indiqué sans renvoyer le corps de la requête, afin de rejouer la même opération de requête
    • Cet URI peut lui aussi être temporaire
    • Si une requête ultérieure échoue, le client peut réessayer avec la cible QUERY d’origine et le corps précédemment soumis

Redirections et requêtes conditionnelles

  • Le serveur peut choisir une réponse indirecte redirigeant le user agent vers un autre URI pour une requête QUERY
  • 301 Moved Permanently et 308 Permanent Redirect indiquent que la ressource cible a été déplacée de manière permanente vers un autre URI pointé par Location
  • 302 Found et 307 Temporary Redirect indiquent un déplacement temporaire de la ressource cible
  • Dans ces quatre cas, le serveur suggère qu’une requête QUERY similaire envoyée au nouvel URI cible permettra d’exécuter la requête d’origine
  • L’exception consistant à rediriger POST vers GET après un 301 ou un 302 ne s’applique pas aux requêtes QUERY
  • Un 303 See Other pour QUERY indique que la requête d’origine peut être exécutée comme une requête de consultation ordinaire sur l’URI indiqué par Location
    • En HTTP, cela signifie envoyer une requête GET vers le nouvel URI cible
  • Dans une QUERY conditionnelle, la selected representation est la même que pour un GET sur l’equivalent resource de cette requête QUERY
  • Le client peut demander que le résultat ne soit renvoyé que si les conditions spécifiées par les en-têtes conditionnels sont remplies

Mise en cache et requêtes Range

  • Les réponses à la méthode QUERY sont cacheables, et un cache peut les utiliser pour satisfaire des requêtes QUERY ultérieures
  • La clé de cache d’une requête QUERY doit inclure le corps de la requête ainsi que les métadonnées associées
  • Le cache peut supprimer certaines différences sémantiquement non significatives lors de la génération de la clé de cache
    • suppression du content encoding
    • normalisation selon les conventions de format indiquées par un suffixe de sous-type de média comme +json
    • normalisation selon la sémantique du corps indiquée par Content-Type
  • Ces transformations servent uniquement à la génération de la clé de cache et ne modifient pas la requête elle-même
  • Le client peut indiquer, avec la directive de cache no-transform, qu’il ne souhaite pas de telles transformations, mais cette directive reste advisory
  • La mise en cache des réponses QUERY est intrinsèquement plus complexe que celle de GET
    • Il faut lire l’intégralité du corps de la requête pour déterminer la clé de cache
    • Si la réponse QUERY fournit un URI d’equivalent resource via Location, le client peut ensuite basculer vers GET pour simplifier le traitement
  • La sémantique des Range Request pour QUERY est la même que pour GET
  • La seule range unit définie au moment de la rédaction, Byte Range Request, a peu d’intérêt pour les résultats de QUERY
  • Dans de nombreux cas, le format de requête lui-même fournit déjà des mécanismes de limitation ou de pagination, comme FETCH FIRST ... ROWS ONLY en SQL, et l’usage de ces fonctions intégrées est attendu

En-tête de réponse Accept-Query

  • L’en-tête de réponse Accept-Query indique directement qu’une ressource prend en charge la méthode QUERY et identifie les types de média de requête pouvant être utilisés
  • Accept-Query est une liste de media ranges utilisant la syntaxe Structured Fields
  • Un media range est représenté comme un List Structured Header Field composé de valeurs Token ou String contenant une valeur media range sans paramètre
  • Les paramètres de type de média sont mappés vers des Structured Field Parameters
    • Le choix entre String et Token n’a pas d’importance sémantique
    • Le destinataire peut convertir un Token en String, mais ne doit pas traiter différemment la valeur selon le type reçu
  • Un type de média ne se mappe pas exactement sur un Token ; lorsqu’il autorise un chiffre en tête, il faut utiliser la forme String
  • Les seuls wildcards pris en charge sont */* et xxxx/*
  • L’ordre des types listés dans la valeur du champ n’a pas d’importance
  • Une valeur Accept-Query s’applique à tous les URI du serveur partageant le même path, et le composant query est ignoré
  • Si des requêtes vers la même ressource renvoient des valeurs Accept-Query différentes, c’est la valeur fraîche la plus récente qui est utilisée
  • Exemple :
    • Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"
  • Accept-Query peut ressembler à Accept, mais comme il s’agit d’un Structured Field, il doit suivre les règles de traitement des Structured Fields de la RFC 9651

Considérations de sécurité et CORS

  • QUERY suit toutes les considérations générales de sécurité applicables aux méthodes HTTP définies dans la RFC 9110
  • QUERY peut être utilisée comme alternative au placement des informations de requête dans le composant query de l’URI
  • Comme les URI ont davantage de chances d’être journalisés ou traités par des intermédiaires que les corps de requête, QUERY peut être préférable à GET pour des requêtes contenant des informations sensibles
  • Si le serveur crée une ressource temporaire représentant le résultat de QUERY et lui attribue un URI, il ne doit pas inclure dans cet URI les parties sensibles du corps de requête original qui ne devraient pas apparaître dans les logs
  • Si un cache normalise incorrectement le corps de QUERY, ou d’une manière trop différente du traitement effectué par la ressource, il peut renvoyer à tort une mauvaise réponse par faux positif
  • Les requêtes QUERY émises par des user agents implémentant CORS nécessitent une requête preflight
    • QUERY ne fait pas partie de l’ensemble des CORS-safelisted methods

Enregistrement IANA et choix du nom de méthode

  • L’IANA a ajouté la méthode QUERY au HTTP Method Registry
    • Method Name: QUERY
    • Safe: yes
    • Idempotent: yes
    • Specification: RFC 10008 Section 2
  • L’IANA a ajouté le champ Accept-Query au HTTP Field Name Registry
    • Field Name: Accept-Query
    • Status: permanent
    • Structured Type: List
  • Le HTTP Method Registry contenait déjà PROPFIND, REPORT et SEARCH, qui ont des propriétés safe et idempotent
  • Aux premières étapes, SEARCH avait été utilisé, mais le nom final de la méthode est devenu QUERY
  • QUERY a été choisi pour les raisons suivantes
    • Les alternatives utilisaient le type de média générique application/xml dans le corps de la requête, avec une sémantique dépendant entièrement du corps
    • Ces alternatives provenaient toutes de l’activité WebDAV
    • QUERY capture bien la relation avec le query component de l’URI

1 commentaires

 
GN⁺ 4 시간 전
Avis sur Hacker News
  • Il aurait été bien plus convaincant s’il y avait eu un exemple de motivation fort, mais l’exemple choisi se laisse trop facilement exprimer avec GET, ce qui rend au contraire l’ensemble plus confus
    Même si on imagine un QUERY avec une grosse structure de filtre JSON ou une image en entrée dans le corps de la requête, le fait que le corps de la requête devienne une partie de la clé de cache paraît très étrange. On se retrouve avec des clés de cache infinies contrôlées par l’utilisateur, et les stratégies de cache classiques n’ont en pratique d’autre choix que de comparer le corps bit à bit ou d’en calculer le hachage, ce qui rend l’invalidation de cache très facile dans un contexte malveillant
    Si l’on construit un service qui a besoin de filtrage complexe ou d’entrées complexes comme des images, le cache sera probablement très loin de la couche HTTP. Par exemple, on utilisera comme clé des colonnes de données individuelles issues d’un join, ou un embedding indexé par le hachage perceptuel d’une image décodée, indépendamment de sa représentation exacte en bits sur le fil
    Je ne vois pas pourquoi il faudrait capturer cela sous une forme générique. Il vaudrait bien mieux exprimer la sémantique de cache sur POST avec un nouvel en-tête comme "Vary: request-body". Ce serait entièrement rétrocompatible, et on pourrait l’ignorer partout sauf dans les 0,1 % de cas d’usage CDN où ce comportement pourrait être utile

    • La partie query de l’URI dans GET est, en pratique, elle aussi presque sans limite et contrôlée par l’utilisateur, et comme elle fait partie de l’URI, elle entre aussi dans la clé de cache. Je ne vois donc pas trop pourquoi l’inverse soulève soudain un problème particulier
    • Si le navigateur veut une clé de cache plus petite, il lui suffit de stocker un hachage résistant aux collisions du corps. Par exemple, SHA-256 ferait l’affaire
      Je ne vois pas vraiment d’attaque liée au cache qui ne s’appliquerait pas de la même façon aux paramètres de requête. Si on veut faire déborder un cache, fabriquer un paramètre de requête unique de 30 caractères est aussi simple que fabriquer un corps de requête de 30 Mo
    • Tous les cas d’usage ne concernent pas l’Internet public, et le fait que ce ne soit pas utile sur l’Internet public ne signifie pas que cela ne peut pas être standardisé
      En pratique, les systèmes destinés à l’Internet public utiliseraient un hachage sécurisé comme clé de cache pour toujours garder une taille fixe. Les clés de cache incluent déjà des URL potentiellement très longues ainsi qu’un ensemble arbitraire de valeurs d’en-tête
    • On peut envoyer une image dans le corps de la requête, mais on peut déjà le faire aujourd’hui avec un paramètre de requête en base64. Toute proposition de standard peut être mal utilisée si on le veut vraiment
      Les GET avec paramètres de requête sont déjà opaques et rendent l’invalidation de cache facile
    • Par exemple, je suis en train de travailler sur un serveur MCP pour une base de données. Dans ChatGPT, je veux d’abord faire un POST de simulation qui sera annulé avant commit, mais comme les deux ne sont que des requêtes POST qui diffèrent d’un seul attribut, cela déclenche souvent la couche de sécurité de l’outil. Pour plusieurs raisons, il est aussi difficile de déboguer la cause exacte
      En revanche, mettre QUERY avant POST me semble meilleur. Ce ne seraient plus simplement les mêmes requêtes avec un drapeau de sécurité, mais deux types de requêtes distincts
  • Je me demande si les formulaires HTML ajouteront le support de QUERY
    QUERY doit être idempotent, donc cela éviterait l’avertissement agaçant de resoumission quand on recharge la page de résultat d’un formulaire POST

    • Le fait de vouloir plus de méthodes que GET/POST dans les formulaires HTML existe depuis des décennies. Il y a justement une proposition WHATWG ; si vous voulez ajouter votre voix, c’est ici : https://github.com/whatwg/html/pull/11347
    • Une des étrangetés des formulaires, c’est que le résultat d’un POST de formulaire est une page avec un emplacement (URL), mais qu’on ne peut pourtant pas charger via cet emplacement. À ma connaissance, le fait que cette page provienne d’un POST et non d’un GET n’est stocké nulle part de visible pour l’utilisateur ou le JS, et le rechargement se comporte bizarrement
      Si on ajoute method=QUERY, cela créera simplement une nouvelle variante de cette étrangeté
    • Il vaut mieux résoudre cela avec le pattern POST-Redirect-GET
    • Voir https://github.com/whatwg/html/issues/12594
    • Ils n’ont jamais ajouté la prise en charge d’autres verbes, mais on est dans une nouvelle époque, donc difficile de savoir
  • Pour ceux qui veulent encore faire comme si on était au siècle dernier : https://www.rfc-editor.org/rfc/rfc10008.txt

    • Je crois que j’aimerai toujours ce genre de document texte brut long et complet. Ça me rappelle la belle époque où je lisais des FAQ de jeux vidéo quand j’étais enfant. À bien des égards, c’est vraiment un format d’information supérieur
    • La mise en forme est magnifique. Je devrais la copier comme modèle de style pour des notes de service internes. C’est intemporel
  • « La possibilité d’attacher un corps aux requêtes GET a été examinée en profondeur par le groupe de travail IETF, mais il a finalement été décidé de créer à la place une nouvelle méthode QUERY. La décision de créer une méthode distincte découle de problèmes historiques d’interopérabilité et d’un respect strict des définitions architecturales fondamentales de HTTP »
    Pourtant, cela fait des années que j’envoie des corps de requête avec la méthode GET

    • Certains load balancers jetteraient le corps
    • En général, ce n’est pas une bonne idée. Certaines implémentations HTTP ne le permettent tout simplement pas. fetch, par exemple
      https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U...
      « Les requêtes GET ne peuvent pas inclure de corps »
      Cela peut aussi provoquer des problèmes étranges à cause du cache transparent
  • C’est surprenant d’en être déjà arrivé à des numéros RFC à 5 chiffres

  • Quelqu’un avait lancé un pari ambigu sur la date de publication de la RFC 10000, mais la numérotation a sauté directement de 9998 à 10008. Personne n’a gagné
    https://manifold.markets/CollectedOverSpread/when-will-rfc-1...

  • Si on doit utiliser la méthode QUERY pour interroger des résultats de recherche via HTTP et ne pas ajouter de paramètres de requête, le nom est déroutant
    Le terme query est déjà utilisé pour désigner les requêtes HTTP en général
    Rien qu’en lisant le titre de la RFC, j’ai été perdu

    • Dans quel domaine le terme query désigne-t-il les requêtes HTTP en général ? À l’oral, on peut parler de requête pour un GET, mais jamais pour POST, PUT ou DELETE
    • Oui. Et ce n’est pas forcément une requête non plus, cela peut aussi avoir un effet idempotent. On aurait plutôt dû l’appeler IPOST, c’est-à-dire POST idempotent
      Édit : ah, je vois, QUERY a été défini comme une méthode « sûre », sans effet de bord, pour permettre la mise en cache. Je m’étais trompé
  • Si cela finit réellement par remplacer les GET avec query string dans la nature, j’espère vraiment que les favoris du navigateur prendront en charge la préservation des paramètres de requête

    • Probablement pas. Cela remplacera plus vraisemblablement les usages actuels de POST pour ce type de requêtes
  • Je sais que cela sort du cadre de cette RFC, mais j’aime l’idée que cela puisse être facilement étendu pour permettre à EventSource de fonctionner aussi avec des requêtes IA en streaming
    Comme le corps de la requête est nécessaire, tout le monde utilise POST, et les résultats en streaming utilisent souvent le protocole text/event-stream dans la réponse. Mais comme l’état ne change pas réellement, ce n’est pas techniquement très cohérent, et EventSource s’obstine à n’utiliser que GET. Beaucoup d’API réimplémentent donc la même chose avec leur propre parseur

  • En voyant GET: Content (body) "no defined semantics", je me suis dit qu’autoriser un corps sur GET ne serait peut-être pas si grave, mais dans la spécification d’origine, le corps d’un GET est censé être totalement ignoré
    En outre, si une partie importante de la requête se retrouve dans un corps qui est supprimé, le cache peut aussi être cassé

    • Un GET avec uniquement un URI a pour sémantique de récupérer la représentation actuelle d’une ressource. C’est la forme la plus fondamentale de l’hyperlien, et c’est assez important pour le fonctionnement du Web
      Si on ajoute des paramètres dans le corps d’un GET, alors deux requêtes utilisant le même URI ne peuvent plus être considérées comme pointant vers la même chose, ce qui brise les contraintes de cette méthode