<?php
declare(strict_types=1);
namespace SHServ\Integrations\GAuth\Http;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
final class CurlHttpClient implements ClientInterface
{
private int $timeout;
private bool $verifySsl;
public function __construct(int $timeout = 30, bool $verifySsl = true)
{
$this->timeout = $timeout;
$this->verifySsl = $verifySsl;
}
public function sendRequest(RequestInterface $request): ResponseInterface
{
$ch = curl_init();
if ($ch === false) {
throw new ClientException('Failed to initialize cURL');
}
$headers = [];
foreach ($request->getHeaders() as $name => $values) {
$headers[] = $name . ': ' . implode(', ', $values);
}
$body = (string) $request->getBody();
$options = [
CURLOPT_URL => (string) $request->getUri(),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => min(10, $this->timeout),
CURLOPT_SSL_VERIFYPEER => $this->verifySsl,
CURLOPT_SSL_VERIFYHOST => $this->verifySsl ? 2 : 0,
CURLOPT_HTTPHEADER => $headers,
];
if ($request->getMethod() !== 'GET') {
$options[CURLOPT_CUSTOMREQUEST] = $request->getMethod();
}
if ($body !== '') {
$options[CURLOPT_POSTFIELDS] = $body;
}
curl_setopt_array($ch, $options);
$rawResponse = curl_exec($ch);
if ($rawResponse === false) {
$error = curl_error($ch);
curl_close($ch);
throw new ClientException('cURL error: ' . $error);
}
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$rawHeaders = substr($rawResponse, 0, $headerSize);
$responseBody = substr($rawResponse, $headerSize);
$parsedHeaders = $this->parseHeaders($rawHeaders);
return new Psr7Response($statusCode, $parsedHeaders, new Psr7Stream($responseBody));
}
private function parseHeaders(string $rawHeaders): array
{
$headers = [];
$lines = explode("\r\n", trim($rawHeaders));
foreach ($lines as $line) {
if (strpos($line, ':') === false) {
continue;
}
[$name, $value] = explode(':', $line, 2);
$name = trim($name);
$value = trim($value);
if (!isset($headers[$name])) {
$headers[$name] = [];
}
$headers[$name][] = $value;
}
return $headers;
}
}
final class ClientException extends \Exception implements ClientExceptionInterface
{
}