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

    Desenvolvedor configurando servidor HTTPS e certificado TLS com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: crie servidor HTTPS local no Python

    Aprenda HTTPSServer no Python para servir HTTPS localmente, configurar certificados, usar threads e entender limites de segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026
    Arquivos protegidos representando extração segura de TAR com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: extraia TAR com segurança

    Aprenda tarfile extraction_filter no Python para extrair arquivos TAR com validação, segurança e controle de caminhos.

    Ler mais

    Tempo de leitura: 6 minutos
    08/10/2026
    Código Python exibindo avisos controlados com catch_warnings
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    catch_warnings: capture warnings em testes Python

    Aprenda catch_warnings no Python para capturar, testar e controlar avisos com filtros específicos e segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código Python representando referências persistentes do pickle
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    pickle persistent_id: serialize referências externas

    Aprenda pickle persistent_id no Python para serializar referências externas com IDs estáveis, validação, segurança e compatibilidade.

    Ler mais

    Tempo de leitura: 5 minutos
    07/10/2026
    Código binário representando buffers e memória no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview.count: conte valores sem copiar buffers

    Aprenda memoryview.count no Python para contar bytes e valores em buffers sem cópias, com formatos, limites e boas práticas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026
    Código Python com anotações de tipos
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    annotationlib: evite imports circulares em anotações

    Aprenda annotationlib no Python para ler anotações adiadas, evitar imports circulares e criar ferramentas de introspecção seguras.

    Ler mais

    Tempo de leitura: 6 minutos
    06/10/2026