Introdução ao HTTP/2

O HTTP/2 tornará nossos aplicativos mais rápidos, simples e robustos, uma combinação rara, permitindo desfazer muitas das soluções alternativas HTTP/1.1 feitas anteriormente em nossos aplicativos e resolver essas questões dentro da própria camada de transporte. Melhor ainda, ele abre uma série de oportunidades totalmente novas para otimizar nossos aplicativos e melhorar o desempenho.

Os principais objetivos do HTTP/2 são reduzir a latência permitindo a multiplexação completa de solicitações e respostas, minimizar a sobrecarga do protocolo com a compactação eficiente dos campos de cabeçalho HTTP e adicionar suporte para priorização de solicitações e envio de servidores. Para implementar esses requisitos, há um grande elenco de suporte com outras melhorias de protocolo, como novos mecanismos de controle de fluxo, tratamento de erros e atualização, mas esses são os recursos mais importantes que todo desenvolvedor da Web precisa entender e usar nos aplicativos.

O HTTP/2 não modifica de maneira alguma a semântica HTTP do aplicativo. Todos os conceitos principais, como métodos HTTP, códigos de status, URIs e campos de cabeçalho, permanecem em vigor. Em vez disso, o HTTP/2 modifica a forma como os dados são formatados (enquadrados) e transportados entre o cliente e o servidor, os quais gerenciam todo o processo, e oculta toda a complexidade dos nossos aplicativos na nova camada de frame. Como resultado, todos os aplicativos existentes podem ser enviados sem modificação.

Por que não HTTP/1.2?

Para atingir as metas de desempenho definidas pelo Grupo de trabalho HTTP, o HTTP/2 introduz uma nova camada de frame binário que não é compatível com versões anteriores de servidores e clientes HTTP/1.x anteriores. Por isso, a versão principal do protocolo foi incrementada para HTTP/2.

Sendo assim, a menos que você esteja implementando um servidor da Web (ou um cliente personalizado) trabalhando com soquetes TCP brutos, não haverá nenhuma diferença: todo o novo enquadramento de baixo nível é executado pelo cliente e pelo servidor em seu nome. As únicas diferenças observáveis serão o desempenho aprimorado e a disponibilidade de novos recursos, como priorização de solicitações, controle de fluxo e push do servidor.

Uma breve história do SDPY e do HTTP/2

O SPDY foi um protocolo experimental, desenvolvido no Google e anunciado em meados de 2009 que tinha como objetivo principal tentar reduzir a latência de carregamento de páginas da Web, corrigindo algumas das limitações de desempenho conhecidas do HTTP/1.1. Especificamente, as metas do projeto foram definidas da seguinte maneira:

  • Reduza em 50% o tempo de carregamento da página (PLT).
  • Evitar a necessidade de alterações no conteúdo pelos autores de sites.
  • Minimize a complexidade da implantação e evite mudanças na infraestrutura de rede.
  • Desenvolver esse novo protocolo em parceria com a comunidade de código aberto.
  • Coletar dados de desempenho reais para (in)validar o protocolo experimental.

Não muito depois do anúncio inicial, Mike Belshe e Roberto Peon, engenheiros de software do Google, compartilharam os primeiros resultados, a documentação e o código-fonte para a implementação experimental do novo protocolo SPDY:

Até agora, testamos o SDPY apenas em condições de laboratório. Os resultados iniciais são muito animadores: quando fazemos o download dos 25 principais sites por conexões de rede doméstica simuladas, notamos uma melhoria significativa no desempenho: as páginas são carregadas até 55% mais rápido. (Blog do Chromium)

Avance para 2012, e o novo protocolo experimental era compatível com Chrome, Firefox e Opera. Um número cada vez maior de sites, tanto grandes (por exemplo, Google, Twitter, Facebook) e pequenos, estavam implantando o SDPY na infraestrutura. Na verdade, o SDPY estava a caminho de se tornar um padrão por causa da crescente adoção do setor.

Observando essa tendência, o Grupo de trabalho HTTP (HTTP-WG) iniciou um novo esforço para aproveitar as lições aprendidas com o SPDY, criar e melhorá-las e fornecer um padrão "HTTP/2" oficial. Um novo termo de abertura foi redigido, uma abertura de propostas do HTTP/2 foi feita e, depois de muita discussão no grupo de trabalho, a especificação SPDY foi adotada como ponto de partida para o novo protocolo HTTP/2.

Nos anos seguintes, o SPDY e o HTTP/2 continuaram coevoluindo em paralelo, com o SPDY atuando como um branch experimental usado para testar novos recursos e propostas para o padrão HTTP/2. O que parece bom no papel pode não funcionar na prática e vice-versa, e o SPDY ofereceu uma maneira de testar e avaliar cada proposta antes da inclusão no padrão HTTP/2. No final, esse processo durou três anos e resultou em mais de uma dúzia de rascunhos intermediários:

  • Março de 2012: chamada de propostas para HTTP/2
  • Novembro de 2012: primeiro rascunho do HTTP/2 (com base no SPDY)
  • Agosto de 2014: publicação do rascunho 17 do HTTP/2 e do rascunho 12 do HPACK
  • Agosto de 2014: última chamada do Grupo de trabalho para HTTP/2
  • Fevereiro de 2015: IESG aprova os rascunhos de HTTP/2 e HPACK
  • Maio de 2015: a RFC 7540 (HTTP/2) e a RFC 7541 (HPACK) foram publicadas

No início de 2015, o IESG revisou e aprovou o novo padrão HTTP/2 para publicação. Pouco depois, a equipe do Google Chrome anunciou a programação da descontinuação da extensão SPDY e NPN para TLS:

As principais mudanças do HTTP/2 em relação ao HTTP/1.1 têm como foco o aprimoramento do desempenho. Alguns recursos importantes, como multiplexação, compactação de cabeçalho, priorização e negociação de protocolo, evoluíram do trabalho feito em um protocolo aberto anteriormente, mas não padrão, chamado SPDY. O Chrome oferece suporte ao SPDY desde o Chrome 6, mas como a maioria dos benefícios está presente no HTTP/2, é hora de nos despedir. Planejamos remover o suporte para o SPDY no início de 2016 e também o suporte para a extensão TLS chamada NPN em favor do ALPN no Chrome. Recomendamos que os desenvolvedores de servidores mudem para HTTP/2 e ALPN.

Estamos felizes por ter contribuído com o processo de padrões abertos que levou ao HTTP/2 e esperamos ver uma ampla adoção, dado o amplo envolvimento do setor na padronização e implementação. (Blog do Chromium)

A evolução conjunta do SPDY e do HTTP/2 permitiu que desenvolvedores de servidores, navegadores e sites ganhassem experiência no mundo real com o novo protocolo à medida que ele vinha sendo desenvolvido. Como resultado, o HTTP/2 é um dos melhores e mais testados padrões desde a sua concepção. Quando o HTTP/2 foi aprovado pelo IESG, havia dezenas de implementações de cliente e servidor completamente testadas e prontas para produção. Na verdade, apenas algumas semanas após a aprovação do protocolo final, muitos usuários já aproveitavam os benefícios porque vários navegadores conhecidos (e muitos sites) implantaram suporte completo a HTTP/2.

Objetivos técnicos e de design

Versões anteriores do protocolo HTTP foram projetadas intencionalmente para simplificar a implementação: o HTTP/0.9 era um protocolo de uma linha para inicializar a World Wide Web; o HTTP/1.0 documentou as extensões conhecidas para HTTP/0.9 em um padrão informativo; o HTTP/1.1 introduziu um padrão IETF oficial. Consulte Breve história do HTTP. Por isso, o HTTP/0.9-1.x ofereceu exatamente o que se propôs a fazer: o HTTP é um dos protocolos de aplicativos mais adotados da Internet.

Infelizmente, a simplicidade de implementação também aconteceu às custas do desempenho do aplicativo: os clientes HTTP/1.x precisam usar várias conexões para conseguir simultaneidade e reduzir a latência; o HTTP/1.x não compacta os cabeçalhos de solicitação e resposta, causando tráfego de rede desnecessário; o HTTP/1.x não permite priorização eficaz de recursos, resultando no uso inadequado da conexão TCP subjacente e assim por diante.

Essas limitações não foram fatais. No entanto, à medida que os aplicativos da Web continuavam a crescer em escopo, complexidade e importância em nossas vidas diárias, elas impunham um encargo crescente para desenvolvedores e usuários da Web, que é a lacuna exata para a qual o HTTP/2 foi projetado:

O HTTP/2 possibilita um uso mais eficiente dos recursos de rede e uma percepção reduzida da latência ao introduzir a compactação do campo de cabeçalho e permitir várias trocas simultâneas na mesma conexão. Especificamente, ele permite intercalar mensagens de solicitação e resposta na mesma conexão e usa uma codificação eficiente para campos de cabeçalho HTTP. Ele também possibilita a priorização de solicitações, permitindo que solicitações mais importantes sejam concluídas com mais rapidez, melhorando ainda mais o desempenho.

O protocolo resultante é mais fácil de usar para a rede, porque é possível usar menos conexões TCP em comparação com o HTTP/1.x. Isso significa menos concorrência com outros fluxos e conexões de maior duração, o que, por sua vez, leva a uma melhor utilização da capacidade de rede disponível. Por fim, o HTTP/2 também possibilita o processamento mais eficiente de mensagens com o uso do enquadramento binário de mensagens. (Protocolo de transferência de hipertexto versão 2, rascunho 17)

É importante observar que o HTTP/2 está ampliando, não substituindo, os padrões HTTP anteriores. A semântica HTTP do aplicativo é a mesma, e nenhuma mudança foi feita na funcionalidade oferecida ou nos conceitos principais, como métodos HTTP, códigos de status, URIs e campos de cabeçalho. Essas mudanças estavam explicitamente fora do escopo da iniciativa HTTP/2. Apesar de a API de alto nível permanecer a mesma, é importante entender como as alterações de nível inferior abordam as limitações de desempenho dos protocolos anteriores. Vamos conhecer a camada de enquadramento binário e seus recursos.

Camada de enquadramento binário

No centro de todas as melhorias de desempenho do HTTP/2 está a nova camada de frame binário, que determina como as mensagens HTTP são encapsuladas e transferidas entre o cliente e o servidor.

Camada de frame binário HTTP/2

A "camada" se refere a uma opção de design para introduzir um novo mecanismo de codificação otimizado entre a interface de soquete e a API HTTP de nível mais alto exposta aos nossos aplicativos. A semântica HTTP, como verbos, métodos e cabeçalhos, não é afetada, mas a maneira como são codificadas em trânsito é diferente. Ao contrário do protocolo HTTP/1.x de texto simples delimitado por nova linha, toda a comunicação HTTP/2 é dividida em mensagens e frames menores, cada um codificado em formato binário.

Como resultado, tanto o cliente quanto o servidor precisam usar o novo mecanismo de codificação binária para se entenderem: um cliente HTTP/1.x não entenderá um servidor somente HTTP/2 e vice-versa. Felizmente, nossos aplicativos não tomam conhecimento de todas essas mudanças, porque o cliente e o servidor realizam todo o trabalho de enquadramento necessário em nosso nome.

Streams, mensagens e frames

A introdução do novo mecanismo de enquadramento binário muda a forma como os dados são trocados entre o cliente e o servidor. Para descrever esse processo, vamos conhecer a terminologia do HTTP/2:

  • Stream: um fluxo bidirecional de bytes dentro de uma conexão estabelecida, que pode carregar uma ou mais mensagens.
  • Mensagem: uma sequência completa de frames que mapeia uma mensagem lógica de solicitação ou resposta.
  • Frame: a menor unidade de comunicação no HTTP/2, cada uma contendo um cabeçalho de frame, que, no mínimo, identifica o stream a que o frame pertence.

A relação desses termos pode ser resumida da seguinte forma:

  • Toda a comunicação é realizada por uma única conexão TCP que pode transportar qualquer número de fluxos bidirecionais.
  • Cada stream tem um identificador exclusivo e informações de prioridade opcionais que são usadas para transportar mensagens bidirecionais.
  • Cada mensagem é uma mensagem HTTP lógica, como uma solicitação ou uma resposta, que consiste em um ou mais frames.
  • O frame é a menor unidade de comunicação que carrega um tipo específico de dados, por exemplo, cabeçalhos HTTP, payload da mensagem e assim por diante. Os frames de diferentes fluxos podem ser intercalados e, em seguida, reagrupados usando o identificador de stream incorporado no cabeçalho de cada frame.

Streams, mensagens e frames do HTTP/2

Em resumo, o HTTP/2 divide a comunicação do protocolo HTTP em uma troca de frames de codificação binária, que são então mapeados para mensagens que pertencem a um fluxo específico, e todas são multiplexadas dentro de uma única conexão TCP. Essa é a base que possibilita todos os outros recursos e otimizações de desempenho fornecidos pelo protocolo HTTP/2.

Multiplexação de solicitação e resposta

Com o HTTP/1.x, se o cliente quiser fazer várias solicitações paralelas para melhorar o desempenho, será necessário usar várias conexões TCP (consulte Como usar várias conexões TCP). Esse comportamento é uma consequência direta do modelo de entrega do HTTP/1.x, que garante que somente uma resposta seja entregue por vez (enfileiramento de respostas) por conexão. Pior ainda, isso também resulta no bloqueio "head-of-line" e no uso ineficiente da conexão TCP subjacente.

A nova camada de frame binário no HTTP/2 remove essas limitações e possibilita a multiplexação total de solicitações e respostas, permitindo que o cliente e o servidor dividam uma mensagem HTTP em frames independentes, intercalem-os e remontem-os na outra extremidade.

Multiplexação de solicitação e resposta HTTP/2 em uma conexão compartilhada

O snapshot captura vários streams em andamento na mesma conexão. O cliente está transmitindo um frame DATA (fluxo 5) ao servidor, enquanto o servidor está transmitindo uma sequência intercalada de frames ao cliente para os fluxos 1 e 3. Como resultado, há três fluxos paralelos em andamento.

A capacidade de dividir uma mensagem HTTP em frames independentes, intercalá-los e remontá-las na outra extremidade é a melhoria mais importante do HTTP/2. Na verdade, ele introduz um efeito dominó de vários benefícios de desempenho em toda a pilha de todas as tecnologias da Web, o que nos permite:

  • Intercalar várias solicitações em paralelo sem bloquear nenhuma delas.
  • Intercalar várias respostas em paralelo sem bloquear nenhuma delas.
  • Usar uma única conexão para entregar várias solicitações e respostas em paralelo.
  • Remova soluções alternativas HTTP/1.x desnecessárias. Consulte Como otimizar para HTTP/1.x, como arquivos concatenados, sprites de imagens e fragmentação de domínio.
  • Ofereça tempos de carregamento de página menores, eliminando a latência desnecessária e melhorando a utilização da capacidade de rede disponível.
  • E muito mais...

A nova camada de frame binário do HTTP/2 resolve o problema de bloqueio no início da linha do HTTP/1.x e elimina a necessidade de várias conexões para permitir o processamento paralelo e a entrega de solicitações e respostas. Como resultado, isso torna nossos aplicativos mais rápidos, simples e baratos de implantar.

Priorização de stream

Quando uma mensagem HTTP pode ser dividida em muitos frames individuais e permitimos que frames de vários streams sejam multiplexados, a ordem em que os frames são intercalados e entregues, tanto pelo cliente quanto pelo servidor, se torna uma consideração de desempenho crítica. Para facilitar isso, o padrão HTTP/2 permite que cada stream tenha um peso e uma dependência associados:

  • A cada stream pode ser atribuído um peso em número inteiro entre 1 e 256.
  • Cada stream pode ter uma dependência explícita em outro stream.

A combinação de dependências e pesos de stream permite que o cliente crie e informe uma "árvore de priorização" que expresse como ele prefere receber respostas. O servidor pode usar essas informações para priorizar o processamento de stream controlando a alocação de CPU, memória e outros recursos e, quando os dados de resposta estiverem disponíveis, alocação de largura de banda para garantir a entrega ideal de respostas de alta prioridade para o cliente.

Dependências e pesos de stream HTTP/2

Uma dependência de stream dentro de HTTP/2 é declarada referenciando o identificador exclusivo de outro stream como o pai. Se o identificador for omitido, o stream será considerado dependente do "stream raiz". Declarar uma dependência de stream indica que, se possível, o stream pai precisa receber os recursos alocados antes das dependências. Em outras palavras, "Processe e entregue a resposta D antes da C".

Os streams com o mesmo pai (ou seja, streams irmãos) precisam receber a alocação de recursos de acordo com o peso de cada um. Por exemplo, se o stream A tiver um peso 12 e o irmão B tiver um peso 4, para determinar a proporção dos recursos que cada um desses fluxos receberá:

  1. Some todos os pesos: 4 + 12 = 16
  2. Divida o peso de cada stream pelo peso total: A = 12/16, B = 4/16

Assim, o fluxo A deve receber 3/4 e o fluxo B, 1/4 dos recursos disponíveis. O fluxo B deve receber 1/3 dos recursos alocados ao fluxo A. Veja mais alguns exemplos práticos na imagem acima. Da esquerda para a direita:

  1. Nem os fluxos A nem B especificam uma dependência pai e são considerados dependentes do "stream raiz implícito". A tem um peso 12 e B tem um peso 4. Assim, com base em pesos proporcionais: o stream B precisa receber um terço dos recursos alocados para o stream A.
  2. O fluxo D é dependente do fluxo raiz, e C é dependente de D. Portanto, D precisa receber a alocação total de recursos antes de C. Os pesos não têm importância, porque a dependência de C comunica uma preferência mais forte.
  3. O stream D precisa receber a alocação total de recursos antes de C, C precisa receber a alocação total de recursos antes de A e B, e o stream B deve receber um terço dos recursos alocados para o stream A.
  4. O fluxo D deve receber a alocação total de recursos antes de E e C; E e C precisam receber alocação igual antes de A e B, e A e B precisam receber alocação proporcional com base nos pesos de cada um.

Conforme ilustrado pelos exemplos acima, a combinação de dependências e pesos de stream fornece uma linguagem expressiva para a priorização de recursos, que é um recurso essencial para melhorar o desempenho de navegação, em que há muitos tipos de recurso com dependências e pesos diferentes. Melhor ainda, o protocolo HTTP/2 também permite que o cliente atualize essas preferências a qualquer momento, o que permite mais otimizações no navegador. Em outras palavras, podemos mudar as dependências e realocar pesos em resposta à interação do usuário e outros sinais.

Uma conexão por origem

Com o novo mecanismo de enquadramento binário, o HTTP/2 não precisa mais de várias conexões TCP para multiplexar streams em paralelo. Cada stream é dividido em vários frames, que podem ser intercalados e priorizados. Como resultado, todas as conexões HTTP/2 são persistentes, e é necessária apenas uma conexão por origem, o que oferece vários benefícios de desempenho.

Tanto para o SPDY quanto para o HTTP/2, o recurso assassino é multiplexação arbitrária em um único canal de congestionamento bem controlado. É impressionante a importância disso e como funciona bem. Uma ótima métrica a respeito disso é a fração de conexões criadas que transportam apenas uma transação HTTP e, assim, fazem essa transação acarretar toda a sobrecarga. Para o HTTP/1, 74% das nossas conexões ativas transportam apenas uma transação. As conexões persistentes não são tão úteis quanto todos queremos. Mas, no HTTP/2, esse número despenca para 25%. Essa é uma grande vitória para a redução de sobrecarga. (HTTP/2 está disponível no Firefox, Patrick McManus)

A maioria das transferências HTTP é curta e em bursts, enquanto o TCP é otimizado para transferências de dados em massa de longa duração. Ao reutilizar a mesma conexão, o HTTP/2 pode fazer um uso mais eficiente de cada conexão TCP e também reduzir significativamente a sobrecarga geral do protocolo. Além disso, o uso de menos conexões reduz o espaço ocupado de memória e processamento ao longo de todo o caminho de conexão (em outras palavras, cliente, intermediários e servidores de origem). Isso reduz os custos operacionais gerais e melhora a utilização e a capacidade da rede. Como resultado, a mudança para HTTP/2 não apenas reduz a latência da rede, mas também ajuda a melhorar a capacidade e reduzir os custos operacionais.

Controle de fluxo

O controle de fluxo é um mecanismo para evitar que o remetente sobrecarregue o receptor com dados que ele pode não querer ou conseguir processar: o receptor pode estar ocupado, sob carga pesada ou só pode estar disposto a alocar uma quantidade fixa de recursos para um fluxo específico. Por exemplo, o cliente pode ter solicitado um grande stream de vídeo com alta prioridade, mas o usuário pausou o vídeo e agora quer pausar ou limitar a entrega do servidor para evitar a busca e o armazenamento em buffer de dados desnecessários. Como alternativa, um servidor proxy pode ter conexões downstream rápidas e upstream lentas e quer regular a rapidez com que o downstream fornece dados para corresponder à velocidade do upstream para controlar o uso de recursos e assim por diante.

Os requisitos acima lembram o controle de fluxo do TCP? Eles deveriam, já que o problema é efetivamente idêntico (consulte Controle de fluxo). No entanto, como os streams HTTP/2 são multiplexados em uma única conexão TCP, o controle de fluxo TCP não é granular o suficiente e não fornece as APIs necessárias no nível do aplicativo para regular o envio de fluxos individuais. Para resolver isso, o HTTP/2 fornece um conjunto de elementos básicos simples que permitem ao cliente e ao servidor implementar o próprio controle de fluxo no nível do fluxo e da conexão:

  • O controle de fluxo é direcional. Cada receptor pode definir o tamanho de janela que quiser para cada stream e toda a conexão.
  • O controle de fluxo é baseado em crédito. Cada receptor anuncia a conexão inicial e a janela de controle de fluxo (em bytes), que é reduzida sempre que o remetente emite um frame DATA e incrementada por um frame WINDOW_UPDATE enviado pelo receptor.
  • O controle de fluxo não pode ser desativado. Quando a conexão HTTP/2 é estabelecida, o cliente e o servidor trocam frames SETTINGS, que definem os tamanhos da janela de controle de fluxo nas duas direções. O valor padrão da janela de controle de fluxo é definido como 65.535 bytes, mas o destinatário pode definir um tamanho máximo de janela grande (2^31-1 bytes) e mantê-lo enviando um frame WINDOW_UPDATE sempre que algum dados for recebido.
  • O controle de fluxo funciona de salto em salto, e não de ponta a ponta. Ou seja, um intermediário pode usá-lo para controlar o uso de recursos e implementar mecanismos de alocação de recursos com base nos próprios critérios e heurística.

O HTTP/2 não especifica nenhum algoritmo específico para implementar o controle de fluxo. Em vez disso, ele fornece os elementos básicos simples e adia a implementação ao cliente e ao servidor, que podem usá-la para implementar estratégias personalizadas a fim de regular o uso e a alocação de recursos, além de implementar novos recursos de entrega que podem ajudar a melhorar o desempenho real e percebido (consulte Velocidade, desempenho e percepção humana) dos nossos aplicativos da Web.

Por exemplo, o controle de fluxo na camada do aplicativo permite que o navegador busque apenas uma parte de um recurso específico, coloque a busca em espera reduzindo a janela de controle de fluxo do stream a zero e a retome mais tarde. Em outras palavras, ele permite que o navegador busque uma visualização ou a primeira verificação de uma imagem, mostre-a e permita que outras buscas de alta prioridade prossigam e retome a busca quando mais recursos críticos terminarem de carregar.

Push de servidor

Outro recurso novo e eficiente do HTTP/2 é a capacidade do servidor de enviar várias respostas a uma única solicitação do cliente. Ou seja, além da resposta à solicitação original, o servidor pode enviar outros recursos para o cliente (Figura 12-5), sem que ele precise solicitá-los explicitamente.

O servidor inicia novos fluxos (promessas) para enviar recursos

Por que precisaríamos desse mecanismo em um navegador? Um aplicativo da Web típico consiste em dezenas de recursos, todos descobertos pelo cliente por meio da análise do documento fornecido pelo servidor. Como resultado, por que não eliminar a latência extra e deixar o servidor enviar os recursos associados com antecedência? O servidor já sabe quais recursos o cliente exigirá. Esse é o envio por push do servidor.

Na verdade, se você já in-lineu um CSS, JavaScript ou qualquer outro recurso usando um URI de dados (consulte Ininline de recursos), você já tem experiência prática com push do servidor. Ao inserir manualmente o recurso no documento, estamos, na verdade, enviando esse recurso para o cliente, sem esperar que o cliente o solicite. Com o HTTP/2, é possível alcançar os mesmos resultados, mas com outros benefícios de desempenho. Os recursos de push podem ser:

  • Armazenado em cache pelo cliente
  • Reutilizados em diferentes páginas
  • multiplexado com outros recursos
  • Priorizados pelo servidor
  • Recusado pelo cliente

INTRODUÇÃO AO PUSH_PROMISE

Todos os streams de push do servidor são iniciados por frames PUSH_PROMISE, que sinalizam a intenção do servidor de enviar os recursos descritos ao cliente e precisam ser entregues antes dos dados de resposta que solicitam os recursos enviados. Essa ordem de entrega é essencial: o cliente precisa saber quais recursos o servidor pretende enviar por push para evitar a criação de solicitações duplicadas para esses recursos. A estratégia mais simples para atender a esse requisito é enviar todos os frames PUSH_PROMISE, que contêm apenas os cabeçalhos HTTP do recurso prometido, antes da resposta do pai (em outras palavras, frames DATA).

Depois que o cliente receber um frame PUSH_PROMISE, ele terá a opção de recusar o stream (por um frame RST_STREAM), se quiser. Isso pode ocorrer, por exemplo, porque o recurso já está armazenado em cache. Essa é uma melhoria importante em relação ao HTTP/1.x. Por outro lado, o uso de in-line de recursos, que é uma "otimização" conhecida para HTTP/1.x, é equivalente a um "envio forçado": o cliente não pode desativar, cancelar ou processar o recurso embutido individualmente.

Com o HTTP/2, o cliente permanece no controle de como o push do servidor é usado. O cliente pode limitar o número de streams enviados simultaneamente, ajustar a janela de controle de fluxo inicial para controlar a quantidade de dados que é enviada quando o stream é aberto pela primeira vez ou desativar completamente o push do servidor. Essas preferências são comunicadas pelos frames SETTINGS no início da conexão HTTP/2 e podem ser atualizadas a qualquer momento.

Cada recurso enviado é um fluxo que, ao contrário de um recurso embutido, permite que ele seja multiplexado, priorizado e processado individualmente pelo cliente. A única restrição de segurança, conforme aplicada pelo navegador, é que os recursos enviados precisam obedecer à política de mesma origem: o servidor precisa ser autoritativo para o conteúdo fornecido.

Compactação de cabeçalho

Cada transferência HTTP carrega um conjunto de cabeçalhos que descrevem o recurso transferido e as propriedades dele. No HTTP/1.x, esses metadados são sempre enviados como texto simples e adicionam de 500 a 800 bytes de sobrecarga por transferência, e às vezes kilobytes a mais se cookies HTTP estiverem sendo usados. Consulte Como medir e controlar a sobrecarga de protocolo. Para reduzir essa sobrecarga e melhorar o desempenho, o HTTP/2 compacta os metadados de cabeçalho de solicitação e resposta usando o formato de compactação HPACK, que usa duas técnicas simples, mas poderosas:

  1. Ele permite que os campos de cabeçalho transmitidos sejam codificados por meio de um código Huffman estático, que reduz o tamanho da transferência individual.
  2. Isso exige que o cliente e o servidor mantenham e atualizem uma lista indexada de campos de cabeçalho vistos anteriormente. Em outras palavras, ela estabelece um contexto de compactação compartilhado, que é usada como uma referência para codificar com eficiência os valores transmitidos anteriormente.

A codificação Huffman permite que os valores individuais sejam compactados quando transferidos, e a lista indexada de valores transferidos anteriormente nos permite codificar valores duplicados transferindo valores de índice que podem ser usados para pesquisar e reconstruir as chaves e os valores completos do cabeçalho.

HPACK: compactação de cabeçalho para HTTP/2

Como mais uma otimização, o contexto de compactação HPACK consiste em uma tabela estática e dinâmica: a tabela estática é definida na especificação e fornece uma lista de campos de cabeçalho HTTP comuns que todas as conexões provavelmente usarão (por exemplo, nomes de cabeçalho válidos). A tabela dinâmica está inicialmente vazia e é atualizada com base nos valores trocados em uma determinada conexão. Como resultado, o tamanho de cada solicitação é reduzido com o uso da codificação Huffman estática para valores que não foram vistos antes e a substituição de índices por valores que já estão presentes nas tabelas estáticas ou dinâmicas de cada lado.

Segurança e desempenho do HPACK

As primeiras versões do HTTP/2 e do SPDY usavam zlib, com um dicionário personalizado, para compactar todos os cabeçalhos HTTP. Isso proporcionou uma redução de 85% a 88% no tamanho dos dados de cabeçalho transferidos e uma melhoria significativa na latência do tempo de carregamento da página:

No link DSL de largura de banda menor, em que o link de upload tem apenas 375 Kbps, a compactação do cabeçalho da solicitação, em particular, levou a melhorias significativas no tempo de carregamento da página para determinados sites (ou seja, aqueles que emitiram um grande número de solicitações de recursos). Encontramos uma redução de 45 a 1.142 ms no tempo de carregamento simplesmente devido à compactação de cabeçalho. (Artigo sobre SPDY, chromium.org)

No entanto, no terceiro semestre de 2012, um ataque de segurança "CRIMINOSO" foi publicado contra os algoritmos de compactação TLS e SPDY, o que poderia resultar no sequestro de sessões. Como resultado, o algoritmo de compactação zlib foi substituído pelo HPACK, que foi projetado especificamente para resolver os problemas de segurança descobertos, ser eficiente e simples de implementar corretamente e, claro, permitir uma boa compactação dos metadados de cabeçalho HTTP.

Para ver todos os detalhes sobre o algoritmo de compactação HPACK, consulte IETF HPACK: compactação de cabeçalho para HTTP/2 (em inglês).

Sugestões de leitura