Resumen

  • Un índice de la tabla dinámica de HPACK solo vale dentro del contexto, la dirección y la secuencia correctos de una conexión HTTP/2.
  • Reconstruir una lista de campos no decide si una respuesta se guardó ni si puede reutilizarse: clave, frescura, validación y resultado pertenecen a otra capa.

Dos contadores suelen acabar bajo la misma palabra. Uno dice cuántos octetos evitó HPACK porque el receptor ya conservaba un campo. Otro dice cuántas solicitudes respondió una caché sin consultar de nuevo al origen. Llamar «cache hit» al primero hace que una optimización de codificación parezca una decisión sobre la respuesta.

La confusión es seductora porque ambos mecanismos recuerdan. Pero HPACK recuerda pares de octetos para reconstruir una lista en una conexión. La caché HTTP conserva mensajes de respuesta y decide cuándo uno puede satisfacer una solicitud posterior. La coincidencia verbal termina ahí.

Roberto Peon coescribió la RFC 7541 con Hervé Ruellan. Su ficha pública en el IETF enumera esa especificación y la RFC 7540 de HTTP/2. Una biografía de QCon San Francisco de 2012 lo describía entonces como ingeniero de Google y cocreador de SPDY. Es contexto histórico, no una afirmación sobre su empleo actual. Tampoco debe atribuírsele la RFC 9113, la especificación vigente de HTTP/2, cuyos autores son otros.

El compresor no interpreta el campo

La RFC 7541 define una lista de cabeceras como una colección ordenada de pares nombre-valor. Puede contener duplicados. Para HPACK, cada nombre y valor es una secuencia opaca de octetos. Comprimir y descomprimir debe conservar el orden.

Por tanto, recuperar cache-control: private no significa haber aplicado private. En esa fase solo reaparecieron los octetos. El código HTTP que recibe la lista todavía debe reconocer el nombre, analizar el valor y darle la semántica correspondiente.

HPACK dispone de una tabla estática común y de tablas dinámicas dependientes del contexto. La estática es inmutable. La dinámica nace vacía, recibe inserciones a medida que se procesan los bloques, admite entradas duplicadas y expulsa las más antiguas para respetar un límite de tamaño.

Un índice puede representar un campo completo o señalar un nombre cuyo valor llega literalmente. No es una identidad estable. Las nuevas inserciones desplazan posiciones; las expulsiones borran referencias; una conexión nueva vuelve a empezar. El número 63 sin la generación de tabla es una dirección sin mapa.

La dirección forma parte de la prueba

En HTTP bidireccional, la RFC 7541 exige que las tablas dinámicas de codificación y decodificación de cada extremo sean independientes. La tabla que utiliza el cliente para enviar solicitudes no es la tabla con la que interpreta respuestas. El servidor mantiene el par inverso.

La RFC 9113 encuadra esos contextos en una conexión HTTP/2. Cada extremo conserva un contexto codificador y otro decodificador para todos los bloques de campos. La tabla dinámica es el estado principal de cada uno.

Una bitácora útil no puede limitarse a «estaba en la tabla de la conexión». Debe decir en qué extremo, en qué sentido y antes de qué bloque. Si el receptor omite la inserción de un bloque que pensaba descartar, su tabla queda distinta. Más tarde, un índice válido para el emisor puede apuntar a otros octetos o quedar fuera de rango para el receptor.

De ahí una regla que sorprende a quien piensa por flujo: incluso un bloque destinado a descartarse debe reensamblarse y descomprimirse. Puede cambiar el contexto necesario para mensajes posteriores. El estado de compresión atraviesa los flujos de una misma dirección.

El límite de memoria tiene su propio reloj

HTTP/2 inicia la conexión con 4.096 octetos como máximo inicial de la tabla. El decodificador comunica su límite mediante SETTINGS_HEADER_TABLE_SIZE. El codificador puede usar una capacidad menor y anuncia los cambios con una instrucción de HPACK.

La cifra es un valor inicial del protocolo, no una medición universal de productos. Además, una reducción debe coordinarse con el acuse de la configuración. Cuando corresponde, el primer bloque posterior al acuse tiene que comenzar con una actualización válida del tamaño.

Las inserciones expulsan entradas hasta volver a caber. Si una sola entrada supera la capacidad máxima, la tabla se vacía y esa entrada no queda almacenada. Se trata de un diccionario efímero, no de un archivo de respuestas.

La consecuencia de perder la secuencia es amplia. Si un bloque no puede descomprimirse, la RFC 9113 exige cerrar la conexión con COMPRESSION_ERROR. El bloque apareció en un flujo, pero la tabla condiciona los bloques futuros de otros flujos. El error prueba que la conversación de compresión dejó de ser fiable; no prueba que la operación de negocio fracasara por su contenido.

La caché sí decide reutilización

La RFC 9111 llama caché al almacén local de mensajes de respuesta y al subsistema que controla su almacenamiento, búsqueda y borrado. Una caché existe para permitir que una respuesta anterior satisfaga una solicitud posterior.

La selección necesita una clave que incluye como mínimo el método y el URI de destino. Vary puede incorporar campos de la solicitud. Después hay que comprobar si la respuesta es fresca, si puede servirse caducada o si fue validada con el origen. Directivas como no-store, private y no-cache afectan esas decisiones.

Una entrada HPACK carece de todo ello. Puede recordar los octetos de un ETag, pero no la representación a la que validan. Puede comprimir Age, pero no actualizar la edad. Puede reconstruir Vary, pero no comparar los valores de dos solicitudes.

El ejemplo más revelador es cache-control: private. Que el codificador lo inserte solo afirma que espera ahorrar octetos si reaparece. No crea una caché privada ni otorga permiso de almacenamiento. El componente de caché aplica el significado después. La respuesta puede seguir guardada cuando la entrada HPACK ya fue expulsada; y la entrada puede sobrevivir aunque la respuesta nunca se guardara.

También hay aciertos reales de caché cuyos campos viajan como literales o mediante la tabla estática. Una nueva conexión puede entregar una respuesta reutilizada con una tabla dinámica vacía. Ningún mecanismo es requisito ni prueba suficiente del otro.

«Descomprimido» no significa «confiable»

Un índice válido establece una compatibilidad estrecha: el receptor tenía el estado necesario para reconstruir ese campo en ese punto de la secuencia. El bloque puede seguir procesándose. Nada más queda resuelto automáticamente.

HTTP/2 aún debe validar nombres y valores. TLS ofrece propiedades de canal distintas. Un intermediario puede reescribir el mensaje. El origen quizá no recibió la solicitud y la aplicación puede ignorar un campo sintácticamente correcto. No hay una escalera automática desde descompresión hasta autenticidad o éxito.

La tabla también tiene un coste de privacidad. La RFC 7541 advierte que quien controla campos y observa longitudes comprimidas puede sondear su estado. TLS oculta el contenido, pero no elimina la señal de longitud. Una reducción de bytes puede revelar que una conjetura coincidió; tampoco toda variación es un ataque.

La forma literal «nunca indexada» evita insertar el valor y obliga a los intermediarios a conservar esa elección al recodificar. Es una precaución concreta, no una promesa total: un secreto con poca entropía sigue siendo adivinable, otras longitudes siguen expuestas y otros sistemas pueden registrarlo.

Una factura para cada mecanismo

La factura de compresión identifica conexión y extremos, dirección, flujo, orden del bloque, límite anunciado, acuse, tamaño efectivo, inserciones, expulsiones, forma de representación y resultado de descompresión. Si falta una parte de la historia, debe indicarlo.

La factura HTTP añade método, URI, estado y ruta hacia el origen. Si hubo caché, conserva la clave, los valores de Vary, la decisión de almacenamiento, edad, frescura, validadores, reglas de autorización, revalidación y resultado entregado a la aplicación.

Una captura puede demostrar sincronización de HPACK sin ver la caché. Una bitácora de caché puede demostrar reutilización sin revelar la representación en la red. Mantener ambos huecos visibles es más exacto que llenarlos con una etiqueta cómoda. Recordar un campo nunca equivale, por sí solo, a tener derecho a reutilizar una respuesta.

Fuentes