HTTP-Client

Führen Sie HTTP-Anfragen an externe Services durch. Unterstützt alle HTTP-Methoden, Header, Query-Parameter, Formulardaten, Datei-Uploads, Streaming-Responses und gleichzeitige Batch-Anfragen.

Laden

local http_client = require("http_client")

HTTP-Methoden

Alle Methoden teilen dieselbe Signatur: method(url, options?) gibt Response, error zurück.

GET-Anfrage

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-Anfrage

local resp, err = http_client.post("https://api.example.com/users", {
    headers = {["Content-Type"] = "application/json"},
    body = json.encode({name = "Alice", email = "alice@example.com"})
})

PUT-Anfrage

local resp, err = http_client.put("https://api.example.com/users/123", {
    headers = {["Content-Type"] = "application/json"},
    body = json.encode({name = "Alice Smith"})
})

PATCH-Anfrage

local resp, err = http_client.patch("https://api.example.com/users/123", {
    body = json.encode({status = "active"})
})

DELETE-Anfrage

local resp, err = http_client.delete("https://api.example.com/users/123", {
    headers = {["Authorization"] = "Bearer " .. token}
})

HEAD-Anfrage

Gibt nur Header zurück, keinen Body.

local resp, err = http_client.head("https://cdn.example.com/file.zip")
local size = resp.headers["Content-Length"]

Benutzerdefinierte Methode

local resp, err = http_client.request("PROPFIND", "https://dav.example.com/folder", {
    headers = {["Depth"] = "1"}
})
Parameter Typ Beschreibung
method string HTTP-Methode
url string Anfrage-URL
options table Anfrageoptionen (optional)

Anfrageoptionen

Feld Typ Beschreibung
headers table Anfrage-Header {["Name"] = "value"}
body string Anfrage-Body
query table Query-Parameter {key = "value"}
form table Formulardaten (setzt Content-Type automatisch)
files table Datei-Uploads (Array von Dateidefinitionen)
cookies table Anfrage-Cookies {name = "value"}
auth table Basic Auth {user = "name", pass = "secret"}
timeout number/string Timeout: Zahl in Sekunden oder String wie "30s", "1m"
stream boolean Response-Body streamen statt puffern
max_response_body number Max. Response-Größe in Bytes (0 = Standard)
unix_socket string Über Unix-Socket-Pfad verbinden
tls table TLS-Konfiguration pro Anfrage (siehe TLS-Optionen)
overlay_network string Über ein Netzwerk-Overlay routen — Registry-ID eines network.socks5- / network.tailscale- / network.i2p-Eintrags

Query-Parameter

local resp, err = http_client.get("https://api.example.com/search", {
    query = {
        q = "lua programming",
        page = "1",
        limit = "20"
    }
})

Header und Authentifizierung

local resp, err = http_client.get("https://api.example.com/data", {
    headers = {
        ["Authorization"] = "Bearer " .. token,
        ["Accept"] = "application/json"
    }
})

-- Oder Basic Auth verwenden
local resp, err = http_client.get("https://api.example.com/data", {
    auth = {user = "admin", pass = "secret"}
})

Formulardaten

local resp, err = http_client.post("https://api.example.com/login", {
    form = {
        username = "alice",
        password = "secret123"
    }
})

Datei-Upload

local resp, err = http_client.post("https://api.example.com/upload", {
    form = {title = "My Document"},
    files = {
        {
            name = "attachment",      -- Formularfeldname
            filename = "report.pdf",  -- Originaler Dateiname
            content = pdf_data,       -- Dateiinhalt
            content_type = "application/pdf"
        }
    }
})
Dateifeld Typ Erforderlich Beschreibung
name string ja Formularfeldname
filename string nein Originaler Dateiname
content string ja* Dateiinhalt
reader userdata ja* Alternative: io.Reader für Inhalt
content_type string nein Derzeit ignoriert: jeder hochgeladene Part wird unabhängig von diesem Feld immer mit Content-Type: application/octet-stream gesendet

*Entweder content oder reader ist erforderlich.

Timeout

-- Zahl: Sekunden
local resp, err = http_client.get(url, {timeout = 30})

-- String: Go-Dauerformat
local resp, err = http_client.get(url, {timeout = "30s"})
local resp, err = http_client.get(url, {timeout = "1m30s"})
local resp, err = http_client.get(url, {timeout = "1h"})

TLS-Optionen

Konfigurieren Sie TLS-Einstellungen pro Anfrage für mTLS (Mutual TLS) und benutzerdefinierte CA-Zertifikate.

Feld Typ Beschreibung
cert string Client-Zertifikat im PEM-Format
key string Privater Schlüssel des Clients im PEM-Format
ca string Benutzerdefiniertes CA-Zertifikat im PEM-Format
server_name string Servername für SNI-Verifizierung
insecure_skip_verify boolean TLS-Zertifikatsverifizierung überspringen

cert und key müssen für mTLS zusammen angegeben werden. Das Feld ca ersetzt den System-Zertifikatspool durch eine benutzerdefinierte CA.

mTLS-Authentifizierung

local cert_pem = fs.read("/certs/client.crt")
local key_pem = fs.read("/certs/client.key")

local resp, err = http_client.get("https://secure.example.com/api", {
    tls = {
        cert = cert_pem,
        key = key_pem,
    }
})

Benutzerdefinierte CA

local ca_pem = fs.read("/certs/internal-ca.crt")

local resp, err = http_client.get("https://internal.example.com/api", {
    tls = {
        ca = ca_pem,
        server_name = "internal.example.com",
    }
})

Unsichere Verifizierung überspringen

TLS-Verifizierung für Entwicklungsumgebungen überspringen. Erfordert die Sicherheitsberechtigung http_client.insecure_tls.

local resp, err = http_client.get("https://localhost:8443/api", {
    tls = {
        insecure_skip_verify = true,
    }
})

Response-Objekt

Feld Typ Beschreibung
status_code number HTTP-Statuscode
body string Response-Body (wenn nicht streaming)
body_size number Body-Größe in Bytes (-1 wenn streaming)
headers table Response-Header
cookies table Response-Cookies
url string Finale URL (nach Weiterleitungen)
stream Stream Stream-Objekt (wenn 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 = json.decode(resp.body)
    print("Content-Type:", resp.headers["Content-Type"])
end

Streaming-Responses

Für große Responses verwenden Sie Streaming, um zu vermeiden, dass der gesamte Body in den Speicher geladen wird.

local resp, err = http_client.get("https://cdn.example.com/large-file.zip", {
    stream = true
})
if err then
    return nil, err
end

-- In Chunks verarbeiten
while true do
    local chunk, err = resp.stream:read(65536)
    if err or not chunk then break end
    -- chunk verarbeiten
end
resp.stream:close()
Stream-Methode Gibt zurück Beschreibung
read(n?) string, error Bis zu n Bytes lesen (Standard: Implementierungspuffer)
close() boolean, error Stream schließen

resp.stream ist ein vollständiges Stream-Objekt — seek, stat und scanner sind ebenfalls verfügbar.

Batch-Anfragen

Führen Sie mehrere Anfragen gleichzeitig aus.

local responses, errors = http_client.request_batch({
    {"GET", "https://api.example.com/users"},
    {"GET", "https://api.example.com/products"},
    {"POST", "https://api.example.com/log", {body = "event"}}
})

if errors then
    for i, err in ipairs(errors) do
        if err then
            print("Request " .. i .. " failed:", err)
        end
    end
else
    -- Alle erfolgreich
    for i, resp in ipairs(responses) do
        print("Response " .. i .. ":", resp.status_code)
    end
end
Parameter Typ Beschreibung
requests table Array von {method, url, options?}

Gibt zurück: responses, errors - Arrays indiziert nach Anfrageposition

Hinweise:

  • Anfragen werden gleichzeitig ausgeführt
  • Streaming (stream = true) wird in Batch nicht unterstützt
  • Ergebnis-Arrays entsprechen der Anfragereihenfolge (1-indiziert)

URL-Kodierung

Kodieren

local encoded = http_client.encode_uri("hello world")
-- "hello+world"

local url = "https://api.example.com/search?q=" .. http_client.encode_uri(query)

Dekodieren

local decoded, err = http_client.decode_uri("hello+world")
-- "hello world"

Berechtigungen

HTTP-Anfragen unterliegen der Sicherheitsrichtlinienauswertung.

Sicherheitsaktionen

Aktion Ressource Beschreibung
http_client.request URL Anfragen an bestimmte URLs erlauben/verweigern
http_client.unix_socket Socket-Pfad Unix-Socket-Verbindungen erlauben/verweigern
http_client.private_ip IP-Adresse Zugriff auf private IP-Bereiche erlauben/verweigern
http_client.insecure_tls URL Unsichere TLS-Verbindungen erlauben/verweigern (Verifizierung überspringen)
network.select Netzwerk-Entry-ID Routing über das in der Anfrage angegebene overlay_network erlauben/verweigern

Zugriff prüfen

local security = require("security")

if security.can("http_client.request", "https://api.example.com/users") then
    local resp = http_client.get("https://api.example.com/users")
end

SSRF-Schutz

Nicht-öffentliche IP-Bereiche sind standardmäßig blockiert. Zugriff erfordert die http_client.private_ip-Berechtigung auf der Adresse:

  • Loopback, private Bereiche (10.x, 172.16-31.x, 192.168.x), Link-Local-Unicast und -Multicast sowie die unspezifizierte Adresse
  • Carrier-Grade-NAT 100.64.0.0/10, 192.0.0.0/24, Multicast 224.0.0.0/4, reserviert 240.0.0.0/4
  • Dokumentations- und Benchmarking-Bereiche 192.0.2.0/24, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 2001:db8::/32
  • IPv6-Multicast ff00::/8
local resp, err = http_client.get("http://192.168.1.1/admin")
-- Error: not allowed: private IP 192.168.1.1

Die Prüfung läuft beim Verbindungsaufbau, nicht auf dem URL-String, und sie erfasst jede Adresse, auf die der Host auflöst. Ein Hostname, der auf mehrere Adressen auflöst, wird Adresse für Adresse geprüft: Eine verweigerte Adresse wird übersprungen und die nächste versucht, und die Anfrage schlägt erst fehl, wenn jeder Kandidat verweigert oder nicht erreichbar ist. Ein öffentlicher Hostname, der auf eine private Adresse auflöst, wird daher genau wie ein privates IP-Literal blockiert.

Redirects

Bis zu neun Redirects werden verfolgt; der zehnte schlägt mit stopped after 10 redirects fehl, wobei diese Zählung die ursprüngliche Anfrage einschließt.

Jeder Sprung wird für sich autorisiert. Bevor der Client einem Redirect folgt, wertet er http_client.request gegen die Ziel-URL aus und wendet die Private-IP-Prüfung darauf an, sodass eine erlaubte URL nicht per Weiterleitung zu einer verweigerten führen kann. Ein Sprung, der eine der beiden Prüfungen nicht besteht, bricht die Anfrage ab.

Siehe Sicherheitsmodell für Richtlinienkonfiguration.

Fehler

Bedingung Art Wiederholbar
Sicherheitsrichtlinie verweigert errors.PERMISSION_DENIED nein
Private IP blockiert errors.PERMISSION_DENIED nein
Unix-Socket verweigert errors.PERMISSION_DENIED nein
Unsichere TLS verweigert errors.PERMISSION_DENIED nein
Ungültige URL oder Optionen errors.INVALID nein
Kein Kontext errors.INTERNAL nein
Netzwerkfehler errors.INTERNAL ja
Timeout errors.INTERNAL ja
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

Siehe Fehlerbehandlung für die Arbeit mit Fehlern.