Klienti tretích strán
Http::safe() chráni požiadavku, ktorú zostavíte vy. Knižnica, ktorá posiela požiadavky sama, potrebuje namiesto toho jedno z tohto:
| Knižnica… | Použite | Chránené |
|---|---|---|
| prijme hotového klienta Guzzle alebo klienta PSR-18 (Client z Guzzle je oboje) | SafeFetch::guzzleClient(array $config = [], ?DestinationPolicy $policy = null) | áno |
| zostavuje si vlastného klienta Guzzle a dovolí odovzdať handler | SafeFetch::handlerStack(?DestinationPolicy $policy = null) | áno |
| posiela cez fasádu Http z Laravelu vo vlastnom kóde | žiadny háčik — podedíte triedu, ktorá požiadavku zostavuje, alebo URL načítate sami (pozrite poznámky ku knižniciam) | len tam, kde volanie nahradíte |
Obe metódy overia, pripnú a skontrolujú každý krok presne ako Http::safe(): ten istý middleware ochrany, ten istý curl handler, ktorý ochrana vlastní, presmerovania sledované len podľa pravidiel, kontrola primary_ip po odoslaní a cookie jar pre každý krok. Ani jedna neuvoľňuje Http::safe(): setClient() alebo voľba handler na chránenej požiadavke Laravelu sa stále odmietne. Bez curl obe odmietnu s cannot_pin.
Chránený klient Guzzle
use RoundlyConsulting\SafeFetch\Facades\SafeFetch;
$client = SafeFetch::guzzleClient(['base_uri' => 'https://api.partner.example/', 'timeout' => 10]);
$response = $client->get('v1/items');
$client->post('v1/hooks', ['json' => ['url' => $tenantUrl]]);
// Redirects are the guard's: turn them on in the policy, never in the client config
$client = SafeFetch::guzzleClient(policy: SafeFetch::policy()->followRedirects());$config je konfigurácia klienta Guzzle. Dva kľúče sa odmietnu s UnsafeRequestOptions, pretože by požiadavky poslali mimo ochrany:
- handler — nahradil by chránený zásobník. Middleware namiesto toho pridajte do SafeFetch::handlerStack().
- allow_redirects s inou hodnotou než false — presmerovania sleduje ochrana sama a overí každý krok. Zapnite ich v pravidlách.
SafeFetch::guzzleClient(['handler' => $myStack]); // UnsafeRequestOptions — push middleware onto handlerStack() instead
SafeFetch::guzzleClient(['allow_redirects' => true]); // UnsafeRequestOptions — use the policy's followRedirects()Každá iná voľba klienta funguje ako pri obyčajnom klientovi Guzzle: http_errors (predvolene zapnuté, takže 4xx alebo 5xx vyhodí ClientException či ServerException z Guzzle), headers, auth, cookies => true, connect_timeout, on_stats, asynchrónne požiadavky a GuzzleHttpPool. Pri každej požiadavke ochrana odmietne to isté ako pri Http::safe(): proxy, ktorú ste zvolili (proxy z prostredia sa obchádzajú), surovú voľbu curl mimo zoznamu povolených, transport_sharing a sink pri nastavenom limite tela. Odmietnutie ochranou je DestinationRejected alebo DestinationUnavailable, vyhodené bez zmeny — pri asynchrónnej požiadavke ako zamietnutý promise.
Zásobník handlerov pre klienta, ktorého si zostaví knižnica
use GuzzleHttp\Client;
use RoundlyConsulting\SafeFetch\Facades\SafeFetch;
$stack = SafeFetch::handlerStack(SafeFetch::policy()->allowHosts(['*.partner.example']));
$client = new Client(['handler' => $stack, 'base_uri' => 'https://api.partner.example/']);Je to ten istý zásobník, cez ktorý ide chránená požiadavka Laravelu — vlastný curl handler ochrany, middleware ochrany, stub a recorder Laravelu — a nad ním tenká vrstva, ktorá predvolené nastavenia klienta Guzzle prevedie na to, s čím ochrana pracuje:
- Presmerovania vypnuté — obyčajný klient Guzzle sleduje presmerovania sám. Na tomto zásobníku dostane presmerovanie späť, ibaže ho sledujú pravidlá (followRedirects()), pričom sa každý krok overí a pripne.
- Cookie jar klienta sa odovzdá ochrane, ktorá ho uplatní pri každom kroku tak, ako to robia vlastné presmerovania Guzzle.
- Middleware, ktorý knižnica do zásobníka pridá cez push, je vnútri ochrany, tesne nad jej handlerom: beží pri každom kroku a požiadavku, ktorú po overení presmeruje (iná URL, hlavička Host), ochrana odmietne s cannot_pin skôr, než sa čokoľvek vytočí. Middleware pridaný cez unshift() je mimo nej a beží raz na volanie.
- Http::globalMiddleware() platí ako pri Http::safe(); Http::globalOptions() nie — platia vlastné voľby klienta. handler v Http::globalOptions() sa odmietne s UnsafeRequestOptions.
Limity
Obe metódy rešpektujú limity z pravidiel, takže request.max_bytes a request.timeout platia aj pre ne. Tu na tom záleží najviac: klient Guzzle predvolene nemá žiadny časový limit. Pri nastavenom request.timeout ho klient bez limitu dostane a dlhší sa naň skráti, vrátane všetkých krokov. Telo nad request.max_bytes sa oreže a označí — SafeFetch::truncated($response) prečíta aj odpoveď PSR-7. Pre jedno volanie: SafeFetch::policy()->timeout(10)->maxResponseBytes(1_048_576), alebo ich zrušíte cez timeout(null) a maxResponseBytes(null).
Súbežnosť a DNS rebinding
Každý krok sa preloží raz a curl sa pripne presne na posúdené adresy (CURLOPT_RESOLVE), takže odpoveď DNS, ktorá sa zmení medzi kontrolou a spojením, sa nikdy nevytočí; ďalšia požiadavka sa overí odznova. Súbežné požiadavky jedného klienta (getAsync(), GuzzleHttpPool) zdieľajú jeden curl multi handle ako členovia Http::pool(): každý krok nesie vlastné pripnutie, nové spojenie a žiadne opätovné použitie a dve súbežné požiadavky na rovnaký hostiteľ a port s rôznymi overenými adresami sa odmietnu, nie zmiešajú.
Testovanie
Http::fake() odpovedá na požiadavky odoslané cez obe metódy a zaznamenáva ich ako pri Http::safe(): platí Http::assertSent(), preventStrayRequests() aj stuby. Pod SafeFetch::fake() sa zaznamená každý krok (assertVetted()) a požiadavka, na ktorú neodpovedá žiadny stub Http::fake(), sa odmietne ešte pred vytočením.
Čo nie je zaručené
- Zásobník je len taký bezpečný ako jeho handler. Kód, ktorý naň zavolá setHandler() alebo ochranu odstráni cez remove(), ju vyradí z cesty; na Guzzle 7 nahradí celý zásobník aj voľba handler pri požiadavke (Guzzle 8 túto voľbu sám odmietne). Zásobník odovzdajte len kódu, ktorý ho použije ako handler.
- stream => true nestreamuje: handler ochrany je curl, ktorý najprv stiahne celé telo (odpoveď server-sent events príde, až keď ju server ukončí). Obmedzte ju cez request.max_bytes alebo pravidlá.
- Udalosť ResponseReceived z Laravelu sa pre tieto požiadavky nespustí; RequestSending áno.
laravel/mcp — podtrieda transportu
Overené na laravel/mcp 1.1.2 a laravel/ai 1.3.0 (Guzzle 8.3); po ich aktualizácii to overte znova. Ani jeden si nezostavuje vlastného klienta Guzzle — oba posielajú cez fasádu Http z Laravelu vo vlastných triedach. HttpTransport z laravel/mcp posiela každú správu JSON-RPC zo send() a reláciu ukončuje v terminateSession(), vždy cez Http::withHeaders(…). Podeďte ho a tieto dve požiadavky zostavte cez SafeFetch::http(); všetko ostatné je protected a použije sa bez zmeny:
final class GuardedMcpTransport extends HttpTransport
{
// send() and terminateSession(): HttpTransport's own bodies, with
// Http::withHeaders(...) replaced by
// SafeFetch::http($this->policy)->withHeaders(...).
}
$client = new WebClient(new GuardedMcpTransport($tenantMcpUrl));
$tools = $client->connect()->tools();
// A named client: registered through laravel/mcp's own factory hook, so Mcp::client('partner')
// and a queued or cached copy of it are rebuilt with the guarded transport.
Mcp::registerClient('partner', fn () => new WebClient(new GuardedMcpTransport($partnerUrl)));- Telá metód skopírujte z verzie laravel/mcp, ktorú používate, a pri aktualizácii ich skopírujte znova.
- Klienti definovaní v config/mcp.php používajú obyčajný transport — zaregistrujte ich namiesto toho cez Mcp::registerClient().
- Klient serializovaný bez mena sa znova zostaví s obyčajným transportom; pomenovaný cez factory z Mcp::registerClient().
- Objavovanie OAuth si adresy pripína samo a sleduje presmerovania Guzzle s kontrolou každého kroku; vnútri ochrany bežať nemôže, lebo tá presmerovania Guzzle odmieta.
laravel/ai — vzdialené súbory načítajte sami
- Požiadavky na poskytovateľov nemajú háčik pre jednotlivého poskytovateľa a ochrana nemôže byť celoaplikačný Http::globalMiddleware() (potrebuje vlastný handler a odmietala by presmerovania pri každej požiadavke v aplikácii). Ak môže tenant nastaviť základnú URL (OpenAI-kompatibilnú alebo Ollama), posúďte ju pri ukladaní — SafeFetch::vet($url) alebo pravidlo safe_url — a odchádzajúcu komunikáciu obmedzte aj na okraji siete; táto kontrola sa pri odoslaní nepripína.
- Vzdialené súbory (Image::fromUrl(), Document::fromUrl(), Audio::fromUrl()) idú cez vlastnú ochranu laravel/ai, ktorú za túto vymeniť nemožno. Ak chcete uplatniť svoje pravidlá — zoznam povolených, blokované rozsahy, tabuľku cloudových metadát — súbor načítajte sami a odovzdajte bajty (nižšie). Gateway, ktorá poskytovateľovi pošle priamo URL, na vašom serveri nič nenačíta.
- Bedrock zostavuje klienta AWS SDK s vlastným handlerom; háčik neexistuje a jeho koncové body patria AWS.
$fetched = SafeFetch::fetch($url, maxBytes: 10_000_000, accept: 'image/*');
$image = Image::fromBase64(base64_encode($fetched->body), $fetched->contentType);Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.