---
title: "ADR-0018: Eine native cURL-Handle-Erzeugung je Transferlease"
manual: "HTTP Guard"
version: "0.1.4"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-http-guard:adr-0018@0.1.4"
source: "Adr/ADR-0018-SingleUseCurlFactory.rst"
rendered: "2026-10-18T14:31:43+00:00"
---

# ADR-0018: Eine native cURL-Handle-Erzeugung je Transferlease {#adr-0018}

-   *Status:* Angenommen
-   *Datum:* 2026-10-10
-   *Bezug:* Ergänzt ADR-0003, ADR-0007 und ADR-0010.

## Kontext {#adr-0018-context}

Eine neue native Verbindung darf die abschließende Policyprüfung nicht
umgehen. Ein SDK kann beim Abschluss eines fehlgeschlagenen Transfers intern
eine Wiederholung anfordern; Callbacks bei der Bodyvorbereitung können
ebenfalls erneut in die Handle-Erzeugung eintreten. Private SDK-Retryzähler
sind keine stabile Grenze für diese Versuche.

Die Transferlease aus [ADR-0010: Adressmemoisierung erlauben, Berechtigungs- und Verbindungspooling isolieren](https://docs.typo3.org/permalink/netresearch/nr-http-guard:adr-0010@0.1.4) benötigt deshalb eine eigene,
unmittelbar vor der nativen Handle-Erzeugung wirksame Einmaligkeitsgrenze.
Die Entscheidung betrifft den kontrollierten Transport aus [ADR-0003: Zwei Middlewaregrenzen und ein kontrollierter terminaler Transport](https://docs.typo3.org/permalink/netresearch/nr-http-guard:adr-0003@0.1.4)
und [ADR-0007: Nur kontrolliertes cURL; Proxies und PHP-Streams nicht in v1](https://docs.typo3.org/permalink/netresearch/nr-http-guard:adr-0007@0.1.4), nicht eine prozessweite Unterbindung beliebiger cURL-Nutzung.

Für die Reihenfolge von Boundary und Terminal ist zusätzlich eine prüfbare
Middleware-Inventarliste nötig. Das derzeit verwendete SDK bietet diese
Liste über die private `HandlerStack.stack`-Eigenschaft. Die
Einmalfactory entfernt diese separate SDK-Kopplung nicht.

## Entscheidung {#adr-0018-decision}

`SingleUseCurlFactory` implementiert die öffentliche
`CurlFactoryInterface` und dekoriert `CurlFactory(0)`.
Vor dem ersten Delegieren setzt sie ihren Verbrauchszustand. Jede zweite
`create()`-Anforderung derselben Instanz scheitert mit
`transport_unsupported`, bevor der Delegate ein weiteres Handle
erzeugt. Das gilt auch nach gescheiterter oder erneut eintretender
Bodyvorbereitung. `release()` delegiert das Freigeben, setzt die
verbrauchte Instanz aber nicht zurück.

`TransferLease` erzeugt eine neue Einmalfactory und einen eigenen
`CurlMultiHandler` je Versuch. Verbindung und DNS-Zustand werden
nicht zwischen unabhängigen Leases übernommen; frische Verbindung,
verbotene Wiederverwendung und abgeschalteter nativer DNS-Cache ergänzen
das verbindungsgebundene Pinning. Nach DNS und Bodyvorbereitung wird die
Policy erneut geprüft, bevor Netzwerkfortschritt beginnt.

`RuntimeSupport` prüft die tatsächlich verwendeten SDK-Versionen
und öffentlichen Factory-/Handler-Verträge. Die Einmaligkeitsgrenze liest
oder verändert keine privaten SDK-Retryzähler. Eine echte neue Wiederholung
des Aufrufers benötigt eine neue Policyprüfung und eine neue Lease;
Policyfehler legitimieren keinen automatischen Retry.

`RuntimeSupport::assertTransportContracts()` prüft zusätzlich die
Form der privaten `HandlerStack.stack`-Inventarliste mit einem
einzelnen bekannten Middlewareeintrag. `GuardedClientFactory` liest
das geprüfte Inventar über Reflection, um Boundary-/Terminalposition und
Einmaligkeit zu kontrollieren. Unbekannte Inventarformen werden abgelehnt;
die Kopplung bleibt ausdrücklich Teil der SDK-Kompatibilitätsgrenze.

## Verworfene Alternativen {#adr-0018-alternatives}

-   **Privaten SDK-Retryzähler setzen:** hängt von internen SDK-Details ab.
-   **Nur Middleware vor dem ersten Send:** begrenzt versteckte weitere
    Handle-Erzeugungen innerhalb desselben terminalen Versuchs nicht.
-   **Factory nach Release oder Vorbereitungsausfall wieder freigeben:** öffnet
    die verbrauchte Lease für einen weiteren nativen Versuch.
-   **Mehrere Versuche mit demselben Pool oder derselben Lease erlauben:**
    überträgt eine frühere Prüfung auf einen neuen Verbindungsversuch.

## Konsequenzen {#adr-0018-consequences}

Eine Lease kann den Delegate höchstens einmal zur Handle-Erzeugung aufrufen.
Das ist eine konkrete Versuchsschranke, keine Zusage über die Anzahl einzelner
Netzwerkpakete oder die von cURL innerhalb eines Handles geprüften Pins.
Callback- und SDK-Retryfehler bleiben kontrollierte Fehler. Cleanup und
Cancellation gehören weiter zur Lease; unabhängige Transfers tragen eigene
Ressourcen und verzichten auf Verbindungswiederverwendung.

Kompatible SDK-Patches innerhalb der unterstützten Semantikbereiche brauchen
kein Extension-Release allein wegen ihrer Versionsnummer. Eine geänderte
öffentliche Schnittstelle oder ein nicht unterstützter Majorgraph wird vor
dem geschützten Transfer abgelehnt und erfordert erneute Qualifikation.
Auch eine Änderung der privaten Stack-Inventarform kann die geschützte
Integration stoppen. Das Projekt muss diese begrenzte Reflection-Kopplung
weiter mit realen SDK-Versionen qualifizieren.

## Quellen und Nachweis {#adr-0018-verification}

Die Umsetzung steht in
`Classes/HttpGuard/Transport/SingleUseCurlFactory.php`,
`Classes/HttpGuard/Transport/TransferLease.php` und
`Classes/HttpGuard/Transport/RuntimeSupport.php`. Die Stack-Inventarprüfung
steht zusätzlich in `Classes/HttpGuard/Client/GuardedClientFactory.php`.

`Tests/HttpGuard/Unit/Transport/SingleUseCurlFactoryTest.php` prüft
Create/Release, Vorbereitungsausfall und Wiedereintritt. Der Vergleich mit
Guzzles echtem öffentlichem `CurlFactory::finish()`-Pfad zeigt dort
eine zweite Handle-Erzeugung ohne Schranke und die Ablehnung mit Schranke.
Dieser Test erzeugt Handles, führt aber keinen Handler-Tick und kein
Netzwerk-I/O aus. Die getrennten Native-/Kontaktbelege sind unter
[Dokumentierte Prüfstände](https://docs.typo3.org/permalink/netresearch/nr-http-guard:verification-report@0.1.4) dokumentiert; die heutige Quellenprüfung dieses
ADRs ist kein neuer Testlauf und kein neuer Transportbeleg.

## Anlass für Neubewertung {#adr-0018-reassessment-trigger}

Änderungen der Factory-/Handler-Verträge oder privaten Inventarform,
alternative Retrypfade,
Verbindungspooling oder neue Transportschnittstellen verlangen erneute
Einmaligkeits-, Lifecycle- und echte Kontaktprüfungen. Eine behauptete
Kompatibilität aus Composer-Auflösung allein reicht dafür nicht aus.
