http.client no Python: HTTP de baixo nível

Publicado em: 20/08/2026
Tempo de leitura: 5 minutos
Rack de servidores representando conexões HTTP de baixo nível com http.client no Python

O módulo http.client implementa o lado cliente de HTTP e HTTPS em um nível mais baixo que urllib.request. Ele expõe conexões, requisições e respostas diretamente, permitindo controlar caminho, headers, corpo, streaming, reutilização da conexão, proxy CONNECT e contexto TLS.

Essa flexibilidade é útil ao aprender o protocolo, testar servidores, implementar clientes específicos e diagnosticar integrações. Para APIs comuns, bibliotecas de nível superior normalmente são mais produtivas. Este guia mostra o ciclo correto de HTTPSConnection, leitura limitada, envio de JSON e arquivos, conexão persistente, erros e segurança.

Quando usar http.client?

Use http.client quando precisar controlar os detalhes HTTP/1.1, construir um handler, testar framing ou evitar dependências. Para redirects, cookies, autenticação e URLs completas, urllib.request no Python já oferece uma camada superior. Para aplicações maiores, considere um cliente com pooling, timeouts separados e retries.

Primeira conexão HTTPS

import http.client

connection = http.client.HTTPSConnection(
    "www.python.org",
    timeout=10,
)

try:
    connection.request(
        "GET",
        "/",
        headers={
            "Host": "www.python.org",
            "Accept": "text/html",
            "User-Agent": "MinhaFerramenta/1.0",
        },
    )
    response = connection.getresponse()
    print(response.status, response.reason)
    body = response.read(200_000)
finally:
    connection.close()

O construtor recebe host e porta, não uma URL completa. O argumento de request() normalmente é um caminho absoluto como /docs?page=1. Sempre configure timeout.

HTTPS e SSLContext

HTTPSConnection valida certificado e hostname por padrão. Para uma CA privada, forneça um contexto seguro.

import ssl

context = ssl.create_default_context(cafile="empresa-ca.pem")
connection = http.client.HTTPSConnection(
    "api.interna.example",
    timeout=10,
    context=context,
)

Não use contexto não verificado para contornar erro. Corrija a cadeia. Veja o guia de ssl no Python.

O ciclo request e getresponse

Envie uma requisição com request() e obtenha a resposta com getresponse(). Antes de enviar outra requisição na mesma conexão, leia completamente o corpo anterior ou feche a resposta.

connection.request("GET", "/primeiro")
first = connection.getresponse()
first_data = first.read()

connection.request("GET", "/segundo")
second = connection.getresponse()
second_data = second.read()

Se deixar bytes pendentes, a próxima resposta não pode ser alinhada corretamente. Para corpos grandes, leia em blocos até EOF.

Leitura limitada

MAX_BYTES = 5 * 1024 * 1024

def read_limited(response, maximum=MAX_BYTES) -> bytes:
    chunks = []
    total = 0

    while chunk := response.read(64 * 1024):
        total += len(chunk)
        if total > maximum:
            response.close()
            raise ValueError("Resposta ultrapassou o limite")
        chunks.append(chunk)

    return b"".join(chunks)

O Content-Length ajuda, mas não deve ser a única defesa. Conte os bytes reais. Para arquivos, grave em temporário em vez de acumular a lista.

Headers e conteúdo

response = connection.getresponse()
content_type = response.getheader("Content-Type")
content_length = response.getheader("Content-Length")
all_headers = response.getheaders()

Headers podem aparecer mais de uma vez. getheader() junta valores por vírgula, o que não é adequado a todos os campos, especialmente Set-Cookie. Use getheaders() quando precisar preservar ocorrências.

Enviando JSON

import json

payload = json.dumps({"name": "Teste"}).encode("utf-8")
headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Content-Length": str(len(payload)),
}

connection.request("POST", "/items", body=payload, headers=headers)
response = connection.getresponse()
raw = read_limited(response, 500_000)

Ao passar bytes, a biblioteca calcula Content-Length automaticamente se você não fornecer. Declarar explicitamente pode facilitar auditoria, mas o valor precisa corresponder.

Corpo com arquivo ou iterável

Se o corpo for arquivo ou iterável e nenhum tamanho for conhecido, a biblioteca usa transfer encoding chunked.

with open("arquivo.bin", "rb") as file:
    connection.request(
        "PUT",
        "/upload",
        body=file,
        headers={"Content-Type": "application/octet-stream"},
    )
    response = connection.getresponse()
    response.read()

Nem todo servidor legado aceita chunked. Quando necessário, descubra o tamanho com segurança e forneça Content-Length. Não reutilize um arquivo parcialmente lido após retry sem reposicionar.

Streaming de download

from pathlib import Path

connection.request("GET", "/download")
response = connection.getresponse()

if response.status != 200:
    error_body = response.read(20_000)
    raise RuntimeError(f"HTTP {response.status}")

with Path("download.tmp").open("wb") as output:
    total = 0
    while chunk := response.read(64 * 1024):
        total += len(chunk)
        if total > 100 * 1024 * 1024:
            response.close()
            raise ValueError("Arquivo muito grande")
        output.write(chunk)

Valide SHA-256 antes de mover o arquivo. O guia de hashlib no Python mostra essa verificação.

Status HTTP

http.client não lança automaticamente exceção para 404 ou 500. Você precisa interpretar response.status.

if 200 <= response.status < 300:
    data = read_limited(response)
elif response.status == 404:
    response.read(20_000)
    raise LookupError("Recurso não encontrado")
else:
    response.read(20_000)
    raise RuntimeError(f"Servidor respondeu {response.status}")

Considere redirects, 429 e Retry-After conforme o contexto. A camada não os segue por você.

Reutilização e Connection: close

HTTP/1.1 permite conexão persistente. Reutilizar reduz handshake TCP/TLS, mas exige consumir respostas, tratar fechamento remoto e não compartilhar uma conexão simultaneamente entre threads sem coordenação.

Se RemoteDisconnected ou um ConnectionError ocorrer antes de uma operação idempotente, feche e crie outra conexão. Não repita POST automaticamente, pois o servidor pode ter processado o pedido.

connection.request("HEAD", "/arquivo.zip")
response = connection.getresponse()
print(response.status, response.getheader("Content-Length"))
response.read()

HEAD não possui corpo, mas leia/feche a resposta para concluir o ciclo. O servidor pode informar tamanho e tipo, porém esses valores não substituem limites durante um GET posterior.

Proxy CONNECT

set_tunnel() configura um túnel CONNECT. A conexão é criada com o endereço do proxy e o túnel aponta ao destino.

proxy = http.client.HTTPSConnection("proxy.example", 8443, timeout=10)
proxy.set_tunnel(
    "www.python.org",
    443,
    headers={"Host": "www.python.org:443"},
)
proxy.request("GET", "/")
response = proxy.getresponse()

Proteja credenciais do proxy e valide o certificado do destino. Desde Python 3.12, CONNECT usa HTTP/1.1 e get_proxy_response_headers() permite inspecionar a resposta do proxy.

API passo a passo

Os métodos putrequest(), putheader(), endheaders() e send() expõem o protocolo em nível ainda mais baixo. Use-os apenas quando request() não atende, porque é fácil gerar headers inválidos, duplicar Host ou errar chunked encoding.

Erros e fechamento

try:
    connection.request("GET", "/")
    response = connection.getresponse()
    data = read_limited(response)
except (TimeoutError, OSError, http.client.HTTPException) as error:
    connection.close()
    raise RuntimeError("Falha HTTP") from error

Exceções importantes incluem IncompleteRead, BadStatusLine, LineTooLong, ResponseNotReady e RemoteDisconnected. Após estado incerto, feche a conexão.

Debuglevel

set_debuglevel(1) imprime detalhes do protocolo em stdout. Use somente em ambiente controlado, pois headers de autorização e outros dados podem aparecer.

Proteção contra SSRF

Se host e porta vierem do usuário, valide esquema, DNS e IP antes da conexão. Bloqueie loopback, redes privadas, link-local e endpoints de metadata. A conexão recebe host separado, mas continua sujeita a SSRF e DNS rebinding.

Quando escolher uma API superior

Use urllib.request ou outro cliente quando precisar de URLs, redirects, cookies e autenticação automática. http.client é ideal para controle fino, aprendizado e infraestrutura interna bem delimitada.

O guia de APIs REST com Python mostra padrões completos de integração.

Erros comuns

Os erros frequentes são omitir timeout, usar URL completa no caminho incorretamente, não consumir a resposta antes da próxima requisição, compartilhar a conexão sem lock, ler corpo ilimitado, desabilitar TLS, repetir POST após falha e ativar debug com credenciais.

Boas práticas

Use HTTPS com contexto validado, timeout, limites de corpo, leitura incremental e fechamento garantido. Consuma ou feche cada resposta. Reutilize apenas conexões saudáveis, trate métodos idempotentes separadamente e mantenha hosts externos em allowlist quando possível.

Conclusão

http.client oferece acesso direto ao cliente HTTP/1.1 da biblioteca padrão. Ele permite controlar conexão, request, resposta, streaming e proxy, mas deixa redirects, status, retries e limites sob responsabilidade da aplicação. Use-o quando o controle justificar o trabalho adicional.

Consulte a documentação oficial de http.client e o RFC 9112 sobre HTTP/1.1.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Código HTML em uma tela representando crawling responsável com robotparser no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    robotparser no Python: leia robots.txt

    Aprenda urllib.robotparser no Python para respeitar robots.txt, crawl-delay, request-rate, sitemaps, cache e limites de crawling.

    Ler mais

    Tempo de leitura: 5 minutos
    20/08/2026
    Teclas formando HTTP representando requisições com urllib.request no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    urllib.request no Python: HTTP nativo

    Aprenda urllib.request no Python para fazer GET, POST e downloads com timeout, TLS, redirects, proxies, limites e tratamento de erros.

    Ler mais

    Tempo de leitura: 5 minutos
    19/08/2026
    Cabos Ethernet conectados representando servidores de rede com socketserver no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    socketserver no Python: crie servidores

    Aprenda socketserver no Python para criar servidores TCP e UDP, aplicar concorrência, limites, timeouts e encerramento seguro.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Sala de servidores iluminada representando conexões TLS seguras com ssl no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ssl no Python: conexões TLS seguras

    Aprenda ssl no Python para criar clientes e servidores TLS, validar certificados, configurar versões mínimas, CA e autenticação mútua.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Tela de verificação de conta representando autenticação de mensagens com HMAC no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    hmac no Python: autentique mensagens

    Aprenda HMAC no Python para assinar e validar webhooks, arquivos e mensagens com SHA-256, chaves seguras e comparação resistente a

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026
    Leitor de impressão digital representando verificação de hashes com hashlib no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    hashlib no Python: hashes seguros

    Aprenda hashlib no Python para calcular SHA-256, verificar arquivos, usar BLAKE2, derivar chaves e evitar erros comuns de segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    19/08/2026