HttpClientAdapterGuzzle
extends HttpClientAdapter
in package
Adapter for the Guzzle HTTP client library.
Tags
Table of Contents
- NEEDED_AUTHNFO = ['basic' => ['username', 'password'], 'digest' => ['username', 'password'], 'bearer' => ['bearertoken']]
- Defines which credential attributes are required for auth mechanisms.
- GUZZLE_KNOWN_AUTHSCHEMES = ['basic', 'digest', 'ntlm']
- A list of authentication schemes that can be handled by Guzzle itself, independent on whether it works only with the Guzzle Curl HTTP handler or not. Strings must be lowercase!
- $baseUri : string
- The base URI for requests.
- $credentials : Credentials
- The credentials to use for authentication
- $authScheme : string|null
- The HTTP authentication scheme to use. Null if not determined yet.
- $client : Client
- The Client object of the Guzzle HTTP library.
- $failedAuthSchemes : array<int, string>
- HTTP authentication schemes tried without success, to avoid trying again.
- $known_authschemes : array<int, string>
- A list of authentication schemes that can be handled by this HttpClientAdapter.
- $schemeToCurlOpt : mixed
- Maps lowercase auth-schemes to their CURLAUTH_XXX constant. Only values not part of GUZZLE_KNOWN_AUTHSCHEMES are relevant here.
- __construct() : mixed
- Constructs a HttpClientAdapterGuzzle object.
- sendRequest() : ResponseInterface
- Sends a PSR-7 request and returns a PSR-7 response.
- checkCredentialsAvailable() : bool
- Checks if the needed credentials for an authentication scheme are available.
- checkSameDomainAsBase() : bool
- Checks whether the given URI has the same domain as the base URI of this HTTP client.
- getDomainFromSubdomain() : string
- Extracts the domain name from a subdomain.
- checkSabreCurlIncompatibility() : bool
- Checks if a request was rejected because of an incompatibility between curl and sabre/dav.
- getSupportedAuthSchemes() : array<int, string>
- Extracts HTTP authentication schemes from a WWW-Authenticate header.
- prepareGuzzleOptions() : array<string|int, mixed>
- Prepares options for the Guzzle request.
Constants
NEEDED_AUTHNFO
Defines which credential attributes are required for auth mechanisms.
protected
mixed
NEEDED_AUTHNFO
= ['basic' => ['username', 'password'], 'digest' => ['username', 'password'], 'bearer' => ['bearertoken']]
If a mechanism is not listed, it is assumed that no credentials are mandatory (e.g. GSSAPI/Kerberos).
GUZZLE_KNOWN_AUTHSCHEMES
A list of authentication schemes that can be handled by Guzzle itself, independent on whether it works only with the Guzzle Curl HTTP handler or not. Strings must be lowercase!
private
array<int, string>
GUZZLE_KNOWN_AUTHSCHEMES
= ['basic', 'digest', 'ntlm']
Tags
Properties
$baseUri
The base URI for requests.
protected
string
$baseUri
$credentials
The credentials to use for authentication
protected
Credentials
$credentials
$authScheme
The HTTP authentication scheme to use. Null if not determined yet.
private
string|null
$authScheme
$client
The Client object of the Guzzle HTTP library.
private
Client
$client
$failedAuthSchemes
HTTP authentication schemes tried without success, to avoid trying again.
private
array<int, string>
$failedAuthSchemes
= []
Tags
$known_authschemes
A list of authentication schemes that can be handled by this HttpClientAdapter.
private
array<int, string>
$known_authschemes
Tags
$schemeToCurlOpt
Maps lowercase auth-schemes to their CURLAUTH_XXX constant. Only values not part of GUZZLE_KNOWN_AUTHSCHEMES are relevant here.
private
static mixed
$schemeToCurlOpt
Methods
__construct()
Constructs a HttpClientAdapterGuzzle object.
public
__construct(string $base_uri, Credentials $credentials) : mixed
Parameters
- $base_uri : string
-
Base URI to be used when relative URIs are given to requests.
- $credentials : Credentials
-
Credentials used to authenticate with the server.
Return values
mixed —sendRequest()
Sends a PSR-7 request and returns a PSR-7 response.
public
sendRequest(string $method, string $uri[, array<string, mixed> $options = [] ]) : ResponseInterface
The given URI may be relative to the base URI given on construction of this object or a full URL. Authentication is only attempted in case the domain name of the request URI matches that of the base URI (subdomains may differ).
Parameters
- $method : string
-
The request method (GET, PROPFIND, etc.)
- $uri : string
-
The target URI. If relative, taken relative to the internal base URI of the HTTP client
- $options : array<string, mixed> = []
-
Options for the HTTP client, and default request options. May include any of the options accepted by HttpClientAdapter::sendRequest().
Tags
Return values
ResponseInterface —The response retrieved from the server.
checkCredentialsAvailable()
Checks if the needed credentials for an authentication scheme are available.
protected
checkCredentialsAvailable(string $scheme) : bool
Parameters
- $scheme : string
Return values
bool —True if the credentials needed for the scheme are available.
checkSameDomainAsBase()
Checks whether the given URI has the same domain as the base URI of this HTTP client.
protected
checkSameDomainAsBase(string $uri) : bool
If the given URI does not contain a domain part, true is returned (as when used, it will get that part from the base URI).
Parameters
- $uri : string
-
The URI to check
Return values
bool —True if the URI shares the same domain as the base URI.
getDomainFromSubdomain()
Extracts the domain name from a subdomain.
protected
static getDomainFromSubdomain(string $subdomain) : string
If the given string does not have a subdomain (i.e. top-level domain or domain only), it is returned as provided.
Parameters
- $subdomain : string
-
The subdomain (e.g. sub.example.com)
Return values
string —The domain of $subdomain (e.g. example.com)
checkSabreCurlIncompatibility()
Checks if a request was rejected because of an incompatibility between curl and sabre/dav.
private
checkSabreCurlIncompatibility(string $method, ResponseInterface $response) : bool
Background: When using DIGEST authentication, it is required to first send a request to the server to determine the parameters for the DIGEST authentication. This request is supposed to fail with 401 and the client can determine the parameters from the WWW-Authenticate header and try again with the proper Authentication header. Curl optimizes the first request by omitting the request body as it expects the request to fail anyway.
Now sabre/dav has a feature that allows to reply to certain REPORT requests without the need for authentication. This is specifically useful for Caldav, which may want to make available certain information from a calendar to anonymous users (e.g. free/busy time). Therefore, the authentication is done at a later time than the first attempt to evaluate the REPORT. A REPORT request requires a body, and thus sabre/dav will bail out with an internal server error instead of a 401, normally causing the client library to fail. The problem specifically only occurs for REPORT requests, for other requests such as PROPFIND the problem is not triggered in sabre and an expected 401 response is returned.
Read all about it here.
As a sidenote, nextcloud is not affected even though it uses sabre/dav, because the feature causing the server errors can be disabled and is in nextcloud. But there are other servers (Baïkal) using sabre/dav that are affected.
As a workaround, it is possible to ask curl to do negotiation of the authentication scheme to use, but providing the authentication scheme CURLAUTH_ANY. With this, curl will not assume that the initial request might fail (as not authentication may be needed), and thus the initial request will include the request body. The downside of this is that even when we know the authentication scheme supported by a server (e.g. basic), this setting will cause twice the number of requests being sent to the server.
Because it doesn't seem that this issue will get fixed, and the widespread usage of sabre/dav, I decided to include this workaround in the carddavclient library that specifically detects the situation and applies the above workaround without affecting the efficiency of communication when talking to other servers.
We detect the situation by the following indicators:
- We have the curl extension loaded
- REPORT request was sent
- Result status code is 500
- The server is a sabre/dav server (X-Sabre-Version header is set)
- The response includes the known error message:
<?xml version="1.0" encoding="utf-8"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:sabredav-version>4.1.2</s:sabredav-version> <s:exception>Sabre\Xml\ParseException</s:exception> <s:message>The input element to parse is empty. Do not attempt to parse</s:message> </d:error>
Parameters
- $method : string
- $response : ResponseInterface
Return values
bool —getSupportedAuthSchemes()
Extracts HTTP authentication schemes from a WWW-Authenticate header.
private
getSupportedAuthSchemes(ResponseInterface $response) : array<int, string>
The schemes offered by the server in the WWW-Authenticate header are intersected with those supported by Guzzle / curl. Schemes that have been tried with this object without success are filtered.
Parameters
- $response : ResponseInterface
-
A status 401 response returned by the server.
Tags
Return values
array<int, string> —An array of authentication schemes that can be tried.
prepareGuzzleOptions()
Prepares options for the Guzzle request.
private
prepareGuzzleOptions([array<string, mixed> $options = [] ][, bool $doAuth = false ]) : array<string|int, mixed>
Parameters
- $options : array<string, mixed> = []
- $doAuth : bool = false
-
True to attempt authentication. False will only try unauthenticated access.