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 nos nossos aplicativos e resolver essas questões na própria camada de transporte. Melhor ainda, ele abre um monte de oportunidades totalmente novas de otimizar nossos aplicativos e melhorar o desempenho.
Os principais objetivos do HTTP/2 são reduzir a latência ao ativar a multiplexação completa de solicitações e respostas, minimizar a sobrecarga do protocolo com a compactação eficiente de campos de cabeçalho HTTP e adicionar suporte à priorização de solicitações e ao 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 upgrade, 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 a semântica HTTP do aplicativo de maneira alguma. 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 cliente e servidor, que gerenciam todo o processo, e oculta toda a complexidade dos aplicativos dentro da 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 é incrementada 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 verá nenhuma diferença: todo o enquadramento novo e de baixo nível é realizado pelo cliente e pelo servidor em seu nome. As únicas diferenças observáveis serão a melhoria do desempenho 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 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 ao lidar com algumas das limitações de desempenho conhecidas do HTTP/1.1. Especificamente, as metas delineadas do projeto foram definidas da seguinte maneira:
- Reduzir 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.
- Desenvolva esse novo protocolo em parceria com a comunidade de código aberto.
- Coletar dados de desempenho reais para (in)validar o protocolo experimental.
Pouco tempo 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, observamos uma melhoria significativa no desempenho: as páginas são carregadas até 55% mais rapidamente. (Blog do Chromium)
Em 2012, o novo protocolo experimental era compatível com Chrome, Firefox e Opera, e 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 devido à crescente adoção do setor.
Observando essa tendência, o grupo de trabalho de HTTP (HTTP-WG) iniciou uma nova ação para usar as lições aprendidas com o SPDY, criá-las e melhorá-las e fornecer um padrão "HTTP/2" oficial. Um novo termo de abertura foi redigido, uma chamada aberta para propostas de 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 forma 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 de HTTP/2 (baseado 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 do HTTP/2 e do HPACK
- Maio de 2015: a publicação do RFC 7540 (HTTP/2) e do RFC 7541 (HPACK)
No início de 2015, o IESG analisou e aprovou o novo padrão HTTP/2 para publicação. Pouco depois disso, a equipe do Google Chrome anunciou a programação para descontinuar a extensão SPDY e NPN para TLS:
As principais alterações do HTTP/2 em relação ao HTTP/1.1 são voltadas para o 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 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 ao SPDY no início de 2016 e também remover o suporte à extensão TLS chamada NPN em favor do ALPN no Chrome. Recomendamos que os desenvolvedores de servidores migrem 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 a ampla adoção, considerando 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 servidor, navegador e site ganhassem experiência real com o novo protocolo à medida que ele ia sendo desenvolvido. Como resultado, o padrão HTTP/2 é um dos melhores e mais testados desde o início. Quando o HTTP/2 foi aprovado pelo IESG, havia dezenas de implementações de clientes e servidores totalmente testadas e prontas para produção. Na verdade, apenas algumas semanas após a aprovação do protocolo final, muitos usuários já estavam aproveitando os benefícios, já que vários navegadores conhecidos (e muitos sites) implantaram compatibilidade total com HTTP/2.
Objetivos técnicos e de design
Versões anteriores do protocolo HTTP foram projetadas intencionalmente para a simplicidade de implementação: HTTP/0.9 era um protocolo de uma linha para inicializar a World Wide Web; o HTTP/1.0 documentou as extensões populares do HTTP/0.9 em um padrão informativo; o HTTP/1.1 introduziu um padrão IETF oficial. Consulte Breve história do HTTP. Assim, o HTTP/0.9-1.x ofereceu exatamente o que se propôs a fazer: o HTTP é um dos protocolos de aplicativo mais adotados da Internet.
Infelizmente, a simplicidade de implementação também prejudica o desempenho do aplicativo: os clientes HTTP/1.x precisam usar várias conexões para alcançar simultaneidade e reduzir a latência; o HTTP/1.x não compacta 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 eram fatais, mas, à medida que os aplicativos da Web continuavam a crescer em escopo, complexidade e importância na nossa vida cotidiana, elas impunham um peso crescente sobre os desenvolvedores e usuários da Web, que é exatamente a lacuna que o HTTP/2 foi projetado para resolver:
O HTTP/2 permite um uso mais eficiente dos recursos de rede e uma percepção reduzida de latência, introduzindo a compactação do 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 priorizar solicitações, permitindo que as 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 vida mais longa, 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 um 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. 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 breve tour pela camada de frame 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.
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 superior exposta aos nossos aplicativos: a semântica HTTP, como verbos, métodos e cabeçalhos, não é afetada, mas a forma 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, o cliente e o servidor precisam usar o novo mecanismo de codificação binária para entenderem um ao outro: um cliente HTTP/1.x não entenderá um servidor somente HTTP/2 e vice-versa. Felizmente, nossos aplicativos não estão cientes de todas essas mudanças, já que o cliente e o servidor realizam todo o trabalho de enquadramento necessário para nós.
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 conter uma ou mais mensagens.
- Mensagem: uma sequência completa de frames que mapeia uma mensagem de solicitação ou resposta lógica.
- Frame: a menor unidade de comunicação em HTTP/2. Cada uma contém 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 dado (por exemplo, Cabeçalhos HTTP, payload da mensagem e assim por diante. É possível intercalar frames de diferentes streams e, em seguida, reagrupá-los usando o 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 codificados em binários, que são então mapeados para mensagens que pertencem a um stream específico, 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 várias 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 somente uma resposta possa ser entregue por vez (enfileiramento de respostas) por conexão. E, para piorar, 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 completa de solicitações e respostas, permitindo que o cliente e o servidor dividam uma mensagem HTTP em frames independentes, os intercalem e remontem na outra extremidade.
O snapshot captura vários streams em andamento na mesma conexão. O
cliente está transmitindo um frame DATA
(stream 5) para o servidor, enquanto o servidor
está transmitindo uma sequência intercalada de frames ao cliente para os streams 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á-los na outra extremidade é a melhoria mais importante do HTTP/2. Na verdade, ela apresenta um efeito dominó de vários benefícios de desempenho em toda a pilha de todas as tecnologias da Web, o que permite:
- Intercale várias solicitações em paralelo sem bloquear nenhuma delas.
- Intercale 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 Otimização para HTTP/1.x, como arquivos concatenados, image sprites e fragmentação de domínio.
- fornecer tempos de carregamento de página mais curtos ao eliminar a latência desnecessária e melhorar a utilização da capacidade de rede disponível;
- E muito mais...
A nova camada de frame 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 a entrega de solicitações e respostas. O resultado é que nossos aplicativos ficam mais rápidos, simples e baratos de implantar.
Priorização de stream
Quando uma mensagem HTTP pode ser dividida em vários frames individuais e permitimos que frames de vários fluxos sejam multiplexados, a ordem em que os frames são intercalados e entregues pelo cliente e pelo servidor, passando a ser 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 fluxo pode receber um peso em número inteiro entre 1 e 256.
- Cada stream pode ter uma dependência explícita de 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 expressa 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, alocar a largura de banda para garantir a entrega ideal de respostas de alta prioridade para o cliente.
Uma dependência de stream dentro do HTTP/2 é declarada referenciando o identificador exclusivo de outro stream como 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 que compartilham o mesmo pai (ou seja, streams irmãos) precisam receber recursos proporcionalmente ao peso deles. Por exemplo, se o fluxo 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 precisa receber:
- Some todos os pesos:
4 + 12 = 16
- Divida o peso de cada fluxo pelo peso total:
A = 12/16, B = 4/16
Assim, o fluxo A receberá 3/4 e o B, 1/4 dos recursos disponíveis. O fluxo B receberá 1/3 dos recursos alocados ao fluxo A. Vejamos mais alguns exemplos práticos na imagem acima. Da esquerda para a direita:
- Nem os streams 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 fluxo B receberá um terço dos recursos alocados para o fluxo A.
- O fluxo D é dependente do fluxo raiz; C é dependente de D. Assim, D deve 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.
- 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 precisa receber um terço dos recursos alocados para o stream A.
- O fluxo D precisa 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.
Como os exemplos acima ilustram, 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 da navegação, quando há muitos tipos de recurso 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 outras otimizações no navegador. Em outras palavras, podemos mudar 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 streams multiplexados em paralelo. Cada fluxo é dividido em vários 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.
Para SPDY e HTTP/2, o recurso "killer" é a multiplexação arbitrária em um único canal de congestionamento bem controlado. É impressionante como isso é importante 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 com que essa transação tenha 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 nós queremos. Mas, em 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 são curtas 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 a área de ocupação de memória e de processamento ao longo de todo o caminho da 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 ser capaz de processar: o receptor pode estar ocupado, sob carga pesada ou que só pode estar disposto a alocar uma quantidade fixa de recursos para um fluxo específico. Por exemplo, o cliente pode ter solicitado um stream de vídeo grande 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 de upstream lenta 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 lembram o controle de fluxo do TCP? Elas 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 streams individuais. Para resolver isso, o HTTP/2 oferece 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 do 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 nas duas 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 de janela grande (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 nos próprios critérios e heurísticas.
O HTTP/2 não especifica nenhum algoritmo específico para implementar o controle de fluxo. Em vez disso, ele fornece 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, 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 a 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, mostre-a e permita que outras buscas de alta prioridade continuem e retome a busca quando os recursos mais importantes forem carregados.
Push de servidor
Outro recurso novo e avançado 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 ele precise 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 vai exigir. Esse é o push do servidor.
Na verdade, se já tiver in-line um CSS, JavaScript ou qualquer outro recurso por meio de um URI de dados (consulte Ininline de recursos), você já terá 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 ele o solicite. Com o HTTP/2, podemos 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 recebe um frame PUSH_PROMISE
, ele tem a opção de recusar o
stream (com 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 em linha, que é uma "otimização"
conhecida para HTTP/1.x, é equivalente a um "envio forçado": o cliente não pode
recusar, cancelar o recurso nem processar o recurso embutido individualmente.
Com o HTTP/2, o cliente mantém o controle total do uso do push do servidor. O cliente pode limitar o número de streams enviados por push simultaneamente, ajustar a janela de controle de fluxo inicial para controlar a quantidade de dados enviados por push quando o stream é aberto pela primeira vez ou desativar completamente o envio 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 stream que, ao contrário de um recurso inline, 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 por push precisam obedecer à política de mesma origem: o servidor precisa ter autoridade 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. Em HTTP/1.x, esses metadados sempre são enviados como texto simples e adicionam de 500 a 800 bytes de sobrecarga por transferência e, às vezes, kilobytes a mais se os cookies HTTP estiverem sendo usados. Para reduzir essa sobrecarga e melhorar o desempenho, o HTTP/2 compacta os metadados de cabeçalhos de solicitação e resposta usando o formato de compactação HPACK, que usa duas técnicas simples, mas avançadas:
- Ele permite que os campos do cabeçalho transmitidos sejam codificados por um código Huffman estático, o que reduz o tamanho da transferência individual.
- Isso exige que o cliente e o servidor mantenham e atualizem uma lista indexada de campos de cabeçalho vistos anteriormente (em outras palavras, estabeleça um contexto de compactação compartilhado), que é então 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 com eficiência as chaves e os valores completos do cabeçalho.
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 de 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 menor largura de banda, 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 da página simplesmente devido à compactação do cabeçalho. (artigo SPDY, chromium.org)
No entanto, em meados de 2012, um ataque de segurança "CRIME" foi publicado contra os algoritmos de compactação do TLS e do SPDY, o que poderia resultar no sequestro da 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 de cabeçalho HTTP.
Para mais detalhes sobre o algoritmo de compactação HPACK, consulte IETF HPACK: compactação de cabeçalho para HTTP/2 (em inglês).
Leia mais
- “HTTP/2”: o artigo completo de Ilya Grigorik
- Como configurar o HTTP/2 – Como configurar o HTTP/2 em diferentes back-ends, de Surma
- "O HTTP/2 chegou, vamos otimizar!" – Apresentação de Ilya Grigorik na Velocity 2015
- Regras gerais para envio de HTTP/2: uma análise de Tom Bergan, Simon Pelchat e Michael Buettner sobre quando e como usar push.