Skip to content

Commit 416bc39

Browse files
committed
Added ElasticCurl transport adapter + updated composer
1 parent 8feae88 commit 416bc39

File tree

11 files changed

+166
-57
lines changed

11 files changed

+166
-57
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ util/doctum.phar
3535
# PHPUnit
3636
/phpunit.xml
3737
.phpunit.result.cache
38+
.phpunit.cache/
3839

3940
# Code coverage
4041
build

composer.json

+11-11
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@
1111
"license": "MIT",
1212
"require": {
1313
"php": "^8.1",
14-
"elastic/transport": "^8.10",
14+
"elastic/transport": "^9.0",
1515
"psr/http-client": "^1.0",
16-
"psr/http-message": "^1.1 || ^2.0",
17-
"psr/log": "^1|^2|^3",
18-
"guzzlehttp/guzzle": "^7.0"
16+
"psr/http-message": "^2.0",
17+
"psr/log": "^2.0|^3.0"
1918
},
2019
"require-dev": {
2120
"ext-yaml": "*",
2221
"ext-zip": "*",
23-
"mockery/mockery": "^1.5",
22+
"mockery/mockery": "^1.6",
2423
"phpstan/phpstan": "^2.1",
25-
"phpunit/phpunit": "^9.5",
26-
"symfony/finder": "~4.0",
27-
"nyholm/psr7": "^1.5",
28-
"php-http/mock-client": "^1.5",
29-
"symfony/http-client": "^5.0|^6.0|^7.0",
30-
"psr/http-factory" : "^1.0"
24+
"phpunit/phpunit": "^10.0",
25+
"symfony/finder": "^6.0",
26+
"nyholm/psr7": "^1.8",
27+
"php-http/mock-client": "^1.6",
28+
"psr/http-factory" : "^1.0",
29+
"guzzlehttp/guzzle": "^7.0",
30+
"symfony/http-client": "^6.0|^7.0"
3131
},
3232
"autoload": {
3333
"psr-4": {

phpunit.xml.dist

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" failOnRisky="true" verbose="true" beStrictAboutChangesToGlobalState="true" beStrictAboutOutputDuringTests="true" beStrictAboutTestsThatDoNotTestAnything="false">
3-
<coverage>
4-
<include>
5-
<directory suffix=".php">src</directory>
6-
</include>
7-
</coverage>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" failOnRisky="true" beStrictAboutChangesToGlobalState="true" beStrictAboutOutputDuringTests="true" beStrictAboutTestsThatDoNotTestAnything="false" cacheDirectory=".phpunit.cache">
83
<testsuites>
94
<testsuite name="Unit tests">
105
<directory>tests</directory>
@@ -18,4 +13,9 @@
1813
<group>cloud</group>
1914
</exclude>
2015
</groups>
16+
<source>
17+
<include>
18+
<directory suffix=".php">src</directory>
19+
</include>
20+
</source>
2121
</phpunit>

src/ClientBuilder.php

+26-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
use Elastic\Transport\NodePool\NodePoolInterface;
2626
use Elastic\Transport\Transport;
2727
use Elastic\Transport\TransportBuilder;
28-
use GuzzleHttp\Client as GuzzleHttpClient;
2928
use Http\Client\HttpAsyncClient;
3029
use Psr\Http\Client\ClientInterface;
3130
use Psr\Log\LoggerInterface;
@@ -393,7 +392,7 @@ public function build(): Client
393392
* Elastic cloud optimized with gzip
394393
* @see https://github.com/elastic/elasticsearch-php/issues/1241 omit for Symfony HTTP Client
395394
*/
396-
if (!empty($this->cloudId) && !$this->isSymfonyHttpClient($transport)) {
395+
if ((!empty($this->cloudId) || $this->isCloudOrServerless($this->hosts)) && !$this->isSymfonyHttpClient($transport)) {
397396
$transport->setHeader('Accept-Encoding', 'gzip');
398397
}
399398

@@ -404,6 +403,31 @@ public function build(): Client
404403
return $client;
405404
}
406405

406+
/**
407+
* Check if the hosts contains only one url and if it is from
408+
* Elastic Cloud or Serverless
409+
*/
410+
protected function isCloudOrServerless(array $hosts): bool
411+
{
412+
if (empty($hosts) || count($hosts)>1) {
413+
return false;
414+
}
415+
$url = $hosts[0];
416+
// Serverless
417+
if (preg_match('/\.elastic\.cloud/i', $url)) {
418+
return true;
419+
}
420+
// Elastic Cloud gcp
421+
if (preg_match('/\.cloud\.es\.io/i', $url)) {
422+
return true;
423+
}
424+
// Elastic Cloud aws or azure
425+
if (preg_match('/\.elastic-cloud\.com/i', $url)) {
426+
return true;
427+
}
428+
return false;
429+
}
430+
407431
/**
408432
* Returns true if the transport HTTP client is Symfony
409433
*/

src/Transport/Adapter/AdapterOptions.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ final class AdapterOptions
2222
const HTTP_ADAPTERS = [
2323
"GuzzleHttp\\Client" => "Elastic\\Elasticsearch\\Transport\\Adapter\\Guzzle",
2424
"Symfony\\Component\\HttpClient\\HttplugClient" => "Elastic\\Elasticsearch\\Transport\\Adapter\\Symfony",
25-
"Symfony\\Component\\HttpClient\\Psr18Client" => "Elastic\\Elasticsearch\\Transport\\Adapter\\Symfony"
25+
"Symfony\\Component\\HttpClient\\Psr18Client" => "Elastic\\Elasticsearch\\Transport\\Adapter\\Symfony",
26+
"Elastic\\Transport\\Client\\Curl" => "Elastic\\Elasticsearch\\Transport\\Adapter\\ElasticCurl"
2627
];
2728
}

src/Transport/Adapter/ElasticCurl.php

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Transport\Adapter;
16+
17+
use Elastic\Elasticsearch\Transport\RequestOptions;
18+
use Psr\Http\Client\ClientInterface;
19+
20+
class ElasticCurl implements AdapterInterface
21+
{
22+
public function setConfig(ClientInterface $client, array $config, array $clientOptions): ClientInterface
23+
{
24+
$curlConfig = [];
25+
foreach ($config as $key => $value) {
26+
switch ($key) {
27+
case RequestOptions::SSL_CERT:
28+
$curlConfig[CURLOPT_SSLCERT] = $value;
29+
break;
30+
case RequestOptions::SSL_KEY:
31+
$curlConfig[CURLOPT_SSH_PRIVATE_KEYFILE] = $value;
32+
break;
33+
case RequestOptions::SSL_VERIFY:
34+
$curlConfig[CURLOPT_SSL_VERIFYPEER] = $value;
35+
break;
36+
case RequestOptions::SSL_CA:
37+
$curlConfig[CURLOPT_CAINFO] = $value;
38+
}
39+
}
40+
$class = get_class($client);
41+
return new $class(array_replace($clientOptions, $curlConfig));
42+
}
43+
}

tests/ClientBuilderTest.php

+6-30
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Elastic\Elasticsearch\Exception\AuthenticationException;
2020
use Elastic\Elasticsearch\Exception\ConfigException;
2121
use Elastic\Elasticsearch\Exception\InvalidArgumentException;
22+
use Elastic\Transport\Exception\CloudIdParseException;
2223
use Elastic\Transport\NodePool\NodePoolInterface;
2324
use Http\Client\HttpAsyncClient;
2425
use Nyholm\Psr7\Factory\Psr17Factory;
@@ -52,7 +53,7 @@ public function testCreate()
5253
$this->assertInstanceOf(ClientBuilder::class, $this->builder);
5354
}
5455

55-
public function getConfig()
56+
public static function getConfig()
5657
{
5758
return [
5859
[[
@@ -227,36 +228,11 @@ public function testSetElasticCloudId()
227228
$this->assertEquals($this->builder, $result);
228229
}
229230

230-
public function getCloudIdExamples()
231+
public function testSetElasticCloudIdThrowsException()
231232
{
232-
return [
233-
['cluster:d2VzdGV1cm9wZS5henVyZS5lbGFzdGljLWNsb3VkLmNvbTo5MjQzJGM2NjM3ZjMxMmM1MjQzY2RhN2RlZDZlOTllM2QyYzE5JA==', 'c6637f312c5243cda7ded6e99e3d2c19.westeurope.azure.elastic-cloud.com', 9243],
234-
['cluster:d2VzdGV1cm9wZS5henVyZS5lbGFzdGljLWNsb3VkLmNvbSRlN2RlOWYxMzQ1ZTQ0OTAyODNkOTAzYmU1YjZmOTE5ZSQ=', 'e7de9f1345e4490283d903be5b6f919e.westeurope.azure.elastic-cloud.com', null],
235-
['cluster:d2VzdGV1cm9wZS5henVyZS5lbGFzdGljLWNsb3VkLmNvbSQ4YWY3ZWUzNTQyMGY0NThlOTAzMDI2YjQwNjQwODFmMiQyMDA2MTU1NmM1NDA0OTg2YmZmOTU3ZDg0YTZlYjUxZg==', '8af7ee35420f458e903026b4064081f2.westeurope.azure.elastic-cloud.com', null]
236-
];
237-
}
238-
239-
/**
240-
* @dataProvider getCloudIdExamples
241-
*/
242-
public function testSetCloudIdWithExamples(string $cloudId, string $url, ?int $port)
243-
{
244-
$this->builder->setElasticCloudId($cloudId);
245-
246-
$response = $this->psr17Factory->createResponse(200);
247-
$this->httpClient->method('sendRequest')
248-
->willReturn($response);
249-
$this->builder->setHttpClient($this->httpClient);
250-
251-
$client = $this->builder->build();
252-
$this->assertInstanceOf(Client::class, $client);
253-
254-
$transport = $client->getTransport();
255-
$request = $this->psr17Factory->createRequest('GET', '');
256-
$transport->sendRequest($request);
257-
258-
$this->assertEquals($url, $transport->getLastRequest()->getUri()->getHost());
259-
$this->assertEquals($port, $transport->getLastRequest()->getUri()->getPort());
233+
$this->builder->setElasticCloudId('xxx');
234+
$this->expectException(CloudIdParseException::class);
235+
$client = $this->builder->build();
260236
}
261237

262238
public function testSetRetries()

tests/Integration/AliasTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function setUp(): void
3838
$this->client = Utility::getClient();
3939
}
4040

41-
public function getIndexNames(): array
41+
public static function getIndexNames(): array
4242
{
4343
$data = [];
4444
foreach (self::NAME_INDICES as $index) {

tests/Integration/BasicTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public function testInfo()
4141
/**
4242
* Get the stock market values
4343
*/
44-
public function getDocuments(): array
44+
public static function getDocuments(): array
4545
{
4646
$docs = [];
4747
foreach (file(__DIR__ . '/stocks.csv', FILE_IGNORE_NEW_LINES) as $line) {

tests/Traits/EndpointTraitTest.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class EndpointTraitTest extends TestCase
2424
{
2525
use EndpointTrait;
2626

27-
public function getQueryStrings(): array
27+
public static function getQueryStrings(): array
2828
{
2929
return [
3030
['http://localhost:9200', ['refresh' => true], ['refresh'], 'http://localhost:9200?refresh=true'],
@@ -43,7 +43,7 @@ public function testAddQueryString(string $url, array $params, array $keys, stri
4343
$this->assertEquals($expected, $result);
4444
}
4545

46-
public function getBodySerialized(): array
46+
public static function getBodySerialized(): array
4747
{
4848
return [
4949
[[ 'foo' => 'bar'], 'application/json', '{"foo":"bar"}'],
@@ -66,7 +66,7 @@ public function testBodySerializeUnknownContentTypeThrowsException()
6666
$this->bodySerialize(['foo' => 'bar'], 'Unknown-content-type');
6767
}
6868

69-
public function getCompatibilityHeaders()
69+
public static function getCompatibilityHeaders()
7070
{
7171
return [
7272
[['Content-Type' => 'application/json'], ['Content-Type' => sprintf(Client::API_COMPATIBILITY_HEADER, 'application', 'json')]],
@@ -96,7 +96,7 @@ public function testBuildCompatibilityHeaders(array $input, array $expected)
9696
$this->assertEquals($expected, $this->buildCompatibilityHeaders($input));
9797
}
9898

99-
public function getRequestParts(): array
99+
public static function getRequestParts(): array
100100
{
101101
return [
102102
[ 'GET', 'http://localhost:9200', ['Foo' => 'bar', 'Content-Type' => 'application/json'], []],
@@ -133,7 +133,7 @@ public function testCreateRequest(string $method, string $url, array $headers, $
133133
}
134134
}
135135

136-
public function getRequiredParams(): array
136+
public static function getRequiredParams(): array
137137
{
138138
return [
139139
[['index'], ['index' => '1'], false],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Elasticsearch PHP Client
4+
*
5+
* @link https://github.com/elastic/elasticsearch-php
6+
* @copyright Copyright (c) Elasticsearch B.V (https://www.elastic.co)
7+
* @license https://opensource.org/licenses/MIT MIT License
8+
*
9+
* Licensed to Elasticsearch B.V under one or more agreements.
10+
* Elasticsearch B.V licenses this file to you under the MIT License.
11+
* See the LICENSE file in the project root for more information.
12+
*/
13+
declare(strict_types = 1);
14+
15+
namespace Elastic\Elasticsearch\Tests\Transport\Adapter;
16+
17+
use Elastic\Elasticsearch\Transport\Adapter\AdapterInterface;
18+
use Elastic\Elasticsearch\Transport\Adapter\ElasticCurl;
19+
use Elastic\Elasticsearch\Transport\RequestOptions;
20+
use Elastic\Transport\Client\Curl;
21+
use PHPUnit\Framework\TestCase;
22+
use Psr\Http\Client\ClientInterface;
23+
24+
class ElasticCurlTest extends TestCase
25+
{
26+
protected AdapterInterface $curlAdapter;
27+
protected ClientInterface $client;
28+
29+
public function setUp(): void
30+
{
31+
$this->curlAdapter = new ElasticCurl;
32+
$this->client = $this->createStub(ClientInterface::class);
33+
}
34+
35+
public function testSetConfigWithEmptyArray()
36+
{
37+
$result = $this->curlAdapter->setConfig($this->client, [], []);
38+
$this->assertInstanceOf(ClientInterface::class, $result);
39+
}
40+
41+
public function testSetConfigWithSslCert()
42+
{
43+
$client = $this->curlAdapter->setConfig(new Curl(), [ RequestOptions::SSL_CERT => 'test'], []);
44+
$this->assertInstanceOf(Curl::class, $client);
45+
}
46+
47+
public function testSetConfigWithSslKey()
48+
{
49+
$client = $this->curlAdapter->setConfig(new Curl(), [ RequestOptions::SSL_KEY => 'test'], []);
50+
$this->assertInstanceOf(Curl::class, $client);
51+
}
52+
53+
public function testSetConfigWithSslVerify()
54+
{
55+
$client = $this->curlAdapter->setConfig(new Curl(), [ RequestOptions::SSL_VERIFY => false], []);
56+
$this->assertInstanceOf(Curl::class, $client);
57+
}
58+
59+
public function testSetConfigWithSslCa()
60+
{
61+
$client = $this->curlAdapter->setConfig(new Curl(), [ RequestOptions::SSL_CA => 'test'], []);
62+
$this->assertInstanceOf(Curl::class, $client);
63+
}
64+
}

0 commit comments

Comments
 (0)