9

I'm searching a way to configure Drupal's HTTP Client Guzzle. I.e. the base_url and user agent.

The Guzzle docs provide info here: http://docs.guzzlephp.org/en/latest/quickstart.html#creating-a-client

But nothing that works with the Drupal implementation.

I can see in core/lib/Drupal/Core/Http/ClientFactory.php that there is a default config with which the Guzzle Client constructor is fed.

//Line 46-65:

public function fromOptions(array $config = []) {
    $default_config = [
      // Security consideration: we must not use the certificate authority
      // file shipped with Guzzle because it can easily get outdated if a
      // certificate authority is hacked. Instead, we rely on the certificate
      // authority file provided by the operating system which is more likely
      // going to be updated in a timely fashion. This overrides the default
      // path to the pem file bundled with Guzzle.
      'verify' => TRUE,
      'timeout' => 30,
      'headers' => [
        'User-Agent' => 'Drupal/' . \Drupal::VERSION . ' (+https://www.drupal.org/) ' . \GuzzleHttp\default_user_agent(),
      ],
      'handler' => $this->stack,
    ];

    $config = NestedArray::mergeDeep($default_config, Settings::get('http_client_config', []), $config);

    return new Client($config);
  }

Any way to reconfigure this?

\Drupal::httpClient() does not provide any parameters for configuration.

2 Answers 2

8

You may add your own factory and corresponding service.

# Service definition in YAML.
services:
 my_module.client:
   class: GuzzleHttp\Client
   factory: my_module.client.factory:get
 my_module.client.factory:
   class: MyModule\ClientFactory

And the corresponding PHP (example).

class ClientFactory {

  /**
   * Return a configured Client object.
   */
  public function get() {
    $config = [
      'base_url' => 'https://example.com',
    ];

    $client = new Client($config);

    return $client;
  }
}

However you can set the URL and Header in the request itself so this is not actually necessary. See Request Options.

$options = [
  'headers' => [
    'User-Agent' => 'Foobar 1.0'
  ]
];
$response = $client->request('GET', 'https://example.com/api/endpoint', $options);

One reason why you might want to use a factory is to inject the ConfigFactory or LoggerFactory to add various options.

However in a unit test it probably makes sense not to call the class from the service container so that you may add the quirky MockHandler (Guzzle developers decided on a non-standard way of mocking because).

$mock = new MockHandler([new Response(200, ['Content-Length' => 0])]);
$options = [
  'handler' => HandlerStack::create($mock)
];
$client = new \GuzzleHttp\Client($options);

My preference is to create the factory class. Add a unit test for the factory class that correctly instantiates, and then add unit tests that create the client class directly if I'm testing a custom client that extends GuzzleHttp\Client.

As suggested by gapple in their answer, you may also forgo creating your own Factory class, and instead use the http_client_factory service provided by Drupal 8 core to create a one-off custom Client via the ClientFactory::fromOptions() method. Or you can add default configuration to your settings.php file under $settings['http_client_config'].

1
  • Works perfect and solved my problem! Thanks mate :-)
    – Hsnbrg
    Nov 20, 2015 at 10:29
11

There is no need to create your own Client or ClientFactory service.

Because ClientFactory::fromOptions() has

$config = NestedArray::mergeDeep($default_config, Settings::get('http_client_config', []), $config);

You can specify default options to Guzzle for all clients created by Drupal via your settings.php. Core only provides an example of proxy settings, but you could alter the user agent via

$settings['http_client_config']['headers']['User-Agent'] = 'Foobar 1.0';

To create a single client with different options, (which will override settings from both ClientFactory::fromOptions() and settings.php), you can use the factory service directly:

\Drupal::service('http_client_factory')->fromOptions([
  'headers' => ['User-Agent' => 'Foobar 1.0'],
]);
3
  • Thanks, exactly what I was looking for to prevent Guzzle from using the system proxy by default !
    – Eria
    Aug 31, 2017 at 16:02
  • $settings['http_client_config']['force_ip_resolve'] = 'v4'; - use case: force Guzzle to use IPv4 for Drupal core media remote video - because YouTube rejects all/many non-API oEmbed requests over IPv6 with 429 Too many requests (due to spam) Nov 15, 2019 at 14:53
  • i was looking for this a long time. Thank you! Its quite irritating that it it is not documented that the Drupal::httpClient does not behave like the Guzzle client
    – makkus
    Jun 9, 2020 at 13:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.