🖧 Como um servidor web processa uma request
Quando você acessa uma URL, o navegador abre uma conexão TCP e envia texto puro seguindo o protocolo HTTP. Do outro lado, o servidor aceita essa conexão, lê bytes, parseia a request, chama o código certo e devolve outra sequência de bytes. Veja cada etapa.
① Intuição
HTTP é texto. Um servidor web no fundo não passa de um programa que:
- Fica escutando em uma porta TCP (geralmente 80 ou 443)
- Aceita conexões de clientes
- Lê bytes, que são texto HTTP
- Decide o que fazer com base no path e método
- Devolve texto HTTP com o conteúdo
Frameworks como Flask, Express ou Rails são camadas de conveniência sobre esse loop fundamental.
② Visualização — ciclo de uma request
Avance pelos passos para ver a jornada de GET /artigos/42 desde o TCP accept
até a resposta chegar de volta ao navegador.
③ Explicação técnica
Socket TCP — escutar e aceitar
import socket # Criar socket TCP server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', 8080)) server.listen(128) # backlog: fila de conexões pendentes while True: conn, addr = server.accept() # bloqueia até nova conexão data = conn.recv(4096) # lê a HTTP request handle_request(conn, data) conn.close()
Parse da HTTP request
HTTP/1.1 usa texto delimitado por \r\n. O parse é uma leitura de string:
def parse_request(raw: bytes) -> dict: text = raw.decode('utf-8') lines = text.split('\r\n') # Primeira linha: "GET /artigos/42 HTTP/1.1" method, path, version = lines[0].split(' ') # Headers até linha em branco headers = {} for line in lines[1:]: if not line: break key, _, value = line.partition(': ') headers[key.lower()] = value return {'method': method, 'path': path, 'headers': headers}
Roteamento
O router compara o path contra padrões e delega ao handler correto:
import re ROUTES = [ ('GET', r'^/$', handler_home), ('GET', r'^/artigos$', handler_lista), ('GET', r'^/artigos/(\d+)$', handler_artigo), ('POST', r'^/artigos$', handler_criar), ] def route(method, path): for m, pattern, handler in ROUTES: if m == method: match = re.match(pattern, path) if match: return handler, match.groups() return handler_404, ()
Montar e enviar a response
def send_response(conn, status, body, content_type='text/html'): body_bytes = body.encode('utf-8') response = ( f"HTTP/1.1 {status}\r\n" f"Content-Type: {content_type}; charset=utf-8\r\n" f"Content-Length: {len(body_bytes)}\r\n" f"Connection: close\r\n" f"\r\n" ).encode() + body_bytes conn.sendall(response)
Códigos de status importantes
| Código | Significado | Quando usar |
|---|---|---|
| 200 OK | Sucesso | Resposta normal |
| 201 Created | Criado | Após POST bem-sucedido |
| 301/302 | Redirect | Mudança de URL |
| 400 | Bad Request | Request malformada |
| 404 | Not Found | Recurso não existe |
| 500 | Server Error | Erro no código do servidor |
④ Projeto
Escreva um servidor HTTP do zero (sem frameworks, só stdlib):
- Crie um socket TCP que escuta na porta 8080
- Aceite conexões em loop
- Leia e parseia a request (método + path + headers)
- Implemente 3 rotas:
GET /,GET /about,GET /json - Responda com HTML ou JSON adequado
- Bônus: suporte a múltiplas conexões simultâneas com
threading
⑤ Perguntas rápidas
- O que é keep-alive e por que ele existe?
- Qual a diferença entre HTTP/1.1 e HTTP/2 no nível de transporte?
- Por que HTTPS precisa de TLS antes do HTTP começar?
- O que um reverse proxy (nginx, Caddy) faz que o servidor de app não faz?
⑥ Aplicações no mundo real
- nginx/Apache — implementam o mesmo loop, mas com event loop não-bloqueante (epoll/kqueue) para milhares de conexões simultâneas
- Flask/Express/Rails — adicionam roteamento declarativo, middleware e ORM sobre o raw socket
- Load balancers — distribuem requests entre múltiplos servidores, cada um rodando o mesmo loop
- CDNs (Cloudflare, Fastly) — servidores web distribuídos globalmente que respondem do PoP mais próximo do usuário