O HTTP/2 tornará nossos aplicativos mais rápidos, simples e robustos (uma rara combinação) ao permitir desfazer muitas das soluções alternativas HTTP/1.1 feitas anteriormente em nossos aplicativos e abordar essas preocupações dentro da própria camada de transporte. Melhor ainda, ele abre um mundo novo de oportunidades para otimizar aplicativos e melhorar o desempenho.
Os principais objetivos do HTTP/2 são reduzir a latência ativando a multiplexação completa de solicitação e resposta, minimizar a sobrecarga do protocolo com a compactação eficiente dos campos de cabeçalho HTTP e adicionar suporte à priorização de solicitações e ao push do servidor. 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 upgrade. No entanto, esses são os recursos mais importantes que todo desenvolvedor Web precisa entender e aproveitar nos aplicativos.
O HTTP/2 não modifica de maneira alguma a semântica do 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 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 dentro da nova camada de enquadramento. Como resultado, todos os aplicativos existentes podem ser enviados sem modificação.
Por que não usar HTTP/1.2?
Para atingir as metas de desempenho definidas pelo grupo de trabalho HTTP, o HTTP/2 introduz uma nova camada de enquadramento binário que não é compatível com versões anteriores dos servidores e clientes HTTP/1.x anteriores. Por isso, o incremento da versão do protocolo principal para HTTP/2.
Dito isso, 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 enquadramento novo e 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.
Um breve histórico do SPDY e do HTTP/2
O SPDY era um protocolo experimental, desenvolvido no Google e anunciado em meados de 2009. O objetivo principal era tentar reduzir a latência de carregamento de páginas da Web abordando algumas das limitações de desempenho conhecidas do HTTP/1.1. Especificamente, as metas do projeto descritas foram definidas da seguinte maneira:
- Redução de 50% no tempo de carregamento da página (PLT, na sigla em inglês).
- Evite a necessidade de alterações de conteúdo pelos criadores de sites.
- Minimizar a complexidade da implantação e evitar mudanças na infraestrutura de rede.
- Desenvolva 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, ambos engenheiros de software do Google, compartilharam os primeiros resultados, documentação e código-fonte para a implementação experimental do novo protocolo SPDY:
Até agora, só testamos o SPDY em condições de laboratório. Os resultados iniciais são muito encorajadores: 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 carregam até 55% mais rápido. (Blog do Chromium)
Em 2012, o novo protocolo experimental era compatível com o Chrome, Firefox e Opera, e um número crescente de sites, grandes (por exemplo, Google, Twitter, Facebook) e pequenos, estavam implantando o SPDY na infraestrutura. Com isso, o SPDY estava a caminho de se tornar um padrão real devido à crescente adoção do setor.
Observando essa tendência, o HTTP Working Group (HTTP-WG) deu início a um novo esforço para aproveitar as lições aprendidas com o SPDY, criá-las e melhorá-las e entregar um padrão "HTTP/2" oficial. Um novo termo de abertura foi elaborado, uma chamada aberta para propostas de HTTP/2 foi feita e, após muita discussão com o grupo de trabalho, a especificação SPDY foi adotada como ponto de partida para o novo protocolo HTTP/2.
Nos anos seguintes, SPDY e HTTP/2 continuaram coevoluindo em paralelo, atuando como uma ramificação experimental usada 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. Por isso, o SPDY ofereceu um caminho para testar e avaliar cada proposta antes da inclusão no padrão HTTP/2. No fim das contas, esse processo durou três anos e resultou em mais de uma dúzia de rascunhos intermediários:
- Março de 2012: Chamada para propostas de HTTP/2
- Novembro de 2012: primeiro rascunho de 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: o IESG aprovou rascunhos do HTTP/2 e do HPACK
- Maio de 2015: o RFC 7540 (HTTP/2) e o RFC 7541 (HPACK) são publicados
No início de 2015, o IESG revisou e aprovou o novo padrão HTTP/2 para publicação. Pouco tempo depois, a equipe do Google Chrome anunciou a programação da suspensão do uso da extensão SPDY e NPN para TLS:
As principais mudanças do HTTP/2 em relação ao HTTP/1.1 se concentram no desempenho aprimorado. 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 anterior aberto, 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 se despedir. Planejamos remover o suporte ao SPDY no início de 2016 e também remover o suporte à extensão TLS chamada NPN para favorecer o ALPN no Chrome ao mesmo tempo. É altamente recomendável que os desenvolvedores de servidores migrem para HTTP/2 e ALPN.
Ficamos felizes em ter contribuído para o processo de padrões abertos que levou ao HTTP/2 e esperamos ver uma ampla adoção, considerando o amplo engajamento do setor em padronização e implementação. (Blog do Chromium) (em inglês)
A evolução conjunta do SPDY e do HTTP/2 permitiu que desenvolvedores de servidores, navegadores e sites ganhassem experiência real com o novo protocolo à medida que ele ia sendo desenvolvido. Por isso, 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, já que vários navegadores conhecidos (e muitos sites) implantaram a compatibilidade total com HTTP/2.
Design e objetivos técnicos
As 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 documentava as extensões populares do HTTP/0.9 em um padrão informativo; o HTTP/1.1 introduziu um padrão IETF oficial. Consulte a breve história do HTTP (em inglês). Assim, o HTTP/0.9-1.x oferece exatamente o que se propôs a fazer: o HTTP é um dos protocolos de aplicativo mais adotados da Internet.
Infelizmente, a simplicidade da implementação também teve um custo de 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 cabeçalhos de solicitação e resposta, gerando tráfego de rede desnecessário; o HTTP/1.x não permite priorização eficaz de recursos, resultando em mau uso da conexão TCP subjacente e assim por diante.
Essas limitações não foram fatais, mas, à medida que os aplicativos da Web continuaram a crescer em escopo, complexidade e importância em nossas vidas cotidianas, impuseram um fardo crescente sobre os desenvolvedores e usuários da Web, que é exatamente essa lacuna que o HTTP/2 foi projetado para atender:
O HTTP/2 permite um uso mais eficiente dos recursos de rede e uma percepção reduzida de latência, introduzindo a compactação de campo de cabeçalho e permitindo 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. Ela também permite a priorização de solicitações, permitindo que as mais importantes sejam concluídas mais rapidamente, melhorando ainda mais o desempenho.
O protocolo resultante é mais amigável para a rede, porque menos conexões TCP podem ser usadas em comparação com 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 permite o processamento mais eficiente de mensagens com o enquadramento binário. (Protocolo de transferência de hipertexto versão 2, rascunho 17)
É importante observar que o HTTP/2 está estendendo, não substituindo, os padrões HTTP anteriores. A semântica HTTP do aplicativo é a mesma, e nenhuma alteração foi feita na funcionalidade oferecida ou nos conceitos principais, como métodos HTTP, códigos de status, URIs e campos de cabeçalho. Essas alterações estavam explicitamente fora do escopo da iniciativa HTTP/2. Dito isso, embora a API de alto nível permaneça a mesma, é importante entender como as alterações de baixo nível abordam as limitações de desempenho dos protocolos anteriores. Vamos fazer um tour rápido pela camada de enquadramento binário e pelas características dela.
Camada de enquadramento binário
No centro de todas as melhorias de desempenho do HTTP/2 está a nova camada de enquadramento binário, que dita como as mensagens HTTP são encapsuladas e transferidas entre o cliente e o servidor.
A "camada" se refere a uma opção de design para introduzir um novo mecanismo de codificação otimizado entre a interface do soquete e a API HTTP mais recente exposta aos aplicativos: a semântica HTTP, como verbos, métodos e cabeçalhos, não é afetada, mas a maneira como é codificada durante o 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 no formato binário.
Como resultado, o cliente e o servidor precisam usar o novo mecanismo de codificação binária para se entenderem: um cliente HTTP/1.x não entende um servidor somente HTTP/2 e vice-versa. Felizmente, nossos aplicativos permanecem felizes com todas essas mudanças, já que 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 transportar uma ou mais mensagens.
- Mensagem: uma sequência completa de frames mapeados para uma mensagem lógica de solicitação ou resposta.
- Frame: a menor unidade de comunicação em HTTP/2, cada uma contendo um cabeçalho de frame, que identifica, no mínimo, 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, composta por um ou mais frames.
- O frame é a menor unidade de comunicação que transporta um tipo específico de dados, por exemplo, cabeçalhos HTTP, payload da mensagem e assim por diante. Os frames de diferentes streams podem ser intercalados e, em seguida, remontados pelo identificador de stream incorporado no cabeçalho de cada frame.
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 determinado fluxo, todos multiplexados em 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á preciso usar diversas conexões TCP (consulte Como usar várias conexões TCP). Esse comportamento é uma consequência direta do modelo de entrega HTTP/1.x, que garante que apenas uma resposta seja entregue por vez (enfileiramento de respostas) por conexão. E, pior ainda, isso resulta em bloqueio no início da fila e uso ineficiente da conexão TCP subjacente.
A nova camada de enquadramento binário no HTTP/2 remove essas limitações e permite a multiplexação completa de solicitação e resposta, permitindo que o cliente e o servidor dividam uma mensagem HTTP em frames independentes, intercalem-os e, em seguida, remontem-os na outra extremidade.
O snapshot captura vários streams em trânsito dentro da mesma conexão. O
cliente está transmitindo um frame DATA
(stream 5) ao servidor, enquanto o servidor
transmite uma sequência intercalada de frames ao cliente para os streams 1
e 3. Como resultado, há três fluxos paralelos em trânsito.
A capacidade de dividir uma mensagem HTTP em frames independentes, intercalá-los e reagrupá-los na outra extremidade é a melhoria mais importante do HTTP/2. Na verdade, ele apresenta um efeito de propagação de vários benefícios de desempenho em toda a pilha de todas as tecnologias da Web, permitindo:
- Intercalar várias solicitações em paralelo sem bloquear nenhuma delas.
- Intercale várias respostas em paralelo sem bloquear nenhuma.
- Use uma única conexão para entregar várias solicitações e respostas em paralelo.
- Remova soluções alternativas de HTTP/1.x desnecessárias. Consulte Otimização para HTTP/1.x (link em inglês), como arquivos concatenados, sprites de imagem e fragmentação de domínio.
- Ofereça tempos de carregamento de página mais curtos, eliminando a latência desnecessária e melhorando a utilização da capacidade de rede disponível.
- E muito mais...
A nova camada de enquadramento binário no HTTP/2 resolve o problema de bloqueio "head-of-line" encontrado no HTTP/1.x e elimina a necessidade de várias conexões para permitir o processamento paralelo e o envio de solicitações e respostas. Como resultado, isso torna nossos aplicativos mais rápidos, simples e baratos de implantar.
Priorização de stream
Assim que 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 pelo cliente e pelo servidor se torna uma consideração essencial de desempenho. Para facilitar isso, o padrão HTTP/2 permite que cada stream tenha um peso e uma dependência associados:
- Cada stream pode receber um peso inteiro entre 1 e 256.
- Cada stream pode ter uma dependência explícita em outro.
A combinação de dependências e pesos de stream permite que o cliente construa e comunique uma "árvore de priorização" que expressa como ele prefere receber respostas. Por sua vez, 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, a alocação de largura de banda para garantir a entrega ideal de respostas de alta prioridade para o cliente.
Uma dependência de stream em HTTP/2 é declarada fazendo referência ao 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 recursos alocados antes das dependências. Em outras palavras, "Processe e envie a resposta D antes da C".
Os streams que compartilham o mesmo pai (ou seja, streams irmãos) precisam receber recursos proporcionais ao peso deles. Por exemplo, se o stream A tiver um peso de 12 e o irmão B tiver um peso de 4, para determinar a proporção dos recursos que cada um desses streams receberá:
- Some todos os pesos:
4 + 12 = 16
- Divida o peso de cada stream pelo peso total:
A = 12/16, B = 4/16
Assim, o stream A deve receber três quartos, e o stream B deve receber 1/4 dos recursos disponíveis. O stream B deve receber 1/3 dos recursos alocados ao stream A. Vamos analisar mais alguns exemplos práticos na imagem acima. Da esquerda para a direita:
- Nem o stream A nem o B especificam uma dependência pai e são considerados dependentes do "stream raiz" implícito. A tem um peso de 12 e B tem um peso de 4. Assim, com base em pesos proporcionais: o stream B precisa receber um terço dos recursos alocados para o stream A.
- O stream D é dependente do stream raiz, e C é dependente de D. Assim, D receberá a alocação completa 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.
- O stream D precisa receber a alocação completa de recursos antes de C. O stream C deve receber a alocação total de recursos antes de A e B. O stream B precisa receber um terço dos recursos alocados ao stream A.
- O stream D precisa receber a alocação completa de recursos antes de E e C. E e C precisam receber alocação igual antes de A e B. A e B precisam receber alocação proporcional com base nos pesos.
Conforme ilustrado nos 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 temos muitos tipos de recursos com diferentes dependências e pesos. 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 dependências e realocar pesos em resposta à interação do usuário e outros indicadores.
Uma conexão por origem
Com o novo mecanismo de enquadramento binário em vigor, o HTTP/2 não precisa mais de várias conexões TCP para multiplexar streams em paralelo. Cada stream é dividido em muitos frames, que podem ser intercalados e priorizados. Como resultado, todas as conexões HTTP/2 são persistentes e apenas uma conexão por origem é necessária, o que oferece vários benefícios de desempenho.
Tanto para o SPDY quanto para o HTTP/2, o recurso destrutivo é a multiplexação arbitrária em um único canal bem controlado de congestionamento. É impressionante a importância disso e quão bem funciona. Uma ótima métrica em relação a isso que eu gosto é a fração de conexões criadas que transportam apenas uma única transação HTTP (e, portanto, fazem com que essa transação arca com toda a sobrecarga). Para HTTP/1, 74% das nossas conexões ativas transportam apenas uma transação. Conexões persistentes não são tão úteis quanto queremos. Mas no HTTP/2 esse número despeja 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 súbita, enquanto o TCP é otimizado para transferências de dados em massa e 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 a pegada de memória e processamento ao longo de todo o caminho de conexão (em outras palavras, clientes, intermediários e servidores de origem). Isso reduz o custo operacional geral e melhora a utilização e a capacidade da rede. Como resultado, a mudança para HTTP/2 não deve apenas reduzir a latência de rede, mas também ajudar 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 talvez não queira ou possa processar: o receptor pode estar ocupado, sob carga pesada ou 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, da mesma forma, 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 fazem você lembrar do controle de fluxo TCP? Elas deveriam ser encontradas, já que o problema é praticamente o mesmo. 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 no nível do aplicativo necessárias para regular a entrega de streams individuais. Para resolver isso, o HTTP/2 fornece um conjunto de elementos básicos simples que permitem que o cliente e o servidor implementem o próprio controle de fluxo no nível da conexão e do stream:
- O controle de fluxo é direcional. Cada receptor pode escolher definir o tamanho que quiser para a janela de cada stream e para toda a conexão.
- O controle de fluxo é baseado em crédito. Cada receptor divulga a conexão inicial
e a janela de controle de fluxo de stream (em bytes), que é reduzida sempre que o
remetente emite um frame
DATA
e incrementada por um frameWINDOW_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 em ambas as direções. O valor padrão da janela de controle de fluxo é definido como 65.535 bytes, mas o receptor pode definir um tamanho máximo grande para a janela (2^31-1
bytes) e mantê-lo enviando um frameWINDOW_UPDATE
sempre que algum dado for recebido. - O controle de fluxo funciona de salto em salto, 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 em critérios e heurística próprios.
O HTTP/2 não especifica nenhum algoritmo em particular para implementar o controle de fluxo. Em vez disso, ele fornece os elementos básicos simples e adia a implementação para o cliente e o servidor, que podem usá-la para implementar estratégias personalizadas a fim de regular o uso e a alocação de recursos, bem como 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 para zero e a retome mais tarde. Em outras palavras, ele permite que o navegador busque uma visualização ou primeira verificação de uma imagem, exiba-a e permita que outras buscas de alta prioridade continuem e retome a busca assim que mais recursos críticos forem carregados.
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 ao cliente (Figura 12-5), sem que o cliente tenha que solicitá-los explicitamente.
Por que precisaríamos desse mecanismo em um navegador? Um aplicativo da Web típico consiste em dezenas de recursos, todos descobertos pelo cliente examinando o 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 de quais recursos o cliente precisará. Esse é o push do servidor.
Na verdade, se você já in-lineu um CSS, JavaScript ou qualquer outro recurso por meio de um URI de dados (consulte In-line de recursos), já tem experiência prática com o push de servidor. Ao embutir o recurso manualmente no documento, estamos, na verdade, enviando esse recurso para o cliente, sem esperar que o cliente o solicite. Com o HTTP/2, podemos alcançar os mesmos resultados, mas com mais benefícios de desempenho. Os recursos de push podem ser:
- Armazenado em cache pelo cliente
- Reutilizados em diferentes páginas
- Multiplexado com outros recursos
- Priorizado 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 é fundamental: o cliente precisa saber quais recursos o servidor pretende enviar por push para evitar 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 (ou seja, frames DATA
).
Depois que o cliente recebe um frame PUSH_PROMISE
, ele tem 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 do recurso inline, que é uma "otimização" conhecida para HTTP/1.x, é equivalente a um "push forçado": o cliente não pode recusar, cancelá-lo ou processar o recurso embutido individualmente.
Com o HTTP/2, o cliente permanece no controle total 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 enviados quando o stream é aberto pela primeira vez ou desativar totalmente 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 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 do protocolo. Para reduzir essa sobrecarga e melhorar o desempenho, o HTTP/2 compacta os metadados do cabeçalho de solicitação e resposta usando o formato de compactação HPACK, que usa duas técnicas simples, mas poderosas:
- Ele permite que os campos de cabeçalho transmitidos sejam codificados por um código Huffman estático, o que reduz o tamanho da transferência individual.
- É necessário que o cliente e o servidor mantenham e atualizem uma lista indexada de campos de cabeçalho vistos anteriormente. Ou seja, estabelece um contexto de compactação compartilhado, que é usado como 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 permite codificar valores duplicados transferindo valores de índice que podem ser usados para procurar e reconstruir com eficiência as chaves e os valores de cabeçalho completos.
Como uma otimização adicional, 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 é inicialmente vazia e é atualizada com base nos valores trocados dentro de 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 com a substituição de índices por valores que já estão presentes nas tabelas estáticas ou dinâmicas em 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 resultou em 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 menor largura de banda, em que o link de upload tem apenas 375 Kbps, a compactação de cabeçalhos de solicitação em particular levou a melhorias significativas no tempo de carregamento da página para determinados sites (ou seja, aqueles que emitiam um grande número de solicitações de recursos). Encontramos uma redução de 45 a 1.142 ms no tempo de carregamento de página simplesmente devido à compactação do cabeçalho. (Artigo sobre SPDY, chromium.org)
No entanto, no verão de 2012, um ataque de segurança "CRIMINOSO" foi publicado contra algoritmos de compressão TLS e SPDY, o que poderia resultar em sequestro de sessão. 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 do 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 (link em inglês).
Leia mais
- "HTTP/2": artigo completo de Ilya Grigorik
- "Como configurar o HTTP/2": Como configurar o HTTP/2 em diferentes back-ends da Surma
- "O HTTP/2 chegou. Vamos otimizar!" – Apresentação de Ilya Grigorik, da Velocity 2015
- "Regras gerais para push no HTTP/2": uma análise de Tom Bergan, Simon Pelchat e Michael Buettner sobre quando e como usar push.