HttpTransport
HTTP transport used by the ArangoDB client.
Wraps a Guzzle Client and composes it with three policies:
- HostRing — round-robin failover over the configured endpoints,
- RetryPolicy — capped exponential back-off on transient errors,
- ClientOptions — base configuration (auth, timeout, connection mode, target database, …).
The transport is intentionally low-level: it speaks JSON, returns HttpResponse value objects, and never throws non-Arango exceptions — every failure surfaces as a subclass of ArangoException.
The Guzzle client can be injected for testing (with a MockHandler).
Runtime auth state — the transport itself is not readonly because
login(), setBearerToken() and setBasicAuth()
mutate the active auth scheme + credentials between requests
($current* properties), and the 401 auto-refresh path inside
request() mutates them too. The injected ClientOptions
remains immutable; the runtime auth state is seeded from it at
construction time and becomes the source of truth afterwards.
Tags
Table of Contents
Constants
- ARANGO_DIRTY_READ_HEADER : string = 'x-arango-allow-dirty-read'
- Header that opts a request into ArangoDB's dirty-read mode on a cluster deployment. When the transport's {@see ClientOptions::$allowDirtyRead} is true, every outbound request carries this header set to {@see \oihana\enums\Boolean::TRUE} so the coordinator may serve reads from any follower.
- ARANGO_TRX_ID_HEADER : string = 'x-arango-trx-id'
- Header that scopes a request to a running streaming transaction.
- DATABASE_PATH_PREFIX : string = '/_db/'
- URL prefix used to scope a request to a specific database (ArangoDB convention).
Properties
- $hostRing : HostRing
- Round-robin ring over the configured server endpoints.
- $options : ClientOptions
- $retryPolicy : RetryPolicy
- Retry policy applied on transient failures.
- $activeTransactionId : string|null
- Active streaming-transaction id, used as a fallback by {@see request()} when no explicit `$transactionId` is passed.
- $currentAuthType : string
- Currently active auth scheme (`BASIC` or `JWT`). Seeded from {@see ClientOptions::$authType}; mutated by {@see setBearerToken()}, {@see setBasicAuth()}, {@see login()} and the auto-refresh on 401.
- $currentBasicPassword : string|null
- Currently active basic-auth password. Seeded from {@see ClientOptions::$password}. See {@see $currentBasicUser}.
- $currentBasicUser : string|null
- Currently active basic-auth user. Seeded from {@see ClientOptions::$user}; mutated by {@see setBasicAuth()}. Kept around even when running in JWT mode, so the transport can refresh the JWT on 401 by re-logging in.
- $currentBearerToken : string|null
- Currently active bearer token (JWT). Seeded from {@see ClientOptions::$token}; mutated by {@see setBearerToken()}, {@see login()} and the auto-refresh on 401.
- $httpClient : Client
- Underlying Guzzle HTTP client.
- $refreshingAuth : bool
- Guard against re-entering the 401 refresh path from inside the refresh request itself (which would loop forever).
Methods
- __construct() : mixed
- login() : string
- Authenticates against ArangoDB by posting `{username, password}` to the unauthenticated `/_open/auth` endpoint and stores the returned JWT in the transport's runtime auth state. Returns the JWT for the caller's convenience (e.g. when it needs to forward it to another client / cache it elsewhere).
- request() : HttpResponse
- Sends an HTTP request to the ArangoDB server and returns the parsed response.
- setBasicAuth() : void
- Switches the transport to Basic auth with the given credentials.
- setBearerToken() : void
- Switches the transport to JWT/Bearer mode with the given token.
- withActiveTransactionId() : mixed
- Runs `$callback` with `$id` installed as the active streaming transaction id on this transport, then restores the previous value (including on exception).
- buildAuthHeader() : string|null
- Builds the Authorization header value from the current runtime auth state (which is seeded from {@see ClientOptions} at construction time and mutated by {@see setBearerToken()} / {@see setBasicAuth()} / {@see login()} / the auto-refresh on 401).
- buildUrl() : string
- Builds the absolute URL for a given API path.
- decodeBody() : mixed
- Decodes a JSON body string into PHP, returning null when the body is empty or cannot be parsed.
- defaultHeaders() : array<string, string>
- Builds the default headers attached to every request (auth + content negotiation + connection mode).
- guzzleConfig() : array<string, mixed>
- Builds the static Guzzle configuration shared across all requests.
- mapBadResponse() : ArangoException
- Wraps a Guzzle 4xx/5xx response into the appropriate ArangoException subclass (Conflict / Maintenance / generic Http).
- mapResponse() : HttpResponse
- Wraps a successful Guzzle response (2xx) into an {@see HttpResponse} value object.
- mergeOptions() : array<string, mixed>
- Merges per-request options (body, query string, headers) into the shape expected by Guzzle's `request()` method.
Constants
ARANGO_DIRTY_READ_HEADER
Header that opts a request into ArangoDB's dirty-read mode on a cluster deployment. When the transport's {@see ClientOptions::$allowDirtyRead} is true, every outbound request carries this header set to {@see \oihana\enums\Boolean::TRUE} so the coordinator may serve reads from any follower.
private
string
ARANGO_DIRTY_READ_HEADER
= 'x-arango-allow-dirty-read'
Tags
ARANGO_TRX_ID_HEADER
Header that scopes a request to a running streaming transaction.
private
string
ARANGO_TRX_ID_HEADER
= 'x-arango-trx-id'
Set by the per-request $transactionId parameter of request();
the server matches the id against an active transaction and applies
the operation in that transactional context.
Tags
DATABASE_PATH_PREFIX
URL prefix used to scope a request to a specific database (ArangoDB convention).
private
string
DATABASE_PATH_PREFIX
= '/_db/'
Not exposed through ArangoRoute because it is not a route — it is an URL-assembly artefact that the transport prepends to every database-scoped request in buildUrl().
Tags
Properties
$hostRing read-only
Round-robin ring over the configured server endpoints.
public
HostRing
$hostRing
$options read-only
public
ClientOptions
$options
$retryPolicy read-only
Retry policy applied on transient failures.
public
RetryPolicy
$retryPolicy
$activeTransactionId
Active streaming-transaction id, used as a fallback by {@see request()} when no explicit `$transactionId` is passed.
private
string|null
$activeTransactionId
= null
Set by withActiveTransactionId() (which always reverts the
previous value in a finally block, including on exception),
so this slot is never left dangling pointing at a transaction
that has been committed or aborted.
The per-request $transactionId parameter of request()
still wins when both are set — the active id is only the
fallback for operations that don't pass one explicitly (e.g. a
Collection::insert() call invoked from inside a
Transaction::step() callback).
$currentAuthType
Currently active auth scheme (`BASIC` or `JWT`). Seeded from {@see ClientOptions::$authType}; mutated by {@see setBearerToken()}, {@see setBasicAuth()}, {@see login()} and the auto-refresh on 401.
private
string
$currentAuthType
$currentBasicPassword
Currently active basic-auth password. Seeded from {@see ClientOptions::$password}. See {@see $currentBasicUser}.
private
string|null
$currentBasicPassword
$currentBasicUser
Currently active basic-auth user. Seeded from {@see ClientOptions::$user}; mutated by {@see setBasicAuth()}. Kept around even when running in JWT mode, so the transport can refresh the JWT on 401 by re-logging in.
private
string|null
$currentBasicUser
$currentBearerToken
Currently active bearer token (JWT). Seeded from {@see ClientOptions::$token}; mutated by {@see setBearerToken()}, {@see login()} and the auto-refresh on 401.
private
string|null
$currentBearerToken
$httpClient read-only
Underlying Guzzle HTTP client.
private
Client
$httpClient
$refreshingAuth
Guard against re-entering the 401 refresh path from inside the refresh request itself (which would loop forever).
private
bool
$refreshingAuth
= false
Methods
__construct()
public
__construct(ClientOptions $options[, Client|null $httpClient = null ][, RetryPolicy|null $retryPolicy = null ][, HostRing|null $hostRing = null ]) : mixed
Parameters
- $options : ClientOptions
-
Connection options (auth, timeout, endpoints, …).
- $httpClient : Client|null = null
-
Optional Guzzle client; defaults to a new client built from $options. Inject a mocked client for tests.
- $retryPolicy : RetryPolicy|null = null
-
Optional retry policy; defaults to RetryPolicy with built-in defaults.
- $hostRing : HostRing|null = null
-
Optional host ring; defaults to a ring built from
$options->endpoints.
login()
Authenticates against ArangoDB by posting `{username, password}` to the unauthenticated `/_open/auth` endpoint and stores the returned JWT in the transport's runtime auth state. Returns the JWT for the caller's convenience (e.g. when it needs to forward it to another client / cache it elsewhere).
public
login(string $user, string $password) : string
The basic credentials are stored alongside the JWT so the transport can refresh it on 401 by re-logging in.
Parameters
- $user : string
- $password : string
Tags
Return values
string —The JWT returned by the server.
request()
Sends an HTTP request to the ArangoDB server and returns the parsed response.
public
request(string $method, string $path[, array<string, mixed>|string|null $body = null ][, array<string, mixed> $query = [] ][, array<string, string> $headers = [] ][, string|null $databaseOverride = null ][, string|null $transactionId = null ]) : HttpResponse
On transient failures (network errors, write-write conflict, cluster maintenance, …) the request is retried according to the configured RetryPolicy. When multiple endpoints are configured the ring cursor is advanced between attempts.
Parameters
- $method : string
-
HTTP verb (
GET,POST, …). - $path : string
-
API path beginning with
/. Prefixed with/_db/{database}according to$databaseOverride. - $body : array<string, mixed>|string|null = null
-
Request body. When an array is passed the transport serialises it as JSON; when a string is passed it is sent verbatim as the raw HTTP body (use this for
/_api/importJSON Lines payloads — caller is then responsible for theContent-Typeheader). Pass null for verbs without a body. - $query : array<string, mixed> = []
-
Query string parameters.
- $headers : array<string, string> = []
-
Extra headers (merged with the per-request defaults).
- $databaseOverride : string|null = null
-
Controls the
/_db/{name}prefix:null(default) — use$options->databasewhen set, otherwise no prefix;''(empty string) — no prefix (global server route, e.g./_api/database);- non-empty — use the given database name (overrides the options).
- $transactionId : string|null = null
-
Optional streaming transaction id to scope this request to. When non-null, the
x-arango-trx-id: {id}header is added so the server attaches the operation to the running transaction. Passed explicitly rather than via$headersto keep the transaction surface typed and testable.
Tags
Return values
HttpResponsesetBasicAuth()
Switches the transport to Basic auth with the given credentials.
public
setBasicAuth(string $user, string $password) : void
Subsequent requests carry Authorization: basic base64(user:password).
Any bearer token previously set is cleared so the basic credentials
take effect.
Parameters
- $user : string
- $password : string
setBearerToken()
Switches the transport to JWT/Bearer mode with the given token.
public
setBearerToken(string|null $token) : void
Subsequent requests carry Authorization: bearer <token>. The basic
credentials (if any) are kept around so the transport can refresh
the JWT on 401 by re-logging in.
Pass null to revert to the basic credentials (or to anonymous mode
when none are configured).
Parameters
- $token : string|null
-
JWT to send on subsequent requests.
withActiveTransactionId()
Runs `$callback` with `$id` installed as the active streaming transaction id on this transport, then restores the previous value (including on exception).
public
withActiveTransactionId(string|null $id, callable(): mixed $callback) : mixed
While the callback runs, any request() that does not pass
an explicit $transactionId falls back to $id — so a
Collection::insert() (or any other plain CRUD call) invoked
from inside the callback is automatically scoped to the
running transaction. This is how Transaction::step()
makes the transactional context transparent to existing code
that does not know about transactions.
Nesting is supported: an inner withActiveTransactionId()
temporarily overrides the outer id, and reverts it on exit.
Pass null for $id to explicitly run a callback outside
any current transaction (e.g. to make a side-channel admin call
that must not be part of the user's transaction).
Parameters
- $id : string|null
-
Transaction id to scope the callback to (or
nullto suspend any active scope). - $callback : callable(): mixed
-
User-provided block.
Tags
Return values
mixed —The value returned by $callback.
buildAuthHeader()
Builds the Authorization header value from the current runtime auth state (which is seeded from {@see ClientOptions} at construction time and mutated by {@see setBearerToken()} / {@see setBasicAuth()} / {@see login()} / the auto-refresh on 401).
private
buildAuthHeader() : string|null
Return values
string|null —The header value (e.g. Basic dXNlcjpwYXNz or Bearer eyJ…), or null when no credentials are configured (anonymous mode).
buildUrl()
Builds the absolute URL for a given API path.
private
buildUrl(string $endpoint, string $path[, string|null $databaseOverride = null ]) : string
The /_db/{database} prefix is applied based on $databaseOverride:
nullfalls back to$options->database,- the empty string skips the prefix entirely (global server route),
- any other value is used as the database name verbatim.
Parameters
- $endpoint : string
-
Base endpoint URL (already normalised by HostRing).
- $path : string
-
API path (with or without a leading
/). - $databaseOverride : string|null = null
-
Optional database scope override (see above).
Return values
string —Absolute URL ready to be passed to the Guzzle client.
decodeBody()
Decodes a JSON body string into PHP, returning null when the body is empty or cannot be parsed.
private
decodeBody(string $raw) : mixed
Parameters
- $raw : string
-
Raw response body.
Return values
mixed —Decoded value (array / scalar / null) or null on parse failure.
defaultHeaders()
Builds the default headers attached to every request (auth + content negotiation + connection mode).
private
defaultHeaders() : array<string, string>
Return values
array<string, string>guzzleConfig()
Builds the static Guzzle configuration shared across all requests.
private
guzzleConfig() : array<string, mixed>
Default headers are intentionally applied per request (in mergeOptions()) rather than at client construction time, so that an externally injected Client (for instance a mocked one in tests) still receives the transport's auth + content negotiation headers.
Return values
array<string, mixed>mapBadResponse()
Wraps a Guzzle 4xx/5xx response into the appropriate ArangoException subclass (Conflict / Maintenance / generic Http).
private
mapBadResponse(BadResponseException $e) : ArangoException
Parameters
- $e : BadResponseException
-
The Guzzle exception raised on a 4xx/5xx response.
Return values
ArangoException —A typed Arango exception ready to be thrown by request().
mapResponse()
Wraps a successful Guzzle response (2xx) into an {@see HttpResponse} value object.
private
mapResponse(ResponseInterface $response) : HttpResponse
Parameters
- $response : ResponseInterface
-
PSR-7 response from Guzzle.
Return values
HttpResponse —Decoded response carrying status, headers, body and raw payload.
mergeOptions()
Merges per-request options (body, query string, headers) into the shape expected by Guzzle's `request()` method.
private
mergeOptions(array<string, mixed>|string|null $body, array<string, mixed> $query, array<string, string> $headers) : array<string, mixed>
The body type drives the wire encoding:
array— sent as JSON via GuzzleOption::JSON (Guzzle adds theContent-Type: application/jsonheader).string— sent verbatim via GuzzleOption::BODY (used for/_api/importJSON Lines payloads; the caller is responsible for theContent-Typeheader).null— no body emitted.
Parameters
- $body : array<string, mixed>|string|null
- $query : array<string, mixed>
- $headers : array<string, string>