Caderno operacional — dissecar uma chamada
Este laboratório cria uma API HTTP local e a utiliza para observar método, caminho, query, status, headers, paginação e timeout sem credenciais nem dependência externa. O servidor Python oferece saúde, lista limitada de ordens, recurso inexistente e resposta lenta; um cliente registra resultados e provoca falhas controladas. O leitor aprende a distinguir recusa HTTP de espera excedida, evitar retries perigosos, produzir uma matriz de diagnóstico e sanitizar evidências. Ao final, comandos, saídas e decisões formam um runbook reproduzível para investigar integrações com ERP sem copiar tokens ou atribuir toda falha genericamente à rede.
Caderno operacional — dissecar uma chamada
Situação concreta
Você precisa aprender HTTP, mas não deve testar falhas numa API de produção nem depender de token. Construirá um servidor que escuta apenas na própria máquina e um cliente que chama quatro endpoints. A experiência simula saúde, lista paginada, recurso ausente e lentidão.
Pré-requisitos, objetivos e artefatos
Leia Da URL à resposta. Tenha Python 3 e dois terminais. Crie laboratorio-http com servidor.py, cliente.py e relatorio.md. Não use dados reais.
Localhost é o nome convencional da própria máquina. O endereço de loopback 127.0.0.1 não publica o servidor na Internet. Endpoint é uma combinação acessível de método e caminho. Runbook é um roteiro operacional com condições e ações verificáveis.
Modelo mental e limite
Ler o fluxo em texto
- 1. Cliente monta a requisição
- 2. Loopback entrega ao servidor
- 3. Servidor escolhe o endpoint
- 4. Aplicação gera status e corpo
- 5. Cliente aplica timeout
- 6. Relatório compara resultado
Como tudo ocorre localmente, você não observará DNS público, roteadores ou TLS. Isso é intencional: isolamos HTTP e timeout da aplicação. TLS deve ser estudado com ferramenta e ambiente apropriados, nunca com certificado improvisado em produção.
Antes e agora
Uma verificação manual costuma registrar apenas “funcionou”. Um teste operacional atual registra versão, método, URL, horário, status, duração e corpo sanitizado. Observabilidade distribuída acrescenta correlação entre serviços, mas a disciplina começa nesta chamada local. IA pode ajudar a interpretar a matriz; não deve receber tokens ou payload empresarial.
Passo 1 — crie o servidor
Crie servidor.py:
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlsplit
import json
import time
ORDENS = [
{"id": "OS-1", "status": "aberta"},
{"id": "OS-2", "status": "aberta"},
{"id": "OS-3", "status": "concluida"},
]
class ApiLocal(BaseHTTPRequestHandler):
def responder(self, status: int, payload: dict) -> None:
corpo = json.dumps(payload).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(corpo)))
self.end_headers()
self.wfile.write(corpo)
def do_GET(self) -> None:
partes = urlsplit(self.path)
if partes.path == "/health":
self.responder(200, {"status": "ok"})
return
if partes.path == "/lento":
time.sleep(0.3)
self.responder(200, {"status": "demorou"})
return
if partes.path == "/ordens":
query = parse_qs(partes.query)
limite = min(int(query.get("limit", ["2"])[0]), 2)
self.responder(200, {"itens": ORDENS[:limite], "total": len(ORDENS)})
return
self.responder(404, {"erro": "nao_encontrado"})
def log_message(self, formato: str, *args: object) -> None:
print(self.address_string(), formato % args)
servidor = ThreadingHTTPServer(("127.0.0.1", 8000), ApiLocal)
print("API local em http://127.0.0.1:8000")
servidor.serve_forever()BaseHTTPRequestHandler analisa a mensagem; do_GET trata GET. O limite máximo é dois, mesmo que o cliente peça mil. Content-Length informa bytes. O exemplo não possui autenticação, persistência nem TLS e só deve ser local.
Passo 2 — crie o cliente
Em cliente.py:
from time import perf_counter
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
BASE = "http://127.0.0.1:8000"
def consultar(caminho: str, timeout: float = 1.0) -> None:
inicio = perf_counter()
pedido = Request(BASE + caminho, headers={"Accept": "application/json"})
try:
with urlopen(pedido, timeout=timeout) as resposta:
corpo = resposta.read().decode("utf-8")
print(caminho, resposta.status, resposta.headers["Content-Type"], corpo)
except HTTPError as erro:
print(caminho, "HTTP", erro.code, erro.read().decode("utf-8"))
except (TimeoutError, URLError) as erro:
print(caminho, "TRANSPORTE", type(erro).__name__)
finally:
print("duracao_ms", round((perf_counter() - inicio) * 1000, 1))
consultar("/health")
consultar("/ordens?limit=1")
consultar("/inexistente")
consultar("/lento", timeout=0.2)Request cria a mensagem; urlopen conecta e lê. HTTPError ainda contém uma resposta HTTP, como 404. Timeout ou URLError indicam que o cliente não obteve uma resposta utilizável no limite.
Passo 3 — execute e observe
No terminal A:
python servidor.pyNo terminal B:
python cliente.pyEspere 200 para saúde e ordens, 404 para caminho inexistente e falha de transporte/timeout para /lento. O servidor pode registrar erro de conexão ao tentar responder depois que o cliente desistiu; isso é evidência da corrida entre prazo e processamento.
Registre Python, sistema, horário, comandos, status e durações. Pare o servidor com Ctrl+C.
Passo 4 — leia cada resultado sem pular camadas
Na saúde, confirme que houve status 200, tipo application/json e corpo esperado. Na coleção, compare limit=1 com quantidade devolvida e total. No caminho inexistente, observe que o cliente recebeu status e corpo: transporte e HTTP funcionaram, mesmo que o recurso não exista. Na resposta lenta, o cliente desistiu antes de receber; não há status utilizável para registrar.
Essa leitura evita um erro frequente: considerar qualquer resultado diferente de 200 como “falha de rede”. Um 400 ou 404 é evidência de comunicação bem-sucedida até uma aplicação capaz de responder. O negócio pode ter falhado, mas a rede não é a primeira hipótese. Registre também o ponto de vista do servidor; ele pode ter concluído trabalho depois que o cliente já desistiu.
Falha e diagnóstico
Execute o cliente com servidor parado: conexão recusada não é 503, pois nenhum servidor HTTP respondeu. Reinicie e troque a porta do cliente para 8001: mesmo sintoma, causa de configuração. Depois aumente timeout lento para 0.5: a resposta deve chegar, mostrando que a rota existe, mas viola o orçamento anterior.
Monte a matriz:
| Sintoma | Camada provável | Evidência |
|---|---|---|
| conexão recusada | processo/porta | servidor ausente ou porta errada |
404 |
HTTP/aplicação | status e corpo recebidos |
| timeout | transporte ou processamento | duração e ausência de resposta no prazo |
200 com corpo errado |
contrato | status, headers e payload |
Não trate retry como correção automática. Três clientes repetindo três vezes multiplicam carga durante falha. Neste laboratório, GET é seguro; numa criação, repetir pode duplicar efeito.
Para cada linha da matriz, acrescente “responsável provável” e “próximo teste que separa hipóteses”. Conexão recusada pode ser servidor parado ou porta incorreta; verificar processo e configuração distingue as causas. Timeout pode ser processamento lento ou orçamento curto; comparar log do servidor e duração ajuda. Uma matriz útil orienta a próxima observação, não apenas atribui culpa a uma equipe.
Aplicação em manutenção/ERP
Substitua mentalmente ORDENS por consulta ao ERP. O endpoint deve aplicar limite máximo, autenticação, autorização e correlação. Se o ERP leva 300 ms e o gateway permite 200 ms, a chamada sempre expira; alinhe orçamento e objetivo, não apenas aumente indefinidamente.
Para paginação real, retorne cursor baseado em ordem estável. Offset pode duplicar ou omitir quando ordens entram entre páginas. Registre total somente se o custo for aceitável.
O parâmetro de consulta, ou query parameter, é entrada não confiável. No exemplo, int() recebe o limite sem tratamento; texto como abc provoca erro no servidor. Em produção, valide formato, mínimo e máximo e devolva erro contratual sem stack trace. Pedir limit=1000 não deve superar o teto do servidor. O cliente também precisa parar ao fim da paginação e detectar cursor repetido para não entrar em laço.
Segurança e privacidade
Mantenha bind em 127.0.0.1; 0.0.0.0 expõe em todas as interfaces disponíveis. Não adicione token ao código. Em capturas, remova cookies, Authorization e payloads reais. O servidor didático não limita corpo, autentica nem cifra; nunca o publique como API empresarial. Bibliotecas de produção oferecem validação e servidores endurecidos.
Exercício guiado
Adicione filtro status=aberta com valor permitido. Para desconhecido, devolva 400 e código status_invalido. Antes de programar, escreva dois casos positivos e um negativo. Execute novamente os quatro casos antigos para evitar regressão.
Depois faça cinco chamadas de saúde e registre mínimo, máximo e mediana. Isso não é benchmark: a amostra local é pequena e não inclui rede real. O objetivo é aprender a guardar distribuição em vez de citar uma única duração. Explique por que aumentar timeout pode reduzir erros visíveis e, ao mesmo tempo, manter recursos ocupados por mais tempo.
Desafio e evidência
Crie relatorio.md com diagrama, comandos, saídas, matriz e runbook de cinco passos. Peça a outra pessoa para reproduzir do zero.
Critérios de aceite:
- servidor escuta apenas loopback;
- saúde, lista, 404 e timeout são demonstrados;
- relatório diferencia resposta HTTP de falha de conexão;
- limite máximo não é ultrapassado;
- nenhum segredo ou dado real aparece;
- retries e escalonamento possuem condição explícita.
Conclusão e transição
Você isolou HTTP do restante da Internet e observou que 404 é resposta, enquanto conexão recusada e timeout são falhas anteriores ou temporais. Esse modelo será reutilizado ao estudar terminal, ambientes, APIs e observabilidade: cada comando precisa de contexto, limite e evidência.
Fontes oficiais e primárias
- Python —
http.server: servidor usado, com alerta oficial de não recomendação para produção. - Python —
urllib.request: cliente HTTP da biblioteca padrão. - RFC 9110 — HTTP Semantics: métodos e status.
- RFC 9112 — HTTP/1.1: sintaxe e conexão da versão usada pelo servidor didático.
Comprove o que você aprendeu
Responda todas as questões. O gabarito comentado só aparece depois do envio.