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.
HEAD
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.







