---
title: "Using clients"
manual: "HTTP Guard"
version: "main"
permalink: "https://docs.typo3.org/permalink/netresearch/nr-http-guard:api@main"
source: "Api/Index.rst"
rendered: "2026-10-18T14:31:38+00:00"
---

# Using clients {#api}

The public interfaces use the `\Netresearch\HttpGuard` namespace and
are provided through TYPO3 services. Applications should inject bound
PSR-18 clients or Public Fetch. The internal Guzzle client and its
progress and cancellation driver belong together; they do not provide
an additional public credential API.

## Ordinary Core requests {#api-core}

The registered Core path uses the public default policy. Existing
supported Core middleware and context restrictions remain effective
in addition. An ordinary call can look like this:

**Public retrieval with TYPO3 RequestFactory**

```php
$response = $requestFactory->request(
    'https://www.example.org/document.json',
    'GET',
    ['timeout' => 10, 'http_errors' => false],
);
```

`$requestFactory` is an injected
`\TYPO3\CMS\Core\Http\RequestFactory`. For public content from
untrusted URLs, [Public Fetch](https://docs.typo3.org/permalink/netresearch/nr-http-guard:api-public-fetch@main) provides a narrower API.
Configuring an endpoint profile does not grant internal access to
RequestFactory requests.

## Bound integration clients {#api-endpoint}

-   **class EndpointClientFactoryInterface**

    -   *Fully qualified name:* `\Netresearch\HttpGuard\EndpointClientFactoryInterface`

    -   **forEndpoint(string $configuredEndpointId)**

        Creates a client bound to a configured endpoint profile.
        Unknown, foreign or expired bindings are rejected.

The profile ID belongs in trusted service code or dependency injection
configuration. It is not taken from the current user request. The
following example uses the profile from [Internal endpoint example](https://docs.typo3.org/permalink/netresearch/nr-http-guard:configuration-example@main):

**Classes/Service/ErpClient.php in a sitepackage**

```php
<?php

declare (strict_types=1);
namespace Vendor\SitePackage\Service;

use GuzzleHttp\Psr7\Request;
use Netresearch\HttpGuard\EndpointClientFactoryInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\ResponseInterface;
final readonly class ErpClient
{
    private ClientInterface $client;
    public function __construct(EndpointClientFactoryInterface $factory)
    {
        $this->client = $factory->forEndpoint('erp-orders');
    }
    public function sendOrder(array $order): ResponseInterface
    {
        return $this->client->sendRequest(
            new Request(
                'POST',
                'https://erp.internal.example:8443/orders',
                ['Content-Type' => 'application/json'],
                json_encode($order, JSON_THROW_ON_ERROR)
            )
        );
    }
}

```

The client checks the origin, method and all resolved addresses on
every send. PSR-18 `sendRequest()` does not follow redirects and
returns HTTP error responses such as 404 or 500 as response objects.
Network errors and policy denials remain exceptions. Never bypass a
policy error with an unprotected replacement client.

## Public Fetch {#api-public-fetch}

-   **class PublicFetchClientInterface**

    -   *Fully qualified name:* `\Netresearch\HttpGuard\PublicFetchClientInterface`

    -   **fetch(Psr\\Http\\Message\\UriInterface $uri, string $method = 'GET')**

        Accepts only GET or HEAD and creates its own public request
        without inherited credentials or a body.

Public Fetch does not inherit authentication, cookies, client certificates,
SSL keys, custom headers, bodies or query defaults from another client.
Its fixed header list consists of `Accept`,
`Accept-Encoding` and `User-Agent`. The query of an
accepted target URI remains part of that URI and is not logged.
Permitted public redirects can reach another origin without forwarding
the secrets of an original integration request.

**Validate the raw URL before constructing a PSR-7 object**

```php
<?php

declare (strict_types=1);
namespace Vendor\SitePackage\Service;

use GuzzleHttp\Psr7\Uri;
use Netresearch\HttpGuard\PublicFetchClientInterface;
use Netresearch\HttpGuard\TargetNormalizer;
use Psr\Http\Message\ResponseInterface;
final readonly class PublicDocument
{
    public function __construct(private PublicFetchClientInterface $client)
    {
    }
    public function fetch(string $rawUrl): ResponseInterface
    {
        (new TargetNormalizer())->assertRawUri($rawUrl);
        return $this->client->fetch(new Uri($rawUrl));
    }
}

```

No endpoint ID is accepted for a user-driven fetch. Private targets
remain blocked even when the project has an internal endpoint profile
with the same hostname.

## Raw URLs and PSR-7 {#api-raw-uri}

`TargetNormalizer::assertRawUri(string $uri)` checks forbidden syntax
while the original string is still available. Reject fragments,
including an empty `#`, backslashes, control characters and
invalid raw forms before constructing a URI object.

A PSR-7 URI object may already have discarded an empty fragment marker
or encoded input. A later PSR-18 client cannot reconstruct that
information. The TYPO3 RequestFactory adapter therefore checks raw
strings before Guzzle. Custom PSR-18 applications validate the original
URL themselves, as shown in the Public Fetch example. The same boundary
applies to raw Vault resource URLs before constructing a
RequestInterface object.

## Handling policy errors {#api-errors}

`PolicyException` implements
`OutboundPolicyExceptionInterface`. Its `reasonCode()` method
returns a stable code from [Understanding denials](https://docs.typo3.org/permalink/netresearch/nr-http-guard:operations-reasons@main). Exception messages
contain no URLs, credentials or request bodies. Applications can use
the code for an understandable error message; do not include secrets
or complete requests in supplementary error logs.

## SDK options and streaming {#api-sdk-options}

Ordinary request bodies, headers and supported authentication remain
available in the controlled RequestFactory and SDK path.
`timeout` and `connect_timeout` accept finite,
non-negative numbers, including zero. `verify` accepts a
boolean or a readable CA bundle, subject to the TLS policy. Readable
client certificates and SSL keys enable mTLS. `sink` can
write to a file path, resource or PSR-7 stream without creating an
additional response copy inside the guard.

Supported callbacks such as `on_headers`, `on_stats`
and `progress` remain available; exceptions terminate the
associated transfer. HTTP 1.0, 1.1 and 2 are qualified; HTTP/3 is not.
The IP-family option can select only `v4` or `v6`
and does not expand address permissions.

Raw `curl` or cURL-multi options, `stream_context`,
Unix sockets, proxy routes, custom handlers, transport sharing,
delayed transport sends, debug dumps, unknown options and
`stream=true` are rejected in the protected path. A response
sink file does not provide an alternative stream HTTP handler.
Guzzle-internal Digest and NTLM control options, inspected at source
level for Guzzle 7, are distinguished from arbitrary raw cURL options.

Cancellation and incremental streaming require an adapter that uses
the associated internal progress driver. The recorded nr-vault reference
adapter does this for its existing APIs. Returning an arbitrary Guzzle client
alone does not guarantee that lifecycle binding.

## Optional nr-vault migration {#api-vault}

The extension does not read Vault secrets or automatically integrate
Vault. The historical reference patch targets its recorded nr-vault
source revision and requires deliberate integration in that project. Resource and
OAuth token origins require their own bound clients and, where
needed, their own profiles. A private token origin does not inherit
a public resource permission, or vice versa.

Secret retrieval, auditing, masking, size limits and Vault's existing
cancellation and streaming semantics remain the adapter's responsibility.
The [reference migration and recorded evidence](https://github.com/netresearch/typo3_http_guard/blob/3f929ae8794ca04ab3dd25627e94d601a2db23e0/integrations/nr-vault/EVIDENCE.md)
remain attached to that historical revision. This extension ships no Vault
overlay and does not deploy or freshly qualify nr-vault. Ordinary TYPO3
RequestFactory protection requires no Vault migration.
