Um pedido falhou. Não há código de estado, porque nenhuma resposta chegou. E quando procura no seu registo de acesso pelo pedido que falhou, não há linha nenhuma para ele em todo o lado.

Essa combinação é a assinatura de uma falha abaixo de HTTP, e ERR_HTTP2_PROTOCOL_ERROR é a forma mais comum de o Chrome reportar uma. Vale a pena dez minutos porque quase tudo o que foi escrito sobre isto recomenda a mesma solução, e a solução é um diagnóstico que as pessoas confundem com uma cura.

O que o código realmente diz

A lista de erros de rede do Chromium, de onde estas cadeias são geradas, define-o em seis palavras:

Há um erro de protocolo HTTP/2.

Essa é a entrada inteira. O número é -337, e nada nela nomeia uma causa, uma direção ou uma parte. Procurar a cadeia dá-lhe mil páginas a adivinhar, porque a fonte em si também está a adivinhar.

Os seis que não são vagos, e por que é que isso importa

Aqui está a parte que ninguém menciona. O Chromium não tem um erro HTTP/2. Tem vários, e qualquer outro é específico:

  • ERR_HTTP2_SERVER_REFUSED_STREAM, -351, que a fonte descreve em detalhe: o servidor “recusou o pedido sem o processar” e disse-o, seja com um frame GOAWAY com código de erro NO_ERROR e um Last-Stream-ID abaixo deste pedido stream, seja um RST_STREAM com REFUSED_STREAM. O comentário acrescenta o detalhe operacional: “O cliente PODE fazer retry (numa ligação diferente).”
  • ERR_HTTP2_PING_FAILED, -352: “O servidor HTTP/2 não respondeu à mensagem PING.”
  • ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY, -360: “A segurança de transporte é inadequada para a versão HTTP/2.”
  • ERR_HTTP2_FLOW_CONTROL_ERROR, -361, ERR_HTTP2_FRAME_SIZE_ERROR, -362, e ERR_HTTP2_COMPRESSION_ERROR, -363.

Portanto quando obtém -337 em vez de um daqueles, aprendeu algo real: não foi um stream recusado, não foi um keepalive morto, não foi um problema de cifra, não foi controlo de fluxo, não foi um tamanho de frame, não foi compressão de cabeçalho. ERR_HTTP2_PROTOCOL_ERROR é o “nenhum dos anteriores” de HTTP/2, e os anteriores são seis coisas nomeadas que agora pode deixar de investigar.

Esse é um resultado menor do que uma causa, e é muito melhor do que nada, que é aquilo que a mensagem por si própria parece oferecer.

Por que é que os seus registos estão vazios

A razão pela qual não há linha no registo de acesso está na especificação, e vale a pena lê-la porque também explica por que é que um utilizador reporta várias coisas a ficarem avariadas ao mesmo tempo.

O RFC 9113 separa dois tipos de falha. “Um erro de stream é um erro relacionado com um stream específico que não afeta o processamento de outros streams.” Um erro de ligação é a coisa completamente diferente: “qualquer erro que impede o processamento adicional da camada de frame ou corrompe qualquer estado de ligação.”

HTTP/2 multiplexes. Uma ligação transporta muitos pedidos ao mesmo tempo, por isso um erro de ligação não faz falhar o pedido que o provocou. Faz falhar todos os pedidos em voo naquela ligação.

O que o relatório diz
Uma página carregou-se a meio, algumas imagens estão em falta e um envio de formulário não fez nada. Três bugs separados, apresentados em separado
O que realmente aconteceu
Um erro de ligação derrubou tudo em voo naquela ligação no mesmo instante. Uma causa, três sintomas, e nenhum código de estado em qualquer um deles

E porque a troca nunca se completou como HTTP, não há nada para o seu servidor registar. Sem método, sem caminho, sem estado. O utilizador viu algo falhar e os seus registos mostram uma lacuna onde deveria ter estado.

O que é seguro fazer retry, que o protocolo realmente responde

Esta é a pergunta que importa se um pagamento ou um envio de formulário estava em voo, e tem uma resposta real em vez de encolher os ombros.

O RFC 9113 diz que “um endpoint que encontra um erro de ligação DEVE primeiro enviar um frame GOAWAY (Secção 6.8) com o identificador de stream do último stream que recebeu com sucesso do seu par.”

Esse identificador é uma linha traçada através dos pedidos. Qualquer coisa numerada acima dela não foi recebida, o que significa que não foi processada, o que significa que fazer retry não pode duplicar nada. É precisamente por isso que a própria nota do Chromium sobre um stream recusado diz que o cliente pode fazer retry numa ligação diferente.

A pegadinha é que esta informação existe durante cerca de um segundo, dentro do navegador, e depois desaparece. Não está no seu registo de servidor, porque os frames que a transportaram estão abaixo da camada que o seu servidor regista.

Session Replay

Extensão gratuita do Chrome. Um clique na página que se está a portar mal captura a captura de ecrã, a consola e o registo de rede, e devolve-lhe um link para colar no ticket.

Instalar a extensão

“Desativar HTTP/2” é um diagnóstico, não uma correção

O conselho que encontra por toda a parte é desativar HTTP/2 e ver se o problema para. É bom conselho e é amplamente mal compreendido.

Se os erros pararem, aprendeu onde a culpa vive: algo na camada HTTP/2 entre aquele utilizador e você está a maltratar frames. Não aprendeu que HTTP/2 é pouco confiável, e não corrigiu nada. Desativou o multiplexing para cada visitante para contornar o bug de uma parte.

Essa parte é geralmente não o seu servidor. As causas comuns são as coisas no meio: um proxy reverso ou balanceador de carga com a sua própria implementação HTTP/2, uma borda CDN, equipamento de rede corporativo que termina e restabelece TLS, e software de segurança na própria máquina do utilizador fazendo a mesma coisa. Cada um deles fala HTTP/2 em seu nome, e cada um é um sítio onde um frame pode estar malformado sem ninguém em qualquer lado estar a fazer algo errado.

Por isso isto frequentemente afeta um escritório, um ISP ou um produto antivírus, e ninguém mais.

O que perguntar à pessoa que a acertou

Duas perguntas, e a segunda vale mais do que parece.

A cadeia exata da consola ou da página de erro. ERR_HTTP2_PROTOCOL_ERROR e ERR_HTTP2_SERVER_REFUSED_STREAM são problemas diferentes com respostas diferentes, e um relatório que diz “um erro de rede” deitou fora a única distinção que era gratuita.

Acontece numa rede diferente? Se para em dados móveis, a causa está entre eles e você em vez de estar no seu servidor, e estreitou-a ao equipamento da sua rede numa pergunta. Este é o mesmo teste que separa um proxy corporativo de um problema real de certificado na tela a ligação não é privada, e funciona aqui pela mesma razão.

O que tirar daqui

A mensagem é vaga porque a fonte é vaga, e saber que parando-o de ler significado nela que não está lá. O que realmente faz é tudo aquilo que não é, e seis alternativas nomeadas é um estreitamento real.

O resto segue-se de onde a falha se senta. Está abaixo de HTTP, por isso não há código de estado e frequentemente não há linha de registo. É um erro de ligação, por isso leva vários pedidos consigo e chega como vários relatórios de bugs. E a coisa que lhe diria se um retry é seguro é um frame que o navegador viu e o seu servidor nunca viu.

A lista de erros do Chromium, com a definição de uma linha de cada, é net_error_list.h, e o protocolo em si é RFC 9113.