DevGuideTK
← Trilha Backend

HTTP e HTTPS

O protocolo de pedido e resposta que sustenta a web e quase toda API. Entenda o que trafega em cada requisição e o que o HTTPS acrescenta.

Os exemplos usam só a biblioteca padrão de cada linguagem, sem framework, para deixar o protocolo à vista. Em produção você vai usar um framework na maioria das vezes; o objetivo é reconhecer o que ele faz por baixo.

Um servidor com uma rota

O servidor responde a GET /pedidos/{id} com JSON. Ele devolve 200 quando o pedido existe e 404 quando não existe.

import { createServer, type ServerResponse } from "node:http";

const pedidos = new Map([["1", { id: "1", status: "enviado" }]]);

const servidor = createServer((req, res) => {
  const url = new URL(req.url ?? "/", "http://localhost");
  const rota = url.pathname.match(/^\/pedidos\/([^/]+)$/);

  if (!rota) {
    return enviar(res, 404, { erro: "rota não encontrada" });
  }
  if (req.method !== "GET") {
    res.setHeader("Allow", "GET");
    return enviar(res, 405, { erro: "método não permitido" });
  }

  const pedido = pedidos.get(rota[1]);
  if (!pedido) {
    return enviar(res, 404, { erro: "pedido não encontrado" });
  }
  enviar(res, 200, pedido);
});

function enviar(res: ServerResponse, status: number, corpo: unknown) {
  const json = JSON.stringify(corpo);
  res.writeHead(status, {
    "Content-Type": "application/json; charset=utf-8",
    "Content-Length": Buffer.byteLength(json),
  });
  res.end(json);
}

servidor.listen(3000);

O que muda de uma linguagem para outra

O protocolo é o mesmo. O que varia é quanto dele a biblioteca resolve sem você pedir.

Roteamento por caminho e métodoMétodo não suportadoContent-LengthModelo de concorrência
Node (node:http)ManualVocê decideManual ou envio em partesUm laço de eventos, sem bloqueio
Python (http.server)Manual, um método da classe por verbo501 automáticoManualUma thread por conexão
Go (net/http)Embutido desde o Go 1.22405 automático com AllowAutomáticoUma goroutine por pedido
Java (HttpServer)Só por prefixo de caminhoVocê decideInformado em sendResponseHeadersUma thread, salvo se você configurar um executor
C# (ASP.NET)Embutido405 automáticoAutomáticoPool de threads com async

Dois pontos dessa tabela costumam passar despercebidos em código gerado:

  • Java atende um pedido por vez com a configuração padrão. O exemplo funciona, e trava sob carga, até você chamar setExecutor com um pool.
  • Python com http.server serve para estudo e ferramentas internas. A documentação oficial avisa que ele não é recomendado para produção.

Veja o protocolo com o curl

Antes de confiar em qualquer biblioteca de cliente, olhe a conversa crua. A opção -v mostra o que foi enviado (linhas com >) e o que voltou (linhas com <):

curl -v http://localhost:3000/pedidos/1
> GET /pedidos/1 HTTP/1.1
> Host: localhost:3000
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Content-Length: 29
<
{"id":"1","status":"enviado"}

Teste também os casos de erro, que é onde as implementações divergem:

curl -i http://localhost:3000/pedidos/999          # espera 404
curl -i -X DELETE http://localhost:3000/pedidos/1  # espera 405

Um cliente com tempo limite

Chamar outro serviço parece a parte simples, e é onde os incidentes nascem. Um cliente sem tempo limite fica esperando para sempre um servidor que parou de responder, segura uma thread ou conexão, e em pouco tempo o seu serviço para junto.

const resposta = await fetch("http://localhost:3000/pedidos/1", {
  signal: AbortSignal.timeout(3000),
});

if (!resposta.ok) {
  throw new Error(`falha ao buscar pedido: HTTP ${resposta.status}`);
}

const pedido = await resposta.json();

Os padrões de cada cliente

Tempo limite padrãoResposta 4xx ou 5xxReaproveita conexões
Node (fetch)Nenhum para o pedido inteiroNão lança erro; confira okSim, automático
Python (urllib)NenhumLança HTTPErrorNão; cada chamada abre uma conexão
Go (http.Client)NenhumNão gera erro; confira StatusCodeSim, se o corpo for lido e fechado
Java (HttpClient)NenhumNão lança erro; confira statusCode()Sim, por instância do cliente
C# (HttpClient)100 segundosNão lança erro sem EnsureSuccessStatusCodeSim, por instância do cliente

Lista de revisão

Use esta lista ao revisar um endpoint ou um cliente HTTP, escrito por você ou por uma IA:

No servidor

  • O método combina com a intenção? Leitura em GET, e nenhuma alteração de estado em GET.
  • Os erros do cliente devolvem 4xx e as falhas do servidor devolvem 5xx? Nenhum erro sai com 200.
  • A resposta declara Content-Type com o conjunto de caracteres?
  • Operações que criam ou cobram podem ser repetidas sem efeito duplicado?
  • Respostas com dados pessoais levam Cache-Control: no-store ou private?

No cliente

  • Há tempo limite definido para a conexão e para o pedido inteiro?
  • O código de status é conferido antes de ler o corpo?
  • O cliente é criado uma vez e reaproveitado?
  • As novas tentativas se limitam a operações idempotentes, com intervalo crescente entre elas?

Prompts para aprofundar

Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.

  • Construir um servidor HTTP passo a passo

    Quero implementar um pequeno servidor HTTP para entender o que os frameworks fazem por mim.
    
    Pergunte primeiro em qual linguagem eu quero trabalhar. Use apenas a biblioteca padrão dessa linguagem, sem framework.
    
    Vamos construir em etapas, e em cada uma você explica o que vamos fazer, me deixa tentar escrever o código e só depois mostra a sua versão comentando as diferenças:
    1. Um servidor que responde "ok" a qualquer pedido.
    2. Uma rota GET /pedidos/{id} que devolve JSON, com 404 quando o pedido não existe.
    3. Tratamento de método não permitido, com 405 e o cabeçalho Allow.
    4. Uma rota POST /pedidos que lê e valida o corpo JSON, devolvendo 201 com Location ou 400/422 com uma mensagem de erro útil.
    5. Idempotência no POST usando um cabeçalho Idempotency-Key.
    
    Ao fim de cada etapa, me passe os comandos curl para testar, incluindo os casos de erro, e explique o que devo observar na saída do curl -v.
    
    No final, liste o que ainda falta para esse servidor ir para produção e quais dessas faltas um framework resolveria.
  • Revisar o meu código HTTP

    Vou colar abaixo um trecho de código que expõe um endpoint HTTP ou que chama outro serviço por HTTP. Faça uma revisão focada no uso correto do protocolo.
    
    Verifique, nesta ordem, e para cada item diga se está correto, incorreto ou ausente, citando a linha:
    1. O método HTTP combina com a intenção da operação? Há alteração de estado em GET?
    2. Os códigos de status estão corretos? Algum erro é devolvido com 200, ou algum erro do cliente com 500?
    3. Os cabeçalhos Content-Type e Cache-Control estão adequados ao conteúdo?
    4. A operação pode ser repetida com segurança? Se não, o que acontece numa nova tentativa?
    5. No código cliente: há tempo limite de conexão e do pedido inteiro? O status é conferido antes de ler o corpo? O cliente HTTP é reaproveitado entre as chamadas?
    6. Há novas tentativas automáticas? Elas se limitam a operações idempotentes e usam intervalo crescente?
    
    Para cada problema encontrado, explique a consequência concreta em produção e mostre a correção. Não reescreva o que já está correto.
    
    Código:
    [cole o seu código aqui]
  • Clientes HTTP resilientes

    Quero aprender a escrever um cliente HTTP que não derrube o meu serviço quando o serviço chamado ficar lento ou instável.
    
    Pergunte em qual linguagem e com qual biblioteca de cliente eu trabalho.
    
    Explique, com código nessa linguagem:
    1. A diferença entre tempo limite de conexão, de leitura e do pedido inteiro, e qual é o comportamento padrão da minha biblioteca para cada um.
    2. Por que criar um cliente novo a cada chamada é um problema, e como funciona o reaproveitamento de conexões na minha biblioteca.
    3. Como implementar novas tentativas com intervalo exponencial e variação aleatória, e por que elas só devem valer para operações idempotentes.
    4. O que é um circuit breaker e em que situação as novas tentativas pioram o incidente em vez de ajudar.
    
    Depois, descreva um cenário de incidente (o serviço de pagamentos passa a demorar 30 segundos para responder) e me pergunte, passo a passo, o que acontece com o meu serviço em cada configuração: sem tempo limite, com tempo limite e sem novas tentativas, e com novas tentativas agressivas. Corrija o meu raciocínio a cada resposta.