Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.x] Add a default cURL HTTP client #1589

Merged
merged 22 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,14 @@
"php": "^7.2|^8.0",
"ext-json": "*",
"ext-mbstring": "*",
"guzzlehttp/promises": "^1.5.3|^2.0",
"ext-curl": "*",
"jean85/pretty-package-versions": "^1.5|^2.0.4",
"php-http/async-client-implementation": "^1.0",
"php-http/client-common": "^1.5|^2.0",
"php-http/discovery": "^1.15",
"php-http/httplug": "^1.1|^2.0",
"php-http/message": "^1.5",
"php-http/message-factory": "^1.1",
"psr/http-factory": "^1.0",
"psr/http-factory-implementation": "^1.0",
"psr/log": "^1.0|^2.0|^3.0",
"symfony/options-resolver": "^3.4.43|^4.4.30|^5.0.11|^6.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.19|3.4.*",
"guzzlehttp/promises": "^1.0|^2.0",
"guzzlehttp/psr7": "^1.8.4|^2.1.1",
"http-interop/http-factory-guzzle": "^1.0",
"monolog/monolog": "^1.6|^2.0|^3.0",
Expand Down
65 changes: 20 additions & 45 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -35,51 +35,6 @@ parameters:
count: 1
path: src/Dsn.php

-
message: "#^Access to constant CONNECT_TIMEOUT on an unknown class GuzzleHttp\\\\RequestOptions\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Access to constant PROXY on an unknown class GuzzleHttp\\\\RequestOptions\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Access to constant TIMEOUT on an unknown class GuzzleHttp\\\\RequestOptions\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Call to static method create\\(\\) on an unknown class Symfony\\\\Component\\\\HttpClient\\\\HttpClient\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Call to static method createWithConfig\\(\\) on an unknown class Http\\\\Adapter\\\\Guzzle6\\\\Client\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Constructor of class Sentry\\\\HttpClient\\\\HttpClientFactory has an unused parameter \\$responseFactory\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Constructor of class Sentry\\\\HttpClient\\\\HttpClientFactory has an unused parameter \\$uriFactory\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Method Sentry\\\\HttpClient\\\\HttpClientFactory\\:\\:resolveClient\\(\\) should return Http\\\\Client\\\\HttpAsyncClient\\|Psr\\\\Http\\\\Client\\\\ClientInterface but returns Http\\\\Client\\\\Curl\\\\Client\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Method Sentry\\\\HttpClient\\\\HttpClientFactory\\:\\:resolveClient\\(\\) should return Http\\\\Client\\\\HttpAsyncClient\\|Psr\\\\Http\\\\Client\\\\ClientInterface but returns Symfony\\\\Component\\\\HttpClient\\\\HttplugClient\\.$#"
count: 1
path: src/HttpClient/HttpClientFactory.php

-
message: "#^Property Sentry\\\\Integration\\\\IgnoreErrorsIntegration\\:\\:\\$options \\(array\\{ignore_exceptions\\: array\\<int, class\\-string\\<Throwable\\>\\>, ignore_tags\\: array\\<string, string\\>\\}\\) does not accept array\\.$#"
count: 1
Expand Down Expand Up @@ -140,6 +95,11 @@ parameters:
count: 1
path: src/Options.php

-
message: "#^Method Sentry\\\\Options\\:\\:getHttpClient\\(\\) should return Sentry\\\\HttpClient\\\\HttpClientInterface\\|null but returns mixed\\.$#"
count: 1
path: src/Options.php

-
message: "#^Method Sentry\\\\Options\\:\\:getHttpConnectTimeout\\(\\) should return float but returns mixed\\.$#"
count: 1
Expand All @@ -150,6 +110,16 @@ parameters:
count: 1
path: src/Options.php

-
message: "#^Method Sentry\\\\Options\\:\\:getHttpProxyAuthentication\\(\\) should return string\\|null but returns mixed\\.$#"
count: 1
path: src/Options.php

-
message: "#^Method Sentry\\\\Options\\:\\:getHttpSslVerifyPeer\\(\\) should return bool but returns mixed\\.$#"
count: 1
path: src/Options.php

-
message: "#^Method Sentry\\\\Options\\:\\:getHttpTimeout\\(\\) should return float but returns mixed\\.$#"
count: 1
Expand Down Expand Up @@ -245,6 +215,11 @@ parameters:
count: 1
path: src/Options.php

-
message: "#^Method Sentry\\\\Options\\:\\:getTransport\\(\\) should return Sentry\\\\Transport\\\\TransportInterface\\|null but returns mixed\\.$#"
count: 1
path: src/Options.php

-
message: "#^Method Sentry\\\\Options\\:\\:hasDefaultIntegrations\\(\\) should return bool but returns mixed\\.$#"
count: 1
Expand Down
14 changes: 9 additions & 5 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace Sentry;

use GuzzleHttp\Promise\PromiseInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Sentry\Integration\IntegrationInterface;
Expand All @@ -13,6 +12,7 @@
use Sentry\Serializer\RepresentationSerializerInterface;
use Sentry\Serializer\SerializerInterface;
use Sentry\State\Scope;
use Sentry\Transport\Result;
use Sentry\Transport\TransportInterface;

/**
Expand Down Expand Up @@ -173,14 +173,18 @@ public function captureEvent(Event $event, ?EventHint $hint = null, ?Scope $scop
}

try {
/** @var Response $response */
$response = $this->transport->send($event)->wait();
$event = $response->getEvent();
/** @var Result $result */
$result = $this->transport->send($event);
$event = $result->getEvent();
cleptric marked this conversation as resolved.
Show resolved Hide resolved

if (null !== $event) {
return $event->getId();
}
} catch (\Throwable $exception) {
$this->logger->error(
sprintf('Failed to send the event to Sentry. Reason: "%s".', $exception->getMessage()),
['exception' => $exception, 'event' => $event]
);
}

return null;
Expand Down Expand Up @@ -216,7 +220,7 @@ public function getIntegration(string $className): ?IntegrationInterface
/**
* {@inheritdoc}
*/
public function flush(?int $timeout = null): PromiseInterface
public function flush(?int $timeout = null): Result
{
return $this->transport->close($timeout);
}
stayallive marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
120 changes: 45 additions & 75 deletions src/ClientBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,36 @@

namespace Sentry;

use Http\Discovery\Psr17FactoryDiscovery;
use Psr\Log\LoggerInterface;
use Sentry\HttpClient\HttpClientFactory;
use Sentry\HttpClient\HttpClient;
use Sentry\HttpClient\HttpClientInterface;
use Sentry\Serializer\PayloadSerializer;
use Sentry\Serializer\RepresentationSerializerInterface;
use Sentry\Serializer\SerializerInterface;
use Sentry\Transport\DefaultTransportFactory;
use Sentry\Transport\TransportFactoryInterface;
use Sentry\Transport\HttpTransport;
use Sentry\Transport\TransportInterface;

/**
* The default implementation of {@link ClientBuilderInterface}.
* A configurable builder for Client objects.
*
* @author Stefano Arlandini <sarlandini@alice.it>
* @internal
*/
final class ClientBuilder implements ClientBuilderInterface
final class ClientBuilder
{
/**
* @var Options The client options
*/
private $options;

/**
* @var TransportFactoryInterface|null The transport factory
* @var TransportInterface The transport
*/
private $transportFactory;
private $transport;

/**
* @var TransportInterface|null The transport
* @var HttpClientInterface The HTTP client
cleptric marked this conversation as resolved.
Show resolved Hide resolved
*/
private $transport;
private $httpClient;

/**
* @var SerializerInterface|null The serializer to be injected in the client
Expand Down Expand Up @@ -68,127 +68,97 @@ final class ClientBuilder implements ClientBuilderInterface
public function __construct(Options $options = null)
{
$this->options = $options ?? new Options();

$this->httpClient = $this->options->getHttpClient() ?? new HttpClient($this->sdkIdentifier, $this->sdkVersion);
$this->transport = $this->options->getTransport() ?? new HttpTransport(
$this->options,
$this->httpClient,
new PayloadSerializer($this->options),
$this->logger
);
cleptric marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* {@inheritdoc}
* @param array<string, mixed> $options The client options, in naked array form
*/
public static function create(array $options = []): ClientBuilderInterface
public static function create(array $options = []): ClientBuilder
{
return new self(new Options($options));
}

/**
* {@inheritdoc}
*/
public function getOptions(): Options
{
return $this->options;
}

/**
* {@inheritdoc}
*/
public function setSerializer(SerializerInterface $serializer): ClientBuilderInterface
public function setSerializer(SerializerInterface $serializer): ClientBuilder
{
$this->serializer = $serializer;

return $this;
}

/**
* {@inheritdoc}
*/
public function setRepresentationSerializer(RepresentationSerializerInterface $representationSerializer): ClientBuilderInterface
public function setRepresentationSerializer(RepresentationSerializerInterface $representationSerializer): ClientBuilder
{
$this->representationSerializer = $representationSerializer;

return $this;
}

/**
* {@inheritdoc}
*/
public function setLogger(LoggerInterface $logger): ClientBuilderInterface
public function setLogger(LoggerInterface $logger): ClientBuilder
{
$this->logger = $logger;

return $this;
}

/**
* {@inheritdoc}
*/
public function setSdkIdentifier(string $sdkIdentifier): ClientBuilderInterface
public function setSdkIdentifier(string $sdkIdentifier): ClientBuilder
{
$this->sdkIdentifier = $sdkIdentifier;

return $this;
}

/**
* {@inheritdoc}
*/
public function setSdkVersion(string $sdkVersion): ClientBuilderInterface
public function setSdkVersion(string $sdkVersion): ClientBuilder
{
$this->sdkVersion = $sdkVersion;

return $this;
}

/**
* {@inheritdoc}
*/
public function setTransportFactory(TransportFactoryInterface $transportFactory): ClientBuilderInterface
public function getTransport(): TransportInterface
{
$this->transportFactory = $transportFactory;

return $this;
return $this->transport;
}

/**
* {@inheritdoc}
*/
public function getClient(): ClientInterface
public function setTransport(TransportInterface $transport): ClientBuilder
{
$this->transport = $this->transport ?? $this->createTransportInstance();
$this->transport = $transport;

return new Client($this->options, $this->transport, $this->sdkIdentifier, $this->sdkVersion, $this->serializer, $this->representationSerializer, $this->logger);
return $this;
}

/**
* Creates a new instance of the transport mechanism.
*/
private function createTransportInstance(): TransportInterface
public function getHttpClient(): HttpClientInterface
{
if (null !== $this->transport) {
return $this->transport;
}
return $this->httpClient;
}

$transportFactory = $this->transportFactory ?? $this->createDefaultTransportFactory();
public function setHttpClient(HttpClientInterface $httpClient): ClientBuilder
{
$this->httpClient = $httpClient;

return $transportFactory->create($this->options);
return $this;
}

/**
* Creates a new instance of the {@see DefaultTransportFactory} factory.
*/
private function createDefaultTransportFactory(): DefaultTransportFactory
public function getClient(): ClientInterface
cleptric marked this conversation as resolved.
Show resolved Hide resolved
{
$streamFactory = Psr17FactoryDiscovery::findStreamFactory();
$httpClientFactory = new HttpClientFactory(
null,
null,
$streamFactory,
null,
return new Client(
$this->options,
$this->transport,
$this->sdkIdentifier,
$this->sdkVersion
);

return new DefaultTransportFactory(
$streamFactory,
Psr17FactoryDiscovery::findRequestFactory(),
$httpClientFactory,
$this->sdkVersion,
$this->serializer,
$this->representationSerializer,
$this->logger
);
}
Expand Down
Loading
Loading