O que uma requisição crua tem de fato
Uma requisição HTTP/1.1 é uma linha de requisição (request line), um bloco de headers (cabeçalhos), uma linha em branco e um body (o corpo da mensagem) opcional. A sintaxe está na 9112 e o significado das partes na RFC 9110, e o formato quase não mudou desde os anos 1990:
POST /v1/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"name":"Alice"}
Existem quatro formas de request target (o alvo da requisição), e vale saber diferenciá-las, porque são elas que decidem o que um tradutor consegue fazer com a mensagem. A origin-form (a forma de origem) é a mais comum: só o caminho. A absolute-form traz a URL inteira e é o que um cliente envia para um proxy. A authority-form é só host e porta, usada com CONNECT para indicar o destino de um túnel. A asterisk-form é um * sozinho, usado com OPTIONS para perguntar algo sobre o servidor, e não sobre um recurso.
A URL não está na mensagem
Esta é a primeira armadilha. GET /users HTTP/1.1 não diz para onde mandar a requisição; a conexão já sabia. Para reconstruir algo que rode, é preciso juntar o alvo com o header Host, e é exatamente por isso que esse header passou a ser obrigatório no HTTP/1.1: foi ele que permitiu que muitos sites dividissem um mesmo endereço, e é ele que torna possível reconstruir uma requisição capturada.
Se o Host estiver faltando numa requisição em origin-form, não há como recuperar a URL só a partir da mensagem. Nesse caso, a ferramenta deve avisar, em vez de inventar um host.
Alguns headers não podem ser copiados
A segunda armadilha é mais sutil e gera bugs que parecem problema do servidor.
O Host é preenchido por qualquer cliente a partir da URL que você passa. Se você copiar esse header como um header explícito, a requisição sai com dois, e qual deles vale depende do cliente.
O Content-Length é calculado pelo cliente a partir do body que ele está prestes a enviar. Copiar o valor capturado fixa um número que pode não ser mais verdadeiro, e um tamanho que não bate com o body não é um erro cosmético. É o ingrediente do request smuggling (o contrabando de requisições), em que o front-end e o back-end discordam sobre onde uma mensagem termina e a próxima começa.
O Connection descreve o comportamento daquele hop específico (o trecho entre um equipamento e o seguinte), e não o significado da requisição.
Ou seja, uma tradução fiel não é uma tradução literal. Reproduzir cada header ao pé da letra gera algo menos parecido com a requisição original, e não mais.
O que você colou costuma ser uma credencial
A terceira armadilha não tem nada a ver com sintaxe. Requisições capturadas de uma sessão ativa trazem headers Authorization, headers Cookie e chaves de API em query strings, e em geral elas são capturadas justamente porque algo estava dando errado, que é o momento em que as pessoas colam tudo em chamados, chats e issue trackers.
Um bearer token num chamado é uma credencial válida até expirar, legível por qualquer pessoa com acesso àquele chamado e pesquisável por muito tempo depois. Trate qualquer requisição colada como um segredo ativo: mascare as credenciais antes de compartilhar e prefira ferramentas que fazem o parsing localmente, em vez de enviar a mensagem para um serviço formatar.
Body em chunks, e a hora de parar
Se a mensagem traz Transfer-Encoding: chunked, o body que você vê não é o body que foi enviado: é uma sequência de chunks (pedaços), cada um precedido do seu tamanho, terminada por um chunk de tamanho zero. Decodificar isso é um trabalho diferente de traduzir uma requisição, e uma ferramenta que faz isso pela metade (decodificando algumas codificações e outras não) é pior do que uma que diz claramente que não fez.
A rotina na prática
- Identifique a forma do alvo. Se for origin-form, encontre o
Host. - Descarte os headers que o cliente vai preencher sozinho.
- Veja se o body é chunked ou tem o tamanho declarado, e se o tamanho declarado é verdadeiro.
- Mascare as credenciais antes que a requisição saia da sua máquina, em qualquer formato.
- Traduza e depois rode o resultado contra um alvo de teste, e não contra produção, porque uma requisição repetida faz de novo o que a original fez, inclusive qualquer gravação de dados.