Resumo

  • Um índice da tabela dinâmica HPACK só pode ser interpretado no contexto, na direção e na sequência corretos de uma conexão HTTP/2.
  • Decodificar uma lista de campos não é acertar o cache: chave, resposta armazenada, frescor, validação, autorização e efeito da aplicação pedem provas próprias.

Uma equipe comemora a queda do tamanho dos cabeçalhos. No painel, o evento recebe o nome de “acerto de cache HPACK”. O número melhora, mas o vocabulário piora: a conexão evitou retransmitir octetos; nenhum componente demonstrou que uma resposta anterior pudesse atender à requisição atual.

A diferença parece acadêmica até o primeiro incidente. Se o indicador for aceito como prova de cache, a carga na origem é calculada errado, uma diretiva pode parecer aplicada quando foi apenas descomprimida e o rastro deixa de explicar por que a mesma referência funcionou em uma direção e falhou na outra.

Roberto Peon é uma referência direta para esse limite. Ele e Hervé Ruellan assinam a RFC 7541, que define o HPACK. O perfil no IETF lista essa especificação e a RFC 7540, primeira RFC do HTTP/2. Uma biografia do QCon San Francisco de 2012 o descrevia, naquele momento, como engenheiro do Google e cocriador do SPDY. O texto é evidência histórica, não confirmação de cargo atual. A RFC 9113 atual possui outros autores.

A memória serve à reconstrução

Para o HPACK, uma lista de cabeçalhos é uma coleção ordenada de pares nome-valor. Pares duplicados são permitidos. Nomes e valores são sequências opacas de octetos, e a ordem original deve reaparecer após a descompressão.

Esse recorte define a autoridade do mecanismo. O decodificador pode devolver cache-control: private, porém não avalia private. Pode reconstruir um ETag, porém não sabe qual representação ele valida. A semântica entra depois, quando a implementação HTTP interpreta a lista.

O formato oferece uma tabela estática e tabelas dinâmicas. A estática é predefinida e imutável. A dinâmica nasce vazia, recebe campos durante o processamento, admite duplicatas e remove entradas antigas para respeitar um teto de memória.

Uma representação indexada aponta para esse espaço em movimento. A entrada mais nova ocupa a menor posição dinâmica. Inserções deslocam as demais; remoções fazem referências antigas perderem o objeto. Em outra conexão, o mesmo índice pode representar algo diferente ou ser inválido. O índice não é um identificador persistente.

O sentido da conexão não é detalhe

Em uso bidirecional, a RFC 7541 separa completamente as tabelas de codificação e decodificação de cada ponta. O cliente codifica requisições com um contexto e decodifica respostas com outro. O servidor mantém os contextos correspondentes. Não existe uma tabela dinâmica única para toda a conversa.

A RFC 9113 reafirma que cada ponta possui um contexto codificador e outro decodificador usados por todos os blocos de campos de uma conexão. A tabela dinâmica é o estado principal de cada contexto.

Por isso um registro precisa dizer quem enviou, quem recebeu, qual era a direção e qual bloco veio antes. Se o receptor deixa de processar uma inserção porque pretendia descartar o fluxo, o seu estado se desvia. Um índice posterior pode então apontar para octetos diferentes dos que o emissor tinha em mente.

Blocos de campos precisam ser reagrupados e descomprimidos mesmo quando a mensagem será descartada. A razão não é preservar a aplicação daquele fluxo, mas a sequência do dicionário compartilhado pelos próximos fluxos na mesma direção.

Capacidade negociada não vira arquivo

Uma conexão HTTP/2 começa com 4.096 bytes como máximo inicial de tabela. O decodificador comunica seu limite por SETTINGS_HEADER_TABLE_SIZE; o codificador escolhe uma capacidade efetiva que não o ultrapassa e sinaliza mudanças por uma instrução HPACK.

O valor inicial não comprova a configuração observada de um produto. O tamanho pode diminuir, e a redução depende da ordem entre o parâmetro e sua confirmação. Depois da confirmação, o próximo bloco precisa carregar uma atualização válida quando o estado existente exceder o novo máximo.

Cada inserção pode expulsar registros antigos. Se uma entrada sozinha for maior que o máximo, a tabela é esvaziada e a entrada não permanece. O objetivo é um dicionário limitado para economizar bytes, não retenção duradoura.

Quando o dicionário diverge, o efeito atravessa fluxos. A RFC 9113 manda encerrar a conexão com COMPRESSION_ERROR se um bloco não puder ser descomprimido. O bloco que revelou o problema pertence a um fluxo; a dependência quebrada pertence ao contexto da conexão. Isso não equivale a um julgamento da operação feita pela aplicação.

Cache é o sistema que reutiliza respostas

A RFC 9111 define cache HTTP como o armazenamento local de mensagens de resposta e o subsistema que controla guardar, localizar e apagar essas mensagens. Seu propósito é permitir que uma resposta anterior atenda a uma solicitação posterior, quando as regras autorizam.

A escolha começa por uma chave que inclui pelo menos método e URI de destino. Campos indicados por Vary podem participar. A resposta precisa estar fresca, poder ser servida vencida em uma condição permitida ou ser validada. no-store, private, no-cache e regras ligadas a autorização influenciam o resultado.

Um índice HPACK não contém essa decisão. A tabela pode reter age: 300 sem atualizar a idade. Pode reter vary: accept-language sem comparar idiomas. Pode comprimir um validador sem armazenar corpo, status ou URI.

Suponha que cache-control: private seja inserido na tabela dinâmica. A inserção diz apenas que o codificador escolheu explorar repetição. Ela não cria um cache privado e não impede por si mesma um armazenamento compartilhado. A camada de cache só decide depois de analisar a resposta. O campo pode sair do HPACK enquanto a resposta segue guardada; também pode ficar no HPACK quando nenhuma resposta foi guardada.

O caminho contrário desfaz outra suposição. Um verdadeiro acerto de cache pode ser enviado com campos literais, com referências estáticas ou numa conexão recém-aberta sem entradas dinâmicas. Acerto de compressão e reuso de resposta não são causas necessárias um do outro.

Decodificar não autentica

Uma referência válida prova que o receptor tinha estado compatível naquele ponto da sequência e conseguiu recompor o campo. É uma evidência útil e estreita. Ela não prova que o valor seja válido segundo HTTP, que veio da origem esperada, que um intermediário não o transformou ou que a aplicação executou algo.

O mesmo cuidado vale para confidencialidade. A RFC 7541 alerta que um agente capaz de influenciar campos e observar comprimentos comprimidos pode sondar o estado da tabela. TLS cifra o conteúdo, mas o comprimento ainda carrega sinal. Nem toda mudança de razão de compressão é ataque; tampouco toda economia é inofensiva.

A representação literal “nunca indexada” impede que o valor entre na tabela e obriga intermediários a preservar essa escolha ao recodificar. Ela reduz uma superfície, mas não transforma um segredo previsível em segredo forte, não elimina todos os sinais de tamanho e não substitui controle de acesso.

O recibo precisa de duas páginas

Na página de compressão ficam conexão, papéis das pontas, direção, fluxo e ordem dos blocos. Também ficam limite anunciado, confirmação, capacidade efetiva, inserções, expulsões, atualizações, forma de representação e resultado da descompressão.

Na página HTTP ficam método, URI, status, caminho até a origem e interpretação dos campos. Havendo cache, entram chave, valores de Vary, decisão de armazenamento, idade, frescor, validadores, regras de autorização, revalidação e resultado que chegou à aplicação.

Uma captura de pacotes pode provar sincronismo sem enxergar o cache. Um log de cache pode provar reuso sem mostrar como os campos cruzaram a rede. A ausência de uma página não autoriza preencher a outra por inferência. A tabela de Peon e Ruellan lembra octetos; ela jamais prometeu guardar respostas.

Fontes