1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • A RFC 10008 define o método HTTP QUERY, no qual o recurso de destino processa de forma segura e idempotente uma consulta contida no corpo da requisição e retorna o resultado
  • O QUERY combina o caráter safe/idempotent do GET com o envio de corpo do POST, reduzindo problemas com URIs longas, custo de codificação em URI, exposição em logs e a necessidade de tratar cada combinação de consulta como um recurso separado
  • O servidor só pode processar uma requisição QUERY quando o Content-Type e o corpo forem consistentes; tipos não suportados, incompatibilidade entre tipo e corpo e consultas impossíveis de processar podem ser distinguidos com diferentes respostas 4xx
  • Uma resposta bem-sucedida pode indicar, com Content-Location, o recurso do resultado da consulta e, com Location, um recurso equivalente para repetir a mesma consulta
  • Respostas QUERY podem ser armazenadas em cache, mas a chave de cache deve incluir também o corpo e os metadados; em ambientes CORS, como não é um safelisted method, é necessário preflight

O padrão de consulta HTTP que o QUERY tenta resolver

  • A RFC 10008 é um documento Internet Standards Track que define o método de requisição HTTP QUERY
  • O QUERY solicita que o recurso de destino processe o corpo da requisição e responda com seu resultado
  • Assim como o POST, ele usa corpo, mas é definido como safe e idempotent, permitindo tentativas automáticas novamente ou reinício
  • Em consultas GET tradicionais, é comum colocar a entrada na URI
    • GET /feed?q=foo&limit=10&sort=-published HTTP/1.1
  • Colocar dados de consulta na URI traz mais restrições à medida que os dados crescem
    • Como a requisição passa por vários sistemas independentes, é difícil saber antecipadamente o limite real de tamanho da URI
    • O HTTP recomenda que remetente e destinatário suportem no mínimo 8000 octets, mas isso não garante todos os sistemas no caminho
    • Alguns dados têm alto custo para serem codificados como URI válida
    • A URI da requisição tem mais chance de ir para logs ou bookmarks do que o corpo da requisição
    • Ao codificar a consulta diretamente na URI, cada combinação possível de entrada passa a ser tratada como um recurso distinto

Um método que esclarece o significado entre GET e POST

  • Muitas implementações enviam consultas no corpo de um POST em vez de usar GET
    • POST /feed
    • Content-Type: application/x-www-form-urlencoded
    • corpo: q=foo&limit=10&sort=-published
  • Sem conhecimento específico do recurso e do servidor, esse padrão não deixa claro se a consulta é segura e idempotente
  • O QUERY envia a mesma entrada no corpo da requisição, mas o próprio método já é seguro e idempotente
    • QUERY /feed
    • Content-Type: application/x-www-form-urlencoded
    • corpo: q=foo&limit=10&sort=-published
  • Esse significado explícito facilita aplicar recursos HTTP como cache e tentativas automáticas novamente
  • O servidor pode atribuir uma URI à própria consulta ou ao resultado de uma consulta específica para uso posterior com GET

Regras centrais do método QUERY

  • O QUERY é usado para iniciar consultas no lado do servidor
  • O GET solicita a representação do recurso identificado pela URI de destino, enquanto o QUERY solicita a execução de uma operação de consulta dentro do escopo do recurso de destino
  • O corpo da requisição e seu tipo de mídia definem a consulta, e o origin server determina o escopo da operação com base no recurso de destino
  • O servidor deve falhar a requisição se o campo de requisição Content-Type estiver ausente ou não corresponder ao corpo da requisição
  • A query part da URI de destino participa da identificação do recurso consultado, como em qualquer método HTTP
    • Se essa query part afeta diretamente o resultado, e de que forma, é comportamento específico do recurso e está fora do escopo desta especificação
  • O QUERY é seguro do ponto de vista do recurso de destino
    • O cliente não solicita nem espera alteração de estado no recurso de destino
    • Não é proibido que o servidor crie recursos HTTP para consulta de informações adicionais
  • O QUERY é idempotente, então pode ser repetido ou reenviado quando necessário após falha de conexão
  • Uma resposta 200 OK indica que a consulta foi processada com sucesso e que o resultado está incluído no corpo da resposta

Tipos de mídia, negociação e tratamento de erros

  • O significado de uma requisição QUERY depende do corpo da requisição e de metadados associados, como o tipo de mídia
  • Requisições em que corpo e metadados não sejam consistentes normalmente devem ser rejeitadas com 4xx Client Error
  • O tratamento do erro varia conforme o ponto em que a requisição está incorreta
    • Se não houver informação de tipo de mídia, a requisição é inválida por definição e deve falhar com um código 4xx como 400
    • Se o tipo de mídia foi especificado, mas o recurso não o suporta, 415 Unsupported Media Type é apropriado
    • Mesmo que o tipo de mídia seja conhecido em princípio, se não houver semântica QUERY para o recurso de destino, também cabe 415
    • Se o tipo de mídia não corresponder ao corpo real da requisição, o servidor pode retornar 400 Bad Request
    • O servidor não pode fazer content sniffing do corpo para inferir o tipo de mídia e sobrescrever um valor ausente ou incorreto
    • Se tipo e corpo estiverem corretos, mas o conteúdo real da consulta não puder ser processado, pode-se usar 422 Unprocessable Content
    • Um exemplo de 422 é uma consulta SQL sintaticamente válida que aponta para uma tabela inexistente
    • Se o recurso não suportar o tipo de mídia de resposta solicitado pelo cliente em Accept, 406 Not Acceptable é apropriado
  • O campo de resposta Accept-Query pode informar ao cliente quais tipos de mídia de consulta são suportados

recurso equivalente, Content-Location e Location

  • Um recurso equivalente é um recurso que representa uma requisição QUERY específica e seu alvo, e que responde a requisições GET
  • O recurso equivalente considera tanto o corpo da requisição quanto os metadados
    • Isso inclui metadados de representação, como o tipo de mídia do corpo
  • O servidor pode atribuir uma URI ao recurso equivalente, mas isso não é obrigatório
  • Uma resposta bem-sucedida pode incluir, no cabeçalho Content-Location, um identificador do recurso correspondente ao resultado da consulta
    • O cliente pode enviar um GET para a URI indicada e recuperar o resultado da operação de consulta recém-executada
    • Esse recurso pode ser temporário
  • Uma resposta bem-sucedida pode incluir, no cabeçalho Location, a URI do recurso equivalente da requisição QUERY
    • O cliente pode repetir a mesma operação de consulta enviando um GET para a URI indicada, sem precisar reenviar o corpo da consulta
    • Essa URI também pode ser temporária
    • Se uma requisição posterior falhar, o cliente pode tentar novamente usando o alvo QUERY original e o corpo enviado anteriormente

Redirecionamento e requisições condicionais

  • O servidor pode optar por uma resposta indireta que redirecione o user agent para outra URI em resposta a uma requisição QUERY
  • 301 Moved Permanently e 308 Permanent Redirect indicam que o recurso de destino foi movido permanentemente para outra URI apontada por Location
  • 302 Found e 307 Temporary Redirect indicam uma movimentação temporária do recurso de destino
  • Nos quatro casos, o servidor sugere que a requisição original pode ser executada enviando uma requisição QUERY semelhante para a nova URI de destino
  • A exceção de redirecionar POST para GET após 301 ou 302 não se aplica a requisições QUERY
  • Um 303 See Other para QUERY indica que a consulta original pode ser executada como uma requisição comum de recuperação ao URI apontado por Location
    • Em HTTP, isso significa enviar um GET para a nova URI de destino
  • Em QUERY condicional, a selected representation é a mesma que em um GET para o recurso equivalente dessa requisição QUERY
  • O cliente pode solicitar que o resultado da consulta seja retornado apenas se as condições especificadas pelos cabeçalhos condicionais forem atendidas

Cache e requisições Range

  • Respostas ao método QUERY podem ser armazenadas em cache, e o cache pode ser usado para satisfazer requisições QUERY futuras
  • A chave de cache de uma requisição QUERY deve incluir o corpo da requisição e os metadados relacionados
  • O cache pode remover diferenças semanticamente irrelevantes ao gerar a chave de cache
    • remoção de content encoding
    • normalização segundo convenções de formato indicadas por sufixos de subtipo de mídia, como +json
    • normalização conforme o significado do corpo indicado por Content-Type
  • Essas transformações servem apenas para gerar a chave de cache e não modificam a requisição em si
  • O cliente pode indicar, com a diretiva de cache no-transform, que não deseja essas transformações, mas essa diretiva é advisory
  • O cache de respostas QUERY é inerentemente mais complexo do que o de GET
    • Para determinar a chave de cache, é preciso ler todo o corpo da requisição
    • Se a resposta QUERY fornecer uma URI de recurso equivalente em Location, o cliente pode depois migrar para GET e simplificar o processamento
  • O significado de Range Request em QUERY é o mesmo que em GET
  • A única range unit definida até o momento, Byte Range Request, tem pouco valor para resultados de QUERY
  • Em muitos casos, o próprio formato da consulta já oferece limitação de resultado ou paginação, como FETCH FIRST ... ROWS ONLY em SQL, e espera-se o uso desses recursos embutidos

Cabeçalho de resposta Accept-Query

  • O cabeçalho de resposta Accept-Query informa diretamente que um recurso suporta o método QUERY e identifica quais tipos de mídia de formato de consulta podem ser usados
  • O Accept-Query é uma lista de media ranges usando a sintaxe de Structured Fields
  • Um media range é expresso como um campo de cabeçalho estruturado do tipo List, com Token ou String contendo um valor de media range sem parâmetros
  • Parâmetros de tipo de mídia são mapeados para Structured Field Parameters
    • A escolha entre String e Token não é semanticamente importante
    • O receptor pode converter Token em String, mas não deve tratar de forma diferente conforme o tipo recebido
  • Tipos de mídia não se mapeiam exatamente para Token e, quando for necessário permitir números no início, deve-se usar o formato String
  • Os únicos wildcards suportados são */* e xxxx/*
  • A ordem dos tipos listados no valor do campo não é importante
  • O valor de Accept-Query se aplica a todas as URIs do servidor que compartilhem o mesmo path, e o query component é ignorado
  • Se a mesma requisição de recurso retornar valores diferentes de Accept-Query, usa-se o valor fresh mais recente recebido
  • O exemplo é o seguinte
    • Accept-Query: "application/jsonpath", application/sql;charset="UTF-8"
  • O Accept-Query se parece com Accept, mas, por ser um Structured Field, deve seguir as regras de processamento de Structured Fields da RFC 9651

Considerações de segurança e CORS

  • O QUERY segue as considerações gerais de segurança de todos os métodos HTTP definidas na RFC 9110
  • O QUERY pode ser usado como alternativa a colocar informações da requisição no query component da URI
  • Como URIs têm mais chance de ir para logs ou serem processadas por intermediários do que corpos de requisição, o QUERY pode ser preferível ao GET em consultas com informações sensíveis
  • Quando o servidor cria um recurso temporário para representar o resultado de uma QUERY e lhe atribui uma URI, não deve incluir nessa URI partes sensíveis se o corpo original da requisição contiver informações que não deveriam ser registradas em logs
  • Se um cache normalizar incorretamente o corpo de uma QUERY, ou de forma muito diferente do modo como o recurso o processa, pode retornar uma resposta errada por falso positivo
  • Requisições QUERY de user agents que implementam CORS exigem uma requisição preflight
    • O QUERY não faz parte do conjunto de CORS-safelisted methods

Registro na IANA e escolha do nome do método

  • A IANA adicionou o método QUERY ao HTTP Method Registry
    • Method Name: QUERY
    • Safe: yes
    • Idempotent: yes
    • Specification: RFC 10008 Section 2
  • A IANA também adicionou o campo Accept-Query ao HTTP Field Name Registry
    • Field Name: Accept-Query
    • Status: permanent
    • Structured Type: List
  • O HTTP Method Registry já continha PROPFIND, REPORT e SEARCH, que também têm propriedades safe e idempotent
  • Em estágios iniciais foi usado SEARCH, mas o nome final do método passou a ser QUERY
  • O QUERY foi escolhido pelos seguintes motivos
    • As alternativas usavam o tipo de mídia genérico application/xml no corpo da requisição, e o significado da requisição dependia inteiramente do corpo
    • Todas as alternativas vinham de atividades relacionadas ao WebDAV
    • QUERY captura bem a relação com o query component da URI

1 comentários

 
GN⁺ 4 시간 전
Comentários do Hacker News
  • Teria sido mais convincente se houvesse um exemplo de motivação forte, mas usaram exemplos que podem ser representados com GET com facilidade demais, o que acabou até distraindo
    Mesmo imaginando um QUERY com uma estrutura grande de filtro em JSON ou entrada de imagem no corpo da requisição, parece muito estranho que o corpo da requisição vire parte da chave de cache. Isso cria chaves de cache infinitas controladas pelo usuário, e as estratégias normais de cache acabam basicamente se resumindo a comparar ou hashear o corpo da requisição bit a bit, o que torna muito fácil invalidar o cache em cenários maliciosos
    Se eu estivesse criando um serviço que exige filtragem complexa ou entradas complexas como imagens, é bem provável que o cache ficasse longe da camada HTTP. Por exemplo, talvez a chave fosse baseada em colunas individuais de dados de um join, ou em embeddings indexados por hash perceptual da imagem decodificada, e não na representação exata em bits transmitida no wire
    Não entendo por que tentar capturar isso à força de forma genérica. Eu preferiria muito mais expressar a semântica de cache no POST com um novo cabeçalho tipo "Vary: request-body". Seria totalmente retrocompatível e poderia ser ignorado fora do 0,1% dos casos de CDN em que esse comportamento talvez seja útil

    • A parte de query do URI no GET também, na prática, quase não tem limite, é controlada pelo usuário e entra na chave de cache por ser parte do URI. Então não entendo por que o oposto estaria sendo tratado como algo tão especial
    • Se o navegador quiser uma chave de cache menor, basta armazenar um hash resistente a colisões do corpo. Poderia usar SHA-256, por exemplo
      Não consigo pensar em nenhum ataque relacionado a cache que não se aplique igualmente a parâmetros de query. Se alguém quiser estourar o cache, criar um parâmetro de query único de 30 caracteres é tão fácil quanto criar um corpo de requisição de 30 MB
    • Nem todo caso de uso é a internet pública, e algo não deixar de ser padronizável só porque não é útil na internet pública
      Na prática, sistemas voltados para a internet pública usariam um hash criptográfico como chave de cache para que ela tenha sempre o mesmo tamanho. Chaves de cache já incluem URLs potencialmente muito longas e conjuntos arbitrários de valores de cabeçalho
    • Também dá para enviar imagens no corpo da requisição, mas isso já pode ser feito com parâmetro de query em base64. Se alguém quiser usar algo de forma bizarra, qualquer padrão proposto pode ser usado mal
      GET com parâmetros de query já é opaco e já facilita invalidação de cache
    • Por exemplo, estou trabalhando agora num servidor MCP para banco de dados. No ChatGPT, antes do commit eu gostaria de fazer primeiro um POST de dry-run que depois é revertido, mas como ambos são requisições POST que só diferem em uma propriedade, isso aciona com frequência a camada de segurança da ferramenta. E por vários motivos é difícil depurar exatamente a causa
      Mas acho que QUERY antes de POST melhoraria isso, porque deixariam de ser o mesmo tipo de requisição com um flag de segurança e passariam a ser tipos de requisição diferentes
  • Fico me perguntando se formulários HTML vão adicionar suporte a QUERY
    QUERY deveria ser idempotente, então poderia evitar aquele aviso irritante de reenvio ao atualizar a página de resultado de um envio de formulário POST

    • Faz décadas que espero suporte a mais métodos além de GET/POST em formulários HTML. Já existe uma proposta no WHATWG, então se quiser reforçar a ideia, é aqui: https://github.com/whatwg/html/pull/11347
    • Uma coisa estranha dos formulários é que o resultado de um POST de formulário é uma página com localização (URL), mas que não pode ser carregada por aquela localização. Até onde eu sei, o fato de aquela página ser POST e não GET não fica armazenado em lugar nenhum visível ao usuário ou ao JS, e o comportamento de atualizar também é esquisito
      Se adicionarem method=QUERY, isso só cria mais uma variante dessa estranheza
    • É melhor resolver isso com o padrão POST-Redirect-GET
    • Veja https://github.com/whatwg/html/issues/12594
    • Nunca adicionaram suporte a outros verbos, mas agora é outra era, então vai saber
  • Para quem ainda quer fingir que está no século passado: https://www.rfc-editor.org/rfc/rfc10008.txt

    • Acho que vou gostar para sempre desse tipo de documento em texto simples longo e completo. Me lembra os bons tempos de ler FAQ de videogame quando eu era criança. Em vários sentidos, é um formato de informação realmente superior
    • A formatação é linda. Acho que vou copiar isso como template de estilo para memorandos internos de trabalho. É atemporal
  • “A opção de anexar um corpo a uma requisição GET foi profundamente considerada pelo grupo de trabalho da IETF, mas no fim decidiu-se criar o novo método QUERY. A decisão de criar um método separado foi motivada por problemas históricos de interoperabilidade e pela adesão estrita à definição arquitetural central do HTTP”
    Só que eu já venho enviando corpo de requisição com método GET há anos

    • Dizem que alguns load balancers descartam o corpo
    • Em geral não é uma boa ideia. Em algumas implementações HTTP isso nem é possível. fetch, por exemplo, é assim
      https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/U...
      “GET requests cannot include a body”
      Também pode causar problemas estranhos por causa de cache transparente
  • É impressionante que já chegamos a números RFC de 5 dígitos

  • Alguém fez uma aposta ambígua sobre quando o RFC 10000 seria publicado, mas a numeração pulou direto de 9998 para 10008. Ninguém ganhou
    https://manifold.markets/CollectedOverSpread/when-will-rfc-1...

  • Se para consultar resultados de busca em HTTP você usar o método QUERY e não adicionar parâmetros de query, então o nome fica confuso. O termo query já é usado para se referir a requisições HTTP de forma geral
    Só o título do RFC já me confundiu

    • Em que área o termo query é usado para se referir a requisições HTTP em geral? Coloquialmente até se chama requisição GET de consulta, mas POST, PUT e DELETE nunca são chamados assim
    • Verdade. E nem precisa necessariamente ser uma consulta; também pode ser um efeito idempotente. Talvez tivesse sido melhor chamar de IPOST, ou seja, POST idempotente
      Edit: ah, entendi, declararam QUERY como método “seguro”, sem efeitos colaterais, por causa da possibilidade de cache. Eu tinha me confundido
  • Se isso realmente acabar substituindo na prática as requisições GET com query string na natureza, eu realmente espero que os favoritos do navegador passem a suportar preservação dos parâmetros da requisição

    • Acho improvável. É mais provável que substitua os lugares que hoje usam POST para consulta
  • Isso foge do escopo deste RFC, mas gosto que uma extensão simples disso poderia fazer o EventSource do JS funcionar também para consultas de IA em streaming
    Como a requisição precisa de corpo, todo mundo usa POST, e o resultado em streaming na resposta costuma usar o protocolo text/event-stream. Mas como na prática não há mudança de estado, tecnicamente isso não se encaixa tão bem, e o EventSource teimosamente só aceita GET. Por isso muitas APIs acabam reimplementando a mesma funcionalidade com um parser próprio

  • Ao ver GET: Content (body) "no defined semantics", pensei que talvez não houvesse problema em permitir corpo no método GET, mas pela especificação original o corpo de GET deve ser completamente ignorado
    Além disso, se uma parte importante da requisição estiver num corpo que acaba sendo removido, o cache também pode quebrar

    • Um GET com apenas URI tem a semântica de obter a representação atual de um recurso. Essa é a forma mais básica de hyperlink e é bem importante para o funcionamento da web
      Se você adicionar parâmetros no corpo do GET, duas requisições com o mesmo URI deixam de poder ser tratadas como apontando para a mesma coisa, o que quebra essa restrição do método