1 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • RFC 10008 define el método HTTP QUERY, que devuelve resultados después de que el recurso de destino procese la consulta incluida en el cuerpo de la solicitud de forma segura e idempotente
  • QUERY combina la naturaleza safe/idempotent de GET con la forma de enviar cuerpo de POST, reduciendo URI largos, el costo de codificación en URI, la exposición en logs y la carga de tratar cada combinación de consulta como un recurso distinto
  • El servidor solo puede procesarla si el Content-Type y el cuerpo de la solicitud son consistentes; tipos no soportados, discrepancias entre tipo y cuerpo, y consultas no procesables pueden distinguirse con diferentes respuestas 4xx
  • Una respuesta exitosa puede indicar con Content-Location el recurso que representa el resultado de la consulta, y con Location un equivalent resource para volver a ejecutar la misma consulta
  • Las respuestas a QUERY pueden cachearse, pero la clave de caché debe incluir también el cuerpo y los metadatos; en entornos CORS, como no es un método safelisted, requiere preflight

El patrón de consultas HTTP que QUERY busca resolver

  • RFC 10008 es un documento de Internet Standards Track que define el método de solicitud QUERY de HTTP
  • QUERY solicita que el recurso de destino procese el cuerpo de la solicitud y responda con el resultado
  • Usa un cuerpo como POST, pero se define como safe e idempotent, por lo que permite reintentos o reinicios automáticos
  • En las consultas tradicionales con GET, lo normal es poner la entrada en la URI
    • GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
  • Cuando los datos de consulta se ponen en la URI, aumentan las limitaciones conforme crecen los datos
    • Como la solicitud pasa por varios sistemas independientes, es difícil conocer de antemano el límite real de tamaño de URI
    • HTTP recomienda que emisor y receptor soporten al menos 8000 octets, pero eso no garantiza a todos los sistemas en la ruta
    • Algunos datos tienen un costo alto para codificarse como URI válida
    • Es más probable que la URI de la solicitud quede en logs o se incluya en marcadores que el cuerpo de la solicitud
    • Si la consulta se codifica directamente en la URI, cada combinación posible de entradas se trata como un recurso separado

Un método que aclara el significado entre GET y POST

  • Muchas implementaciones envían consultas en el cuerpo de POST en lugar de usar GET
    • POST /feed
    • Content-Type: application/x-www-form-urlencoded
    • Cuerpo: q=foo&limit=10&sort=-published
  • Con ese enfoque, sin conocer el recurso específico y el servidor, no queda claro si es una consulta segura e idempotente
  • QUERY envía la misma entrada en el cuerpo de la solicitud, pero el propio método es seguro e idempotente
    • QUERY /feed
    • Content-Type: application/x-www-form-urlencoded
    • Cuerpo: q=foo&limit=10&sort=-published
  • Gracias a esta semántica explícita, es más fácil aplicar funciones HTTP como caché y reintentos automáticos
  • El servidor puede asignar una URI a la propia consulta o al resultado de una consulta específica para usarla después con GET

Reglas clave del método QUERY

  • QUERY se usa para iniciar una consulta del lado del servidor
  • GET solicita una representación del recurso identificado por la URI de destino, mientras que QUERY solicita realizar una operación de consulta dentro del alcance del recurso de destino
  • El cuerpo de la solicitud y su tipo de medio definen la consulta, y el origin server determina el alcance de la operación en función del recurso de destino
  • El servidor debe hacer fallar la solicitud si no hay campo de solicitud Content-Type o si no coincide con el cuerpo solicitado
  • La query part de la URI de destino participa en la identificación del recurso consultado, igual que en cualquier método HTTP
    • Si esa query part afecta directamente el resultado y de qué manera depende del comportamiento del recurso y queda fuera del alcance de esta especificación
  • QUERY es seguro desde la perspectiva del recurso de destino
    • El cliente no solicita ni espera cambios en el estado del recurso de destino
    • No está prohibido que el servidor cree recursos HTTP para consultar información adicional
  • QUERY es idempotente, por lo que puede repetirse o reintentarse si es necesario tras un fallo de conexión
  • Una respuesta 200 OK indica que la consulta se procesó correctamente y que el resultado está incluido en el cuerpo de la respuesta

Tipos de medio, negociación y manejo de errores

  • La semántica de una solicitud QUERY depende del cuerpo de la solicitud y de metadatos relacionados, como el tipo de medio
  • Las solicitudes en las que el cuerpo y los metadatos no son consistentes normalmente deben rechazarse con un 4xx Client Error
  • El manejo de errores varía según dónde esté el problema en la solicitud
    • Si no hay información de tipo de medio, la solicitud es inválida por definición y debe fallar con un código 4xx como 400
    • Si el tipo de medio está especificado pero el recurso no lo soporta, corresponde 415 Unsupported Media Type
    • Aunque el tipo de medio sea conocido en principio, si no tiene semántica QUERY para el recurso de destino, también corresponde 415
    • Si el tipo de medio no coincide con el cuerpo real de la solicitud, se puede devolver 400 Bad Request
    • El servidor no puede hacer content sniffing del cuerpo para inferir el tipo de medio y sobrescribir un valor ausente o incorrecto
    • Si el tipo y el cuerpo coinciden, pero el contenido real de la consulta no puede procesarse, puede usarse 422 Unprocessable Content
    • Un ejemplo de 422 es una consulta SQL sintácticamente válida que apunta a una tabla inexistente
    • Si el recurso no soporta el tipo de medio de respuesta solicitado por el cliente con Accept, corresponde 406 Not Acceptable
  • El campo de respuesta Accept-Query puede informar al cliente qué tipos de medio de consulta están soportados

equivalent resource, Content-Location y Location

  • Un equivalent resource es un recurso que representa una solicitud QUERY específica y su destino, y responde a solicitudes GET
  • El equivalent resource toma en cuenta tanto el cuerpo de la solicitud como sus metadatos
    • Esto incluye metadatos de representación, como el tipo de medio del cuerpo
  • El servidor puede asignar una URI a un equivalent resource, pero no está obligado a hacerlo
  • Una respuesta exitosa puede incluir en el encabezado Content-Location un identificador del recurso correspondiente al resultado de la consulta
    • El cliente puede enviar un GET a la URI indicada para recuperar el resultado de la operación de consulta recién ejecutada
    • Ese recurso puede ser temporal
  • Una respuesta exitosa puede incluir en el encabezado Location la URI del equivalent resource de la solicitud QUERY
    • El cliente puede enviar un GET a la URI indicada para repetir la misma operación de consulta sin reenviar el cuerpo de la consulta
    • Esa URI también puede ser temporal
    • Si una solicitud posterior falla, el cliente puede volver a intentar con el destino QUERY original y el cuerpo enviado previamente

Redirecciones y solicitudes condicionales

  • El servidor puede optar por una respuesta indirecta que redirija el user agent a otra URI para una solicitud QUERY
  • 301 Moved Permanently y 308 Permanent Redirect indican que el recurso de destino se movió de forma permanente a otra URI apuntada por Location
  • 302 Found y 307 Temporary Redirect significan un movimiento temporal del recurso de destino
  • En los cuatro casos, el servidor sugiere que la solicitud original puede realizarse enviando una solicitud QUERY similar a la nueva URI de destino
  • La excepción que redirige POST a GET después de 301 o 302 no aplica a las solicitudes QUERY
  • 303 See Other para QUERY indica que la consulta original puede realizarse como una solicitud de recuperación normal al URI indicado por Location
    • En HTTP eso significa enviar una solicitud GET a la nueva URI de destino
  • En QUERY condicional, la selected representation es la misma que la de un GET al equivalent resource de esa solicitud QUERY
  • El cliente puede pedir que el resultado de la consulta solo se devuelva en la respuesta si se cumplen las condiciones especificadas por los encabezados condicionales

Caché y solicitudes Range

  • Las respuestas al método QUERY son cacheables, y la caché puede usarse para satisfacer solicitudes QUERY posteriores
  • La clave de caché de una solicitud QUERY debe incluir el cuerpo de la solicitud y sus metadatos relacionados
  • La caché puede eliminar diferencias semánticamente irrelevantes al generar la clave de caché
    • eliminación de content encoding
    • normalización según convenciones de formato indicadas por sufijos de subtipo de medio como +json
    • normalización según la semántica del cuerpo indicada por Content-Type
  • Estas transformaciones son solo para generar la clave de caché y no modifican la solicitud en sí
  • El cliente puede indicar con la directiva de caché no-transform que no desea tales transformaciones, pero esta directiva es advisory
  • El caché de respuestas QUERY es inherentemente más complejo que el de GET
    • Para determinar la clave de caché, hay que leer todo el cuerpo de la solicitud
    • Si la respuesta QUERY proporciona una URI de equivalent resource en Location, el cliente puede pasar luego a GET para simplificar el manejo
  • La semántica de Range Request para QUERY es la misma que para GET
  • El único range unit definido al momento de redactarse, Byte Range Request, aporta poco valor para resultados de QUERY
  • Con frecuencia, el propio formato de consulta ya ofrece limitación de resultados o paginación, como FETCH FIRST ... ROWS ONLY en SQL, y se espera que se usen esas funciones integradas

Encabezado de respuesta Accept-Query

  • El encabezado de respuesta Accept-Query informa directamente que un recurso soporta el método QUERY e identifica los tipos de medio de consulta que pueden usarse
  • Accept-Query es una lista de media ranges que usa la sintaxis de Structured Fields
  • Un media range se expresa como un List Structured Header Field de Token o String que contiene un valor media range sin parámetros
  • Los parámetros del tipo de medio se mapean como Structured Field Parameters
    • La elección entre String y Token no tiene importancia semántica
    • El receptor puede convertir Token a String, pero no debe tratarlos de forma distinta según el tipo recibido
  • Los tipos de medio no se mapean exactamente a Token, y cuando se permite un número inicial debe usarse la forma String
  • Los únicos wildcard soportados son */* o xxxx/*
  • El orden de los tipos listados en el valor del campo no importa
  • El valor de Accept-Query aplica a todas las URI del servidor que comparten el mismo path, y se ignora el query component
  • Si solicitudes al mismo recurso devuelven distintos valores de Accept-Query, se usa el valor fresh más reciente recibido
  • El ejemplo es el siguiente
    • Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"
  • Accept-Query se parece a Accept, pero como es un Structured Field debe seguir las reglas de procesamiento de Structured Fields de RFC 9651

Consideraciones de seguridad y CORS

  • QUERY sigue todas las consideraciones generales de seguridad para métodos HTTP definidas en RFC 9110
  • QUERY puede usarse como alternativa a poner la información de la solicitud en el query component de la URI
  • Como las URI tienen más probabilidad de quedar en logs o de ser procesadas por intermediarios que los cuerpos de solicitud, QUERY puede ser preferible a GET para consultas con información sensible
  • Si el servidor crea un recurso temporal para representar el resultado de QUERY y le asigna una URI, no debe incluir en esa URI partes sensibles si el cuerpo original contenía información que no debería quedar en logs
  • Si una caché normaliza incorrectamente el cuerpo de QUERY o lo normaliza de forma muy distinta a como lo procesa el recurso, puede devolver una respuesta equivocada por false positive
  • Las solicitudes QUERY de user agents que implementan CORS requieren una solicitud preflight
    • QUERY no forma parte del conjunto de CORS-safelisted methods

Registro en IANA y elección del nombre del método

  • IANA agrega el método QUERY al HTTP Method Registry
    • Method Name: QUERY
    • Safe: yes
    • Idempotent: yes
    • Specification: RFC 10008 Section 2
  • IANA agrega el campo Accept-Query al HTTP Field Name Registry
    • Field Name: Accept-Query
    • Status: permanent
    • Structured Type: List
  • En el HTTP Method Registry ya existían PROPFIND, REPORT y SEARCH, con propiedades safe e idempotent
  • En etapas iniciales se usó SEARCH, pero el nombre final del método pasó a ser QUERY
  • QUERY fue elegido por las siguientes razones
    • Las alternativas usaban el tipo de medio general application/xml en el cuerpo de la solicitud y el significado de la solicitud dependía por completo del cuerpo
    • Todas las alternativas surgieron de actividades de WebDAV
    • QUERY captura bien la relación con el query component de la URI

1 comentarios

 
GN⁺ 4 시간 전
Comentarios en Hacker News
  • Habría sido más convincente si hubiera habido un ejemplo con motivación fuerte; usaron ejemplos que se pueden expresar demasiado fácil con GET y eso más bien distrae
    Incluso si imaginas un QUERY con una gran estructura de filtros JSON o una imagen de entrada en el cuerpo de la solicitud, se siente muy raro que el cuerpo de la solicitud forme parte de la clave de caché. Aparecen claves de caché infinitas controladas por el usuario, y las estrategias de caché normales en la práctica solo pueden comparar el cuerpo bit a bit o hacerle hash, así que en escenarios maliciosos se vuelve muy fácil invalidar la caché
    Si construyes un servicio que necesita filtrado complejo o entradas complejas como imágenes, es muy probable que el caché esté muy lejos de la capa HTTP. Por ejemplo, podría basarse en columnas de datos individuales de un join, o en embeddings cuya clave sea el hash perceptual de una imagen decodificada, sin relación con la representación exacta en bits sobre el cable
    No entiendo por qué querrían capturar esto a la fuerza de forma genérica. Me parece mucho mejor expresar la semántica de caché en POST con un encabezado nuevo como "Vary: request-body". Sería totalmente retrocompatible, y fuera de ese 0.1% de casos de uso de CDN donde tal vez sirva, se puede ignorar

    • La parte de query del URI en GET, en la práctica, también es casi ilimitada y controlada por el usuario, y como forma parte del URI también entra en la clave de caché. Por eso no entiendo por qué se plantea como si el caso opuesto fuera algo especial
    • Si el navegador quiere una clave de caché más pequeña, basta con guardar un hash resistente a colisiones del cuerpo. Por ejemplo, SHA-256
      No se me ocurre ningún ataque relacionado con caché que no aplique igual a los parámetros de query. Si quieres saturar la caché, crear un parámetro de query único de 30 caracteres es tan fácil como crear un cuerpo de solicitud de 30 MB
    • No todos los casos de uso son Internet pública, y que algo no sea útil en Internet pública no significa que no se pueda estandarizar
      En la práctica, un sistema para Internet pública usaría un hash criptográfico como clave de caché para que siempre tenga el mismo tamaño. Las claves de caché ya incluyen URLs que pueden ser larguísimas y conjuntos arbitrarios de valores de encabezado
    • También puedes enviar imágenes en el cuerpo de la solicitud, pero ya hoy puedes hacerlo con parámetros de query en base64. Si alguien quiere usar mal un estándar propuesto, lo hará con cualquiera
      Los GET con parámetros de query ya son opacos y facilitan la invalidación de caché
    • Por ejemplo, ahora mismo estoy creando un servidor MCP para una base de datos. En ChatGPT quiero hacer primero un POST de prueba que se revierte antes del commit, pero ambos son solicitudes POST que solo se diferencian en una propiedad, así que eso activa a menudo la capa de seguridad de la herramienta. Y por varias razones es difícil depurar la causa exacta
      Pero creo que sería mejor tener QUERY seguido de POST, porque ya no serían simplemente la misma solicitud con un flag de seguridad, sino tipos de solicitud distintos
  • Me pregunto si los formularios HTML agregarán soporte para QUERY
    QUERY debería ser idempotente, así que podría evitar la molesta advertencia de reenvío al refrescar la página de resultado de un envío de formulario POST

    • Llevo décadas queriendo que los formularios HTML soporten más métodos aparte de GET/POST. Ya existe una propuesta en WHATWG, así que si quieres sumar tu voz, aquí está: https://github.com/whatwg/html/pull/11347
    • Una rareza de los formularios es que el resultado de un POST de formulario es una página con una ubicación (URL), pero no se puede cargar desde esa ubicación. Hasta donde sé, el hecho de que esa página venga de POST y no de GET no se guarda en ningún lugar visible para el usuario o para JS, y refrescar también funciona de forma extraña
      Si se agrega method=QUERY, aparecerá otra variante nueva de esa rareza
    • Es mejor resolver esto con el patrón POST-Redirect-GET
    • Véase https://github.com/whatwg/html/issues/12594
    • Nunca han agregado soporte para otros verbos, pero ya es otra época, así que quién sabe
  • Para quienes todavía quieran fingir que seguimos en el siglo pasado: https://www.rfc-editor.org/rfc/rfc10008.txt

    • Creo que siempre me van a gustar este tipo de documentos largos y completos en texto plano. Me recuerdan a los buenos tiempos cuando leía FAQs de videojuegos de niño. En muchos sentidos es un formato de información realmente superior
    • El formato es hermoso. Debería copiarlo como plantilla de estilo para memorandos internos del trabajo. No pasa de moda
  • “La opción de adjuntar un cuerpo a una solicitud GET fue estudiada a fondo por el grupo de trabajo de IETF, pero al final se decidió crear el nuevo método QUERY. La decisión de crear un método aparte se debió a problemas históricos de interoperabilidad y al estricto apego a la definición arquitectónica central de HTTP”
    Bueno, yo llevo años enviando cuerpos de solicitud con el método GET

    • Algunos balanceadores de carga tiran el cuerpo
    • En general no es buena idea. En algunas implementaciones HTTP ni siquiera es posible. Por ejemplo, fetch
      https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U...
      “No puedes incluir un cuerpo en solicitudes GET”
      También puede causar problemas raros por el caché transparente
  • Sorprende que ya hayamos llegado a números RFC de 5 dígitos

  • Alguien hizo una apuesta ambigua sobre cuándo se publicaría el RFC 10000, pero la numeración saltó directamente de 9998 a 10008. Nadie ganó
    https://manifold.markets/CollectedOverSpread/when-will-rfc-1...

  • Si para consultar resultados de búsqueda en HTTP hay que usar el método QUERY y no agregar parámetros de query, el nombre termina siendo confuso
    El término query ya se usa para referirse a las solicitudes HTTP en general
    Solo con ver el título del RFC ya me confundí

    • ¿En qué ámbito se usa query para referirse a las solicitudes HTTP en general? Coloquialmente a veces se llama consulta a una solicitud GET, pero jamás a POST, PUT o DELETE
    • Sí. Además ni siquiera tiene que ser una consulta; también podría tener un efecto idempotente. Tal vez habría sido mejor llamarlo IPOST, o sea, POST idempotente
      Edit: ah, ya entendí, declararon QUERY como un método “seguro”, sin efectos colaterales, por la posibilidad de caché. Me confundí
  • Si esto realmente llegara a reemplazar en la práctica a las solicitudes GET con query string, de verdad espero que los marcadores del navegador soporten preservar los parámetros de la solicitud

    • Probablemente no pase. Es más probable que reemplace a los lugares que hoy usan POST con fines de consulta
  • Sé que esto queda fuera del alcance de este RFC, pero me gusta que una extensión sencilla de esto podría hacer que EventSource también funcione para consultas de IA en streaming
    Como la solicitud necesita cuerpo, todos usan POST, y para los resultados en streaming a menudo usan el protocolo text/event-stream en la respuesta. Pero en realidad no hay cambio de estado, así que técnicamente no encaja del todo, y EventSource de forma obstinada solo puede usar GET. Por eso muchas APIs reimplementan la misma funcionalidad con su propio parser

  • Al ver GET: Content (body) "no defined semantics", pensé que no estaría mal permitir cuerpos en GET, pero según la especificación original el cuerpo en GET debe ignorarse por completo
    Además, si una parte importante de la solicitud va en un cuerpo que luego se descarta, también se puede romper el caché

    • Un GET con solo URI tiene la semántica de obtener la representación actual de un recurso. Esa es la forma más básica de hipervínculo y es bastante importante para cómo funciona la web
      Si agregas parámetros en el cuerpo a GET, dos solicitudes con el mismo URI ya no pueden tratarse como si apuntaran a lo mismo, y eso rompe las restricciones de este método