Kiste
DE

The book is currently only available in German.

Standard-Bibliothek — `netz`

Verfügbar — implementiert in Phase B.7.10. Vollständige Spec in docs/netz.md.

netz ist Kistes HTTP-Client: Server kontaktieren, Antworten empfangen, mit Status-Code, Header und Body arbeiten. Synchron — der Aufruf kehrt erst zurück wenn die Antwort da ist (oder das Zeitlimit zuschlägt). Die Antwort kommt als eigener Wert-Typ Antwort (analog Geld, Zeit, Bytes).

31.1 Eine GET-Anfrage

nutze netz

nimm a = netz.hole("https://example.com/")
sag a.status                      // 200
sag a.erfolgreich                 // wahr
sag a.dauer_ms                    // z.B. 142
sag länge(a.bytes)                // Body-Größe in Bytes
sag a.header["content-type"]      // "text/html; charset=UTF-8"

a.inhalt gibt den Body als text (mit UTF-8-Validierung), a.bytes als rohe Bytes (immer ok).

31.2 Antwort als eigener Wert-Typ

sag typ(a)                        // Antwort
sag typ(a) == Antwort             // wahr
sag a                             // antwort(200, 528 bytes, 142ms)

Die sieben Felder einer Antwort:

Feld Typ Bedeutung
status ganz HTTP-Code (200, 404, 500, …)
inhalt text Body als UTF-8-text (wirft bei Binär-Body)
bytes Bytes Roher Body (geht immer)
header karte<text, text> Antwort-Header, lower-cased Keys
dauer_ms ganz Round-Trip-Zeit
url text Final-URL nach Redirects
erfolgreich wahrheit wahr wenn 2xx

31.3 Strikte Form: hole_text für „muss klappen"

// Muss 2xx + UTF-8 sein, sonst Fehler:
nimm html = netz.hole_text("https://example.com/")
sag html                          // "<!doctype html>..."

Idiomatisch wenn der Aufruf auf jeden Fall klappen muss. Wer „graceful degradation" will, prüft a.status selber statt sich auf catch zu verlassen — siehe §31.7.

31.4 POST/PUT/DELETE/PATCH mit sende

// Text-Body:
nimm a = netz.sende("POST", "https://api.example.com/log",
    "Skript gestartet",
    {"header": {"content-type": "text/plain"}})
sag a.status

// Bytes-Body (Datei-Upload):
nutze datei
nimm bilddaten = datei.bytes_inhalt("foto.jpg")
netz.sende("PUT", "https://api.example.com/foto.jpg",
    bilddaten,
    {"header": {"content-type": "image/jpeg"}})

// Kein Body (DELETE):
netz.sende("DELETE", "https://api.example.com/items/42", nichts)

Erlaubte Body-Typen: text, Bytes, nichts. Andere Typen werfen einen Fehler — wer Karten/Listen senden will, nutzt sende_json.

31.5 JSON-Convenience: sende_json

nimm neuer_user = {"name": "Sascha", "land": "CH"}
nimm a = netz.sende_json("POST", "https://api.example.com/users", neuer_user)
sag a.status                      // 201

sende_json ruft intern json.erzeugt auf den Daten auf und setzt Content-Type: application/json.

31.5b Erreichbarkeit prüfen: pinge

netz.pinge(url) schickt eine schlanke HEAD-Anfrage und gibt nur den Status-Code (Ganz) zurück — ideal, um schnell zu prüfen ob ein Server erreichbar ist, ohne den Inhalt zu laden:

nimm status = netz.pinge("https://example.com")
wenn status == 200 {
    sag "Server ist erreichbar"
}

31.6 Optionen-Karte

Alle fünf Funktionen akzeptieren als letztes Argument optional eine karte:

Schlüssel Default Was es macht
"header" {} Request-Headers
"parameter" {} URL-Query, automatisch encoded
"zeitlimit_ms" 30000 Timeout in Millisekunden
"folge_redirects" wahr 3xx automatisch folgen (max. 10 Hops)
"ca_datei" "" Eigene CA-Bundle, zusätzlich zum System-Trust
"tls_pruefen" wahr falsch deaktiviert TLS-Verifikation (mit stderr-Warnung)
nimm a = netz.hole("https://api.example.com/items", {
    "header": {"authorization": "Bearer abc"},
    "parameter": {"seite": "2", "pro_seite": "50"},
    "zeitlimit_ms": 5000,
})

Tippfehler bei Optionen-Schlüsseln werfen einen klaren Fehler:

netz.hole: unbekannte Option "headers" (meintest du "header"?)

31.7 Status manuell prüfen statt catch

Stdlib-Fehler werden in Kiste nicht durch versuche/fange gefangen (siehe Kapitel 11) — sie beenden das Skript. Für „graceful" HTTP-Behandlung prüft man den Status selbst:

nimm a = netz.hole("https://api.example.com/item/9999")
wenn a.status == 404 {
    sag "Nicht gefunden — neu anlegen"
} sonstwenn a.erfolgreich {
    sag "Geladen: " + a.inhalt
} sonst {
    sag "Server-Fehler " + als_text(a.status)
}

31.8 TLS — drei Sicherheits-Stufen

// Default: System-Trust (Windows Cert Store / macOS Keychain / /etc/ssl/certs)
netz.hole("https://api.example.com/")

// Corporate-CA dazuladen:
netz.hole("https://intern.firma/", {"ca_datei": "C:\\corp-ca.pem"})

// Self-Signed-Bypass für lokalen Test-Server:
netz.hole("https://localhost:8443/", {"tls_pruefen": falsch})
// → schreibt einmal pro Session auf stderr:
//   "netz: TLS-Verifikation deaktiviert für localhost:8443 — nur in Tests verwenden!"

Niemals tls_pruefen: falsch in Produktions-Code lassen — der Reviewer findet's am Schlüsselnamen, das stderr-Log macht's sichtbar.

31.9 Was netz v1 NICHT kann

  • Server-Seite (lauschen, Routes definieren) — eigene Phase
  • Async / Parallel-Requests — Kiste hat keine Goroutinen
  • WebSockets / SSE — eigene Welt
  • Multipart-Upload — eigene Phase
  • Cookies-Jar — wer Cookies braucht, setzt sie manuell als Header

31.10 Praxis: API-Daten holen und speichern

nutze netz
nutze json
nutze datei

funktion repo_info(besitzer, repo) {
    nimm url = "https://api.github.com/repos/" + besitzer + "/" + repo
    nimm a = netz.hole(url, {
        "header": {"accept": "application/vnd.github+json"},
        "zeitlimit_ms": 10000,
    })

    wenn !a.erfolgreich {
        wirf "GitHub-API-Fehler: Status " + als_text(a.status)
    }

    nimm daten = json.geparst(a.inhalt)
    nimm zusammenfassung = {
        "name": daten["name"],
        "sterne": daten["stargazers_count"],
        "geladen_in_ms": a.dauer_ms,
    }
    datei.schreibe("./repo.json", json.erzeugt(zusammenfassung))
    sag "Gespeichert — " + als_text(zusammenfassung["sterne"]) + " Sterne"
}

repo_info("sascha", "kiste")