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, Multicast224.0.0.0/4, reserviert240.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.