diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 14:58:55 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 14:58:55 -0300 |
| commit | 64764fb9fac0227f8e0443e7c7519d73aec275b9 (patch) | |
| tree | cb4fc56eeb6ecce53854f9c530609cf01ab3349f /src/CurlTransport.php | |
| download | php-64764fb9fac0227f8e0443e7c7519d73aec275b9.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'src/CurlTransport.php')
| -rw-r--r-- | src/CurlTransport.php | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/src/CurlTransport.php b/src/CurlTransport.php new file mode 100644 index 0000000..d22949b --- /dev/null +++ b/src/CurlTransport.php @@ -0,0 +1,85 @@ +<?php + +declare(strict_types=1); + +namespace Cepis; + +/** + * The default {@see Transport}, built on ext-curl. + * + * curl is used directly rather than through an HTTP abstraction: this library + * makes one GET, and a wrapper for that would cost dependencies without buying + * anything. Hosts that need their own HTTP layer implement {@see Transport} + * instead. + * + * Each call opens its own handle, so one instance is safe to share. + */ +final class CurlTransport implements Transport +{ + /** + * @param bool $verifyTls Whether to verify the peer's certificate. Leave + * it alone. It exists for a lab against a self-signed local instance, + * and turning it off on the public API removes the only thing making + * the answer trustworthy. + * @param int $maxRedirects How many redirects to follow. + */ + public function __construct( + public readonly bool $verifyTls = true, + public readonly int $maxRedirects = 3, + ) { + } + + public function get(string $url, array $headers, float $timeout): Response + { + $handle = curl_init(); + if ($handle === false) { + throw new NetworkException('could not initialise a curl handle'); + } + + $lines = []; + foreach ($headers as $name => $value) { + $lines[] = $name . ': ' . $value; + } + + $milliseconds = max(1, (int) round($timeout * 1000)); + $received = []; + + curl_setopt_array($handle, [ + CURLOPT_URL => $url, + CURLOPT_HTTPGET => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => $lines, + CURLOPT_CONNECTTIMEOUT_MS => $milliseconds, + CURLOPT_TIMEOUT_MS => $milliseconds, + CURLOPT_FOLLOWLOCATION => $this->maxRedirects > 0, + CURLOPT_MAXREDIRS => $this->maxRedirects, + CURLOPT_SSL_VERIFYPEER => $this->verifyTls, + CURLOPT_SSL_VERIFYHOST => $this->verifyTls ? 2 : 0, + CURLOPT_NOSIGNAL => true, + CURLOPT_HEADERFUNCTION => static function ($_handle, string $line) use (&$received): int { + $length = strlen($line); + $parts = explode(':', $line, 2); + if (count($parts) === 2) { + $received[trim($parts[0])][] = trim($parts[1]); + } + + return $length; + }, + ]); + + $body = curl_exec($handle); + $error = curl_error($handle); + $status = (int) curl_getinfo($handle, CURLINFO_RESPONSE_CODE); + unset($handle); + + if ($body === false || $status === 0) { + throw new NetworkException(sprintf( + 'could not reach %s: %s', + $url, + $error !== '' ? $error : 'the request did not complete', + )); + } + + return new Response($status, $received, (string) $body); + } +} |