Cliente HTTP
O módulo http_client envia requisições HTTP com headers, parâmetros de query, formulários, uploads de arquivos, autenticação, opções TLS, respostas em streaming e lotes concorrentes.
Esta página é uma referência de API com receitas parciais de requisição. URLs, tokens, credenciais, dados das requisições e certificados são fornecidos pela aplicação. Os exemplos verificam Response, error antes de usar a resposta e fecham explicitamente os corpos recebidos em streaming.
Carregamento
local http_client = require("http_client")
Adicione http_client à lista modules: da entrada executável antes de importá-lo. As receitas com JSON e sistema de arquivos também exigem json e fs.
Métodos HTTP
Os métodos de conveniência usam a assinatura method(url, options?) e retornam Response, error.
GET
Envia uma requisição GET.
local resp, err = http_client.get("https://api.example.com/users")
if err then
return nil, err
end
print(resp.status_code) -- 200
print(resp.body) -- response body
POST
Envia uma requisição POST.
local json = require("json")
local body, body_err = json.encode({name = "Alice", email = "alice@example.com"})
if body_err then return nil, body_err end
local resp, err = http_client.post("https://api.example.com/users", {
headers = {["Content-Type"] = "application/json"},
body = body
})
if err then return nil, err end
PUT
Envia uma requisição PUT.
local body, body_err = json.encode({name = "Alice Smith"})
if body_err then return nil, body_err end
local resp, err = http_client.put("https://api.example.com/users/123", {
headers = {["Content-Type"] = "application/json"},
body = body
})
if err then return nil, err end
PATCH
Envia uma requisição PATCH.
local body, body_err = json.encode({status = "active"})
if body_err then return nil, body_err end
local resp, err = http_client.patch("https://api.example.com/users/123", {
headers = {["Content-Type"] = "application/json"},
body = body
})
if err then return nil, err end
DELETE
Envia uma requisição DELETE.
local resp, err = http_client.delete("https://api.example.com/users/123", {
headers = {["Authorization"] = "Bearer " .. token}
})
if err then return nil, err end
HEAD
Uma requisição HEAD retorna os headers sem um corpo de resposta.
local resp, err = http_client.head("https://cdn.example.com/file.zip")
if err then return nil, err end
local size = resp.headers["Content-Length"]
Métodos Personalizados
Envia uma requisição usando uma string de método HTTP explícita.
local resp, err = http_client.request("PROPFIND", "https://dav.example.com/folder", {
headers = {["Depth"] = "1"}
})
if err then return nil, err end
| Parâmetro | Tipo | Descrição |
|---|---|---|
method |
string | Método HTTP |
url |
string | URL da requisição |
options |
table | Opções da requisição (opcional) |
Opções de Requisição
| Campo | Tipo | Descrição |
|---|---|---|
headers |
table | Headers da requisição {["Name"] = "value"} |
body |
string | Corpo da requisição |
query |
table | Parametros de query {key = "value"} |
form |
table | Dados de formulario (define Content-Type automaticamente) |
files |
table | Uploads de arquivo (array de definicoes de arquivo) |
cookies |
table | Cookies da requisição {name = "value"} |
auth |
table | Basic auth {user = "name", pass = "secret"} |
timeout |
number/string | Timeout: numero em segundos, ou string como "30s", "1m" |
stream |
boolean | Streaming do corpo da resposta ao inves de buffer |
max_response_body |
number | Tamanho maximo da resposta em bytes (0 = padrão) |
unix_socket |
string | Conectar via caminho de socket Unix |
tls |
table | Configuracao TLS por requisicao (ver Opcoes TLS) |
overlay_network |
string | Roteia através de um overlay de rede — ID de registro de uma entrada network.socks5 / network.tailscale / network.i2p |
Selecionar overlay_network exige a permissão network.select no ID da rede.
Parâmetros de Query
local resp, err = http_client.get("https://api.example.com/search", {
query = {
q = "lua programming",
page = "1",
limit = "20"
}
})
if err then return nil, err end
Headers e Autenticação
local resp, err = http_client.get("https://api.example.com/data", {
headers = {
["Authorization"] = "Bearer " .. token,
["Accept"] = "application/json"
}
})
if err then return nil, err end
-- Or use basic auth
local resp, err = http_client.get("https://api.example.com/data", {
auth = {user = service_user, pass = service_password}
})
if err then return nil, err end
Carregue os valores de autenticação de um armazenamento de segredos controlado pela aplicação e envie-os somente por TLS.
Dados de Formulario
local resp, err = http_client.post("https://api.example.com/login", {
form = {
username = username,
password = password
}
})
if err then return nil, err end
Upload de Arquivo
local resp, err = http_client.post("https://api.example.com/upload", {
form = {title = "My Document"},
files = {
{
name = "attachment", -- form field name
filename = "report.pdf", -- original filename
content = pdf_data, -- file content
content_type = "application/pdf"
}
}
})
if err then return nil, err end
| Campo de Arquivo | Tipo | Obrigatorio | Descrição |
|---|---|---|---|
name |
string | sim | Nome do campo do formulario |
filename |
string | não | Nome original do arquivo |
content |
string | sim* | Conteudo do arquivo |
reader |
userdata | sim* | Alternativa: io.Reader para conteudo |
content_type |
string | não | Atualmente ignorado: cada parte enviada é sempre transmitida com Content-Type: application/octet-stream independentemente deste campo |
*É obrigatório fornecer content ou reader.
O runtime fixado lê todo o reader na memória antes do envio, não o fecha e não relata separadamente uma falha de leitura diferente de EOF; ele pode enviar os bytes acumulados antes dessa falha. Prefira content para dados cujo tamanho já é limitado e feche readers pertencentes ao chamador depois da requisição. O campo content_type é interpretado, mas não encaminhado pelo runtime v0.3.32a, portanto as partes enviadas usam o padrão do transporte.
Arquivos baseados em reader só funcionam em chamadas de requisição individual nesta versão. request_batch encaminha o campo content, mas descarta um reader interpretado; uploads em lote devem fornecer content.
Timeout
-- Number: seconds
local resp, err = http_client.get(url, {timeout = 30})
if err then return nil, err end
-- String alternatives use Go duration format: "30s", "1m30s", or "1h".
Opções TLS {id="tls-options"}
Configure TLS mútuo e certificados CA personalizados para uma requisição.
| Campo | Tipo | Descrição |
|---|---|---|
cert |
string | Certificado do cliente em formato PEM |
key |
string | Chave privada do cliente em formato PEM |
ca |
string | Certificado CA customizado em formato PEM |
server_name |
string | Nome do servidor para verificacao SNI |
insecure_skip_verify |
boolean | Pular verificacao de certificado TLS |
Tanto cert quanto key devem ser fornecidos juntos para mTLS. O campo ca substitui o pool de certificados do sistema por um CA customizado.
Autenticacao mTLS
local fs = require("fs")
local certs, volume_err = fs.get("app:certs")
if volume_err then return nil, volume_err end
local cert_pem, cert_err = certs:readfile("client.crt")
if cert_err then return nil, cert_err end
local key_pem, key_err = certs:readfile("client.key")
if key_err then return nil, key_err end
local resp, err = http_client.get("https://secure.example.com/api", {
tls = {
cert = cert_pem,
key = key_pem,
}
})
if err then return nil, err end
CA Customizado
local fs = require("fs")
local certs, volume_err = fs.get("app:certs")
if volume_err then return nil, volume_err end
local ca_pem, ca_err = certs:readfile("internal-ca.crt")
if ca_err then return nil, ca_err end
local resp, err = http_client.get("https://internal.example.com/api", {
tls = {
ca = ca_pem,
server_name = "internal.example.com",
}
})
if err then return nil, err end
Pular Verificacao TLS
insecure_skip_verify desativa a verificação TLS e exige a permissão de segurança http_client.insecure_tls.
local resp, err = http_client.get("https://localhost:8443/api", {
tls = {
insecure_skip_verify = true,
}
})
if err then return nil, err end
Use insecure_skip_verify somente com um endpoint de diagnóstico controlado. A opção desativa tanto a verificação da cadeia de certificados quanto a do hostname.
Objeto Response
| Campo | Tipo | Descrição |
|---|---|---|
status_code |
number | Código de status HTTP |
body |
string | Corpo da resposta (se não streaming) |
body_size |
number | Tamanho do corpo em bytes (-1 se streaming) |
headers |
table | Headers da resposta |
cookies |
table | Cookies da resposta |
url |
string | URL final (apos redirecionamentos) |
stream |
Stream | Objeto stream (se stream = true) |
local resp, err = http_client.get("https://api.example.com/data")
if err then
return nil, err
end
if resp.status_code == 200 then
local data, decode_err = json.decode(resp.body)
if decode_err then return nil, decode_err end
print("Content-Type:", resp.headers["Content-Type"])
end
Respostas em Streaming
Defina stream = true para processar uma resposta incrementalmente, em vez de manter todo o corpo em memória.
local resp, err = http_client.get("https://cdn.example.com/large-file.zip", {
stream = true
})
if err then
return nil, err
end
-- Process in chunks
local read_err
while true do
local chunk
chunk, read_err = resp.stream:read(65536)
if read_err or not chunk then break end
-- process chunk
end
local _, close_err = resp.stream:close()
if read_err then return nil, read_err end
if close_err then return nil, close_err end
| Método Stream | Retorna | Descrição |
|---|---|---|
read(n?) |
string, error | Ler até n bytes (padrão: buffer da implementação) |
close() |
boolean, error | Fechar o stream |
resp.stream é um objeto stream completo — seek, stat e scanner também estão disponíveis. O chamador é responsável pelo corpo recebido em streaming e deve fechá-lo em toda saída; a limpeza da tarefa é um fallback, não um substituto para a liberação imediata.
Requisicoes em Lote
request_batch executa várias requisições concorrentemente.
local requests = {
{"GET", "https://api.example.com/users"},
{"GET", "https://api.example.com/products"},
{"POST", "https://api.example.com/log", {body = "event"}}
}
local responses, batch_errors = http_client.request_batch(requests)
if not responses then
return nil, batch_errors -- whole-batch dispatch or validation failure
end
if batch_errors then
for i = 1, #requests do
local err = batch_errors[i]
if err then
print("Request " .. i .. " failed:", err)
end
end
else
-- All succeeded
for i, resp in ipairs(responses) do
print("Response " .. i .. ":", resp.status_code)
end
end
| Parâmetro | Tipo | Descrição |
|---|---|---|
requests |
table | Array de {method, url, options?} |
Retorna: responses, errors - arrays indexados pela posicao da requisição
Notas:
- Requisicoes executam concorrentemente
- Streaming (
stream = true) não e suportado em lote - Uploads baseados em
readernão são aceitos em lote; usefiles[].content - Arrays de resultado correspondem a ordem das requisicoes (indexados a partir de 1)
Codificação de URL
Codificar
Codifica uma string para inclusão em uma URL.
local encoded = http_client.encode_uri("hello world")
-- "hello+world"
local url = "https://api.example.com/search?q=" .. http_client.encode_uri(query)
Decodificar
Decodifica uma string codificada anteriormente com http_client.encode_uri.
local decoded, err = http_client.decode_uri("hello+world")
if err then return nil, err end
-- "hello world"
Permissões
Requisicoes HTTP estao sujeitas a avaliação de política de segurança.
Acoes de Segurança
| Ação | Recurso | Descrição |
|---|---|---|
http_client.request |
URL | Permitir/negar requisicoes para URLs específicas |
http_client.unix_socket |
Caminho do socket | Permitir/negar conexoes Unix socket |
http_client.private_ip |
Endereco IP | Permitir/negar acesso a faixas de IP privado |
http_client.insecure_tls |
URL | Permitir/negar TLS inseguro (pular verificacao) |
network.select |
ID da entrada de rede | Permitir/negar roteamento pela overlay_network informada na requisição |
Verificando Acesso
local security = require("security")
if security.can("http_client.request", "https://api.example.com/users") then
local resp, request_err = http_client.get("https://api.example.com/users")
if request_err then return nil, request_err end
end
Protecao SSRF
Faixas de IP nao publicas sao bloqueadas por padrão. Acesso requer a permissão http_client.private_ip no endereço:
- loopback, privadas (10.x, 172.16-31.x, 192.168.x), link-local unicast e multicast, e o endereço não especificado
- NAT de operadora
100.64.0.0/10,192.0.0.0/24, multicast224.0.0.0/4, reservadas240.0.0.0/4 - faixas de documentação e benchmarking
192.0.2.0/24,198.18.0.0/15,198.51.100.0/24,203.0.113.0/24,2001:db8::/32 - multicast IPv6
ff00::/8
local resp, err = http_client.get("http://192.168.1.1/admin")
-- Error: not allowed: private IP 192.168.1.1
A verificação ocorre no momento da conexão, não sobre a string da URL, e cobre todos os endereços para os quais o host resolve. Um hostname que resolve para vários endereços é verificado endereço por endereço: um endereço negado é ignorado e o próximo é tentado, e a requisição só falha quando todos os candidatos são negados ou inalcançáveis. Um hostname público que resolve para um endereço privado é portanto bloqueado exatamente como um IP privado literal.
Redirecionamentos
Até nove redirecionamentos são seguidos; o décimo falha com stopped after 10 redirects, uma contagem que inclui a requisição original.
Cada salto é autorizado por conta própria. Antes de seguir um redirecionamento, o cliente avalia http_client.request contra a URL alvo e aplica a verificação de IP privado a ela, então uma URL permitida não pode ser usada para alcançar uma negada por redirecionamento. Um salto que falha em qualquer das verificações aborta a requisição.
Veja Security Model para configuração de políticas.
Erros
| Condição | Tipo | Retentável |
|---|---|---|
| Política de segurança negou | errors.PERMISSION_DENIED |
não |
| IP privado bloqueado | errors.PERMISSION_DENIED |
não |
| Socket Unix negado | errors.PERMISSION_DENIED |
não |
| TLS inseguro negado | errors.PERMISSION_DENIED |
não |
| Item de lote inválido, streaming em lote ou escape de URI inválido | errors.INVALID |
não |
| Sem contexto | errors.INTERNAL |
não |
| URL de transporte malformada ou falha de rede | errors.INTERNAL |
sim |
| Timeout | errors.INTERNAL |
sim |
Muitos valores de opções não aceitos são ignorados, em vez de retornarem erros estruturados. Tipos de argumentos Lua inválidos e um lote vazio geram erros de argumento Lua. Valide tabelas de opções fornecidas pela aplicação antes de chamar o cliente.
local resp, err = http_client.get(url)
if err then
if errors.is(err, errors.PERMISSION_DENIED) then
print("Access denied:", err:message())
elseif err:retryable() then
print("Temporary error:", err:message())
end
return nil, err
end
Veja Tratamento de Erros para trabalhar com erros.