← All changes
|
vendor_prefixed/guzzlehttp/guzzle/src/Handler/StreamHandler.php
+0
-464
27.7
→
trunk
View file →
| @@ -1,464 +1,0 @@ | ||
| 1 | -<?php | |
| 2 | - | |
| 3 | -namespace YoastSEO_Vendor\GuzzleHttp\Handler; | |
| 4 | - | |
| 5 | -use YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException; | |
| 6 | -use YoastSEO_Vendor\GuzzleHttp\Exception\RequestException; | |
| 7 | -use YoastSEO_Vendor\GuzzleHttp\Promise as P; | |
| 8 | -use YoastSEO_Vendor\GuzzleHttp\Promise\FulfilledPromise; | |
| 9 | -use YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface; | |
| 10 | -use YoastSEO_Vendor\GuzzleHttp\Psr7; | |
| 11 | -use YoastSEO_Vendor\GuzzleHttp\TransferStats; | |
| 12 | -use YoastSEO_Vendor\GuzzleHttp\Utils; | |
| 13 | -use YoastSEO_Vendor\Psr\Http\Message\RequestInterface; | |
| 14 | -use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface; | |
| 15 | -use YoastSEO_Vendor\Psr\Http\Message\StreamInterface; | |
| 16 | -use YoastSEO_Vendor\Psr\Http\Message\UriInterface; | |
| 17 | -/** | |
| 18 | - * HTTP handler that uses PHP's HTTP stream wrapper. | |
| 19 | - * | |
| 20 | - * @final | |
| 21 | - */ | |
| 22 | -class StreamHandler | |
| 23 | -{ | |
| 24 | - /** | |
| 25 | - * @var array | |
| 26 | - */ | |
| 27 | - private $lastHeaders = []; | |
| 28 | - /** | |
| 29 | - * Sends an HTTP request. | |
| 30 | - * | |
| 31 | - * @param RequestInterface $request Request to send. | |
| 32 | - * @param array $options Request transfer options. | |
| 33 | - */ | |
| 34 | - public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface | |
| 35 | - { | |
| 36 | - // Sleep if there is a delay specified. | |
| 37 | - if (isset($options['delay'])) { | |
| 38 | - \usleep($options['delay'] * 1000); | |
| 39 | - } | |
| 40 | - $protocolVersion = $request->getProtocolVersion(); | |
| 41 | - if ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) { | |
| 42 | - throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf('HTTP/%s is not supported by the stream handler.', $protocolVersion), $request); | |
| 43 | - } | |
| 44 | - $startTime = isset($options['on_stats']) ? \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime() : null; | |
| 45 | - try { | |
| 46 | - // Does not support the expect header. | |
| 47 | - $request = $request->withoutHeader('Expect'); | |
| 48 | - // Append a content-length header if body size is zero to match | |
| 49 | - // the behavior of `CurlHandler` | |
| 50 | - if ((0 === \strcasecmp('PUT', $request->getMethod()) || 0 === \strcasecmp('POST', $request->getMethod())) && 0 === $request->getBody()->getSize()) { | |
| 51 | - $request = $request->withHeader('Content-Length', '0'); | |
| 52 | - } | |
| 53 | - return $this->createResponse($request, $options, $this->createStream($request, $options), $startTime); | |
| 54 | - } catch (\InvalidArgumentException $e) { | |
| 55 | - throw $e; | |
| 56 | - } catch (\Exception $e) { | |
| 57 | - // Determine if the error was a networking error. | |
| 58 | - $message = $e->getMessage(); | |
| 59 | - // This list can probably get more comprehensive. | |
| 60 | - if (\false !== \strpos($message, 'getaddrinfo') || \false !== \strpos($message, 'Connection refused') || \false !== \strpos($message, "couldn't connect to host") || \false !== \strpos($message, 'connection attempt failed')) { | |
| 61 | - $e = new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException($e->getMessage(), $request, $e); | |
| 62 | - } else { | |
| 63 | - $e = \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException::wrapException($request, $e); | |
| 64 | - } | |
| 65 | - $this->invokeStats($options, $request, $startTime, null, $e); | |
| 66 | - return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($e); | |
| 67 | - } | |
| 68 | - } | |
| 69 | - private function invokeStats(array $options, \YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, ?float $startTime, ?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, ?\Throwable $error = null) : void | |
| 70 | - { | |
| 71 | - if (isset($options['on_stats'])) { | |
| 72 | - $stats = new \YoastSEO_Vendor\GuzzleHttp\TransferStats($request, $response, \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime() - $startTime, $error, []); | |
| 73 | - $options['on_stats']($stats); | |
| 74 | - } | |
| 75 | - } | |
| 76 | - /** | |
| 77 | - * @param resource $stream | |
| 78 | - */ | |
| 79 | - private function createResponse(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, $stream, ?float $startTime) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface | |
| 80 | - { | |
| 81 | - $hdrs = $this->lastHeaders; | |
| 82 | - $this->lastHeaders = []; | |
| 83 | - try { | |
| 84 | - [$ver, $status, $reason, $headers] = \YoastSEO_Vendor\GuzzleHttp\Handler\HeaderProcessor::parseHeaders($hdrs); | |
| 85 | - } catch (\Exception $e) { | |
| 86 | - return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered while creating the response', $request, null, $e)); | |
| 87 | - } | |
| 88 | - [$stream, $headers] = $this->checkDecode($options, $headers, $stream); | |
| 89 | - $stream = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($stream); | |
| 90 | - $sink = $stream; | |
| 91 | - if (\strcasecmp('HEAD', $request->getMethod())) { | |
| 92 | - $sink = $this->createSink($stream, $options); | |
| 93 | - } | |
| 94 | - try { | |
| 95 | - $response = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Response($status, $headers, $sink, $ver, $reason); | |
| 96 | - } catch (\Exception $e) { | |
| 97 | - return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered while creating the response', $request, null, $e)); | |
| 98 | - } | |
| 99 | - if (isset($options['on_headers'])) { | |
| 100 | - try { | |
| 101 | - $options['on_headers']($response); | |
| 102 | - } catch (\Exception $e) { | |
| 103 | - return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered during the on_headers event', $request, $response, $e)); | |
| 104 | - } | |
| 105 | - } | |
| 106 | - // Do not drain when the request is a HEAD request because they have | |
| 107 | - // no body. | |
| 108 | - if ($sink !== $stream) { | |
| 109 | - $this->drain($stream, $sink, $response->getHeaderLine('Content-Length')); | |
| 110 | - } | |
| 111 | - $this->invokeStats($options, $request, $startTime, $response, null); | |
| 112 | - return new \YoastSEO_Vendor\GuzzleHttp\Promise\FulfilledPromise($response); | |
| 113 | - } | |
| 114 | - private function createSink(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, array $options) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface | |
| 115 | - { | |
| 116 | - if (!empty($options['stream'])) { | |
| 117 | - return $stream; | |
| 118 | - } | |
| 119 | - $sink = $options['sink'] ?? \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'r+'); | |
| 120 | - return \is_string($sink) ? new \YoastSEO_Vendor\GuzzleHttp\Psr7\LazyOpenStream($sink, 'w+') : \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($sink); | |
| 121 | - } | |
| 122 | - /** | |
| 123 | - * @param resource $stream | |
| 124 | - */ | |
| 125 | - private function checkDecode(array $options, array $headers, $stream) : array | |
| 126 | - { | |
| 127 | - // Automatically decode responses when instructed. | |
| 128 | - if (!empty($options['decode_content'])) { | |
| 129 | - $normalizedKeys = \YoastSEO_Vendor\GuzzleHttp\Utils::normalizeHeaderKeys($headers); | |
| 130 | - if (isset($normalizedKeys['content-encoding'])) { | |
| 131 | - $encoding = $headers[$normalizedKeys['content-encoding']]; | |
| 132 | - if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') { | |
| 133 | - $stream = new \YoastSEO_Vendor\GuzzleHttp\Psr7\InflateStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($stream)); | |
| 134 | - $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; | |
| 135 | - // Remove content-encoding header | |
| 136 | - unset($headers[$normalizedKeys['content-encoding']]); | |
| 137 | - // Fix content-length header | |
| 138 | - if (isset($normalizedKeys['content-length'])) { | |
| 139 | - $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']]; | |
| 140 | - $length = (int) $stream->getSize(); | |
| 141 | - if ($length === 0) { | |
| 142 | - unset($headers[$normalizedKeys['content-length']]); | |
| 143 | - } else { | |
| 144 | - $headers[$normalizedKeys['content-length']] = [$length]; | |
| 145 | - } | |
| 146 | - } | |
| 147 | - } | |
| 148 | - } | |
| 149 | - } | |
| 150 | - return [$stream, $headers]; | |
| 151 | - } | |
| 152 | - /** | |
| 153 | - * Drains the source stream into the "sink" client option. | |
| 154 | - * | |
| 155 | - * @param string $contentLength Header specifying the amount of | |
| 156 | - * data to read. | |
| 157 | - * | |
| 158 | - * @throws \RuntimeException when the sink option is invalid. | |
| 159 | - */ | |
| 160 | - private function drain(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $source, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $sink, string $contentLength) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface | |
| 161 | - { | |
| 162 | - // If a content-length header is provided, then stop reading once | |
| 163 | - // that number of bytes has been read. This can prevent infinitely | |
| 164 | - // reading from a stream when dealing with servers that do not honor | |
| 165 | - // Connection: Close headers. | |
| 166 | - \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::copyToStream($source, $sink, \strlen($contentLength) > 0 && (int) $contentLength > 0 ? (int) $contentLength : -1); | |
| 167 | - $sink->seek(0); | |
| 168 | - $source->close(); | |
| 169 | - return $sink; | |
| 170 | - } | |
| 171 | - /** | |
| 172 | - * Create a resource and check to ensure it was created successfully | |
| 173 | - * | |
| 174 | - * @param callable $callback Callable that returns stream resource | |
| 175 | - * | |
| 176 | - * @return resource | |
| 177 | - * | |
| 178 | - * @throws \RuntimeException on error | |
| 179 | - */ | |
| 180 | - private function createResource(callable $callback) | |
| 181 | - { | |
| 182 | - $errors = []; | |
| 183 | - \set_error_handler(static function ($_, $msg, $file, $line) use(&$errors) : bool { | |
| 184 | - $errors[] = ['message' => $msg, 'file' => $file, 'line' => $line]; | |
| 185 | - return \true; | |
| 186 | - }); | |
| 187 | - try { | |
| 188 | - $resource = $callback(); | |
| 189 | - } finally { | |
| 190 | - \restore_error_handler(); | |
| 191 | - } | |
| 192 | - if (!$resource) { | |
| 193 | - $message = 'Error creating resource: '; | |
| 194 | - foreach ($errors as $err) { | |
| 195 | - foreach ($err as $key => $value) { | |
| 196 | - $message .= "[{$key}] {$value}" . \PHP_EOL; | |
| 197 | - } | |
| 198 | - } | |
| 199 | - throw new \RuntimeException(\trim($message)); | |
| 200 | - } | |
| 201 | - return $resource; | |
| 202 | - } | |
| 203 | - /** | |
| 204 | - * @return resource | |
| 205 | - */ | |
| 206 | - private function createStream(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) | |
| 207 | - { | |
| 208 | - static $methods; | |
| 209 | - if (!$methods) { | |
| 210 | - $methods = \array_flip(\get_class_methods(__CLASS__)); | |
| 211 | - } | |
| 212 | - if (!\in_array($request->getUri()->getScheme(), ['http', 'https'])) { | |
| 213 | - throw new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException(\sprintf("The scheme '%s' is not supported.", $request->getUri()->getScheme()), $request); | |
| 214 | - } | |
| 215 | - // HTTP/1.1 streams using the PHP stream wrapper require a | |
| 216 | - // Connection: close header | |
| 217 | - if ($request->getProtocolVersion() === '1.1' && !$request->hasHeader('Connection')) { | |
| 218 | - $request = $request->withHeader('Connection', 'close'); | |
| 219 | - } | |
| 220 | - // Ensure SSL is verified by default | |
| 221 | - if (!isset($options['verify'])) { | |
| 222 | - $options['verify'] = \true; | |
| 223 | - } | |
| 224 | - $params = []; | |
| 225 | - $context = $this->getDefaultContext($request); | |
| 226 | - if (isset($options['on_headers']) && !\is_callable($options['on_headers'])) { | |
| 227 | - throw new \InvalidArgumentException('on_headers must be callable'); | |
| 228 | - } | |
| 229 | - if (!empty($options)) { | |
| 230 | - foreach ($options as $key => $value) { | |
| 231 | - $method = "add_{$key}"; | |
| 232 | - if (isset($methods[$method])) { | |
| 233 | - $this->{$method}($request, $context, $value, $params); | |
| 234 | - } | |
| 235 | - } | |
| 236 | - } | |
| 237 | - if (isset($options['stream_context'])) { | |
| 238 | - if (!\is_array($options['stream_context'])) { | |
| 239 | - throw new \InvalidArgumentException('stream_context must be an array'); | |
| 240 | - } | |
| 241 | - $context = \array_replace_recursive($context, $options['stream_context']); | |
| 242 | - } | |
| 243 | - // Microsoft NTLM authentication only supported with curl handler | |
| 244 | - if (isset($options['auth'][2]) && 'ntlm' === $options['auth'][2]) { | |
| 245 | - throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler'); | |
| 246 | - } | |
| 247 | - $uri = $this->resolveHost($request, $options); | |
| 248 | - $contextResource = $this->createResource(static function () use($context, $params) { | |
| 249 | - return \stream_context_create($context, $params); | |
| 250 | - }); | |
| 251 | - return $this->createResource(function () use($uri, $contextResource, $context, $options, $request) { | |
| 252 | - $resource = @\fopen((string) $uri, 'r', \false, $contextResource); | |
| 253 | - // See https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_http_response_header_predefined_variable | |
| 254 | - if (\function_exists('YoastSEO_Vendor\\http_get_last_response_headers')) { | |
| 255 | - /** @var array|null */ | |
| 256 | - $http_response_header = \YoastSEO_Vendor\http_get_last_response_headers(); | |
| 257 | - } | |
| 258 | - $this->lastHeaders = $http_response_header ?? []; | |
| 259 | - if (\false === $resource) { | |
| 260 | - throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf('Connection refused for URI %s', $uri), $request, null, $context); | |
| 261 | - } | |
| 262 | - if (isset($options['read_timeout'])) { | |
| 263 | - $readTimeout = $options['read_timeout']; | |
| 264 | - $sec = (int) $readTimeout; | |
| 265 | - $usec = ($readTimeout - $sec) * 100000; | |
| 266 | - \stream_set_timeout($resource, $sec, $usec); | |
| 267 | - } | |
| 268 | - return $resource; | |
| 269 | - }); | |
| 270 | - } | |
| 271 | - private function resolveHost(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface | |
| 272 | - { | |
| 273 | - $uri = $request->getUri(); | |
| 274 | - if (isset($options['force_ip_resolve']) && !\filter_var($uri->getHost(), \FILTER_VALIDATE_IP)) { | |
| 275 | - if ('v4' === $options['force_ip_resolve']) { | |
| 276 | - $records = \dns_get_record($uri->getHost(), \DNS_A); | |
| 277 | - if (\false === $records || !isset($records[0]['ip'])) { | |
| 278 | - throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request); | |
| 279 | - } | |
| 280 | - return $uri->withHost($records[0]['ip']); | |
| 281 | - } | |
| 282 | - if ('v6' === $options['force_ip_resolve']) { | |
| 283 | - $records = \dns_get_record($uri->getHost(), \DNS_AAAA); | |
| 284 | - if (\false === $records || !isset($records[0]['ipv6'])) { | |
| 285 | - throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request); | |
| 286 | - } | |
| 287 | - return $uri->withHost('[' . $records[0]['ipv6'] . ']'); | |
| 288 | - } | |
| 289 | - } | |
| 290 | - return $uri; | |
| 291 | - } | |
| 292 | - private function getDefaultContext(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : array | |
| 293 | - { | |
| 294 | - $headers = ''; | |
| 295 | - foreach ($request->getHeaders() as $name => $value) { | |
| 296 | - foreach ($value as $val) { | |
| 297 | - $headers .= "{$name}: {$val}\r\n"; | |
| 298 | - } | |
| 299 | - } | |
| 300 | - $context = ['http' => ['method' => $request->getMethod(), 'header' => $headers, 'protocol_version' => $request->getProtocolVersion(), 'ignore_errors' => \true, 'follow_location' => 0], 'ssl' => ['peer_name' => $request->getUri()->getHost()]]; | |
| 301 | - $body = (string) $request->getBody(); | |
| 302 | - if ('' !== $body) { | |
| 303 | - $context['http']['content'] = $body; | |
| 304 | - // Prevent the HTTP handler from adding a Content-Type header. | |
| 305 | - if (!$request->hasHeader('Content-Type')) { | |
| 306 | - $context['http']['header'] .= "Content-Type:\r\n"; | |
| 307 | - } | |
| 308 | - } | |
| 309 | - $context['http']['header'] = \rtrim($context['http']['header']); | |
| 310 | - return $context; | |
| 311 | - } | |
| 312 | - /** | |
| 313 | - * @param mixed $value as passed via Request transfer options. | |
| 314 | - */ | |
| 315 | - private function add_proxy(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void | |
| 316 | - { | |
| 317 | - $uri = null; | |
| 318 | - if (!\is_array($value)) { | |
| 319 | - $uri = $value; | |
| 320 | - } else { | |
| 321 | - $scheme = $request->getUri()->getScheme(); | |
| 322 | - if (isset($value[$scheme])) { | |
| 323 | - if (!isset($value['no']) || !\YoastSEO_Vendor\GuzzleHttp\Utils::isHostInNoProxy($request->getUri()->getHost(), $value['no'])) { | |
| 324 | - $uri = $value[$scheme]; | |
| 325 | - } | |
| 326 | - } | |
| 327 | - } | |
| 328 | - if (!$uri) { | |
| 329 | - return; | |
| 330 | - } | |
| 331 | - $parsed = $this->parse_proxy($uri); | |
| 332 | - $options['http']['proxy'] = $parsed['proxy']; | |
| 333 | - if ($parsed['auth']) { | |
| 334 | - if (!isset($options['http']['header'])) { | |
| 335 | - $options['http']['header'] = []; | |
| 336 | - } | |
| 337 | - $options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}"; | |
| 338 | - } | |
| 339 | - } | |
| 340 | - /** | |
| 341 | - * Parses the given proxy URL to make it compatible with the format PHP's stream context expects. | |
| 342 | - */ | |
| 343 | - private function parse_proxy(string $url) : array | |
| 344 | - { | |
| 345 | - $parsed = \parse_url($url); | |
| 346 | - if ($parsed !== \false && isset($parsed['scheme']) && $parsed['scheme'] === 'http') { | |
| 347 | - if (isset($parsed['host']) && isset($parsed['port'])) { | |
| 348 | - $auth = null; | |
| 349 | - if (isset($parsed['user']) && isset($parsed['pass'])) { | |
| 350 | - $auth = \base64_encode("{$parsed['user']}:{$parsed['pass']}"); | |
| 351 | - } | |
| 352 | - return ['proxy' => "tcp://{$parsed['host']}:{$parsed['port']}", 'auth' => $auth ? "Basic {$auth}" : null]; | |
| 353 | - } | |
| 354 | - } | |
| 355 | - // Return proxy as-is. | |
| 356 | - return ['proxy' => $url, 'auth' => null]; | |
| 357 | - } | |
| 358 | - /** | |
| 359 | - * @param mixed $value as passed via Request transfer options. | |
| 360 | - */ | |
| 361 | - private function add_timeout(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void | |
| 362 | - { | |
| 363 | - if ($value > 0) { | |
| 364 | - $options['http']['timeout'] = $value; | |
| 365 | - } | |
| 366 | - } | |
| 367 | - /** | |
| 368 | - * @param mixed $value as passed via Request transfer options. | |
| 369 | - */ | |
| 370 | - private function add_crypto_method(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void | |
| 371 | - { | |
| 372 | - if ($value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT || $value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT || $value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT || \defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT) { | |
| 373 | - $options['http']['crypto_method'] = $value; | |
| 374 | - return; | |
| 375 | - } | |
| 376 | - throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); | |
| 377 | - } | |
| 378 | - /** | |
| 379 | - * @param mixed $value as passed via Request transfer options. | |
| 380 | - */ | |
| 381 | - private function add_verify(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void | |
| 382 | - { | |
| 383 | - if ($value === \false) { | |
| 384 | - $options['ssl']['verify_peer'] = \false; | |
| 385 | - $options['ssl']['verify_peer_name'] = \false; | |
| 386 | - return; | |
| 387 | - } | |
| 388 | - if (\is_string($value)) { | |
| 389 | - $options['ssl']['cafile'] = $value; | |
| 390 | - if (!\file_exists($value)) { | |
| 391 | - throw new \RuntimeException("SSL CA bundle not found: {$value}"); | |
| 392 | - } | |
| 393 | - } elseif ($value !== \true) { | |
| 394 | - throw new \InvalidArgumentException('Invalid verify request option'); | |
| 395 | - } | |
| 396 | - $options['ssl']['verify_peer'] = \true; | |
| 397 | - $options['ssl']['verify_peer_name'] = \true; | |
| 398 | - $options['ssl']['allow_self_signed'] = \false; | |
| 399 | - } | |
| 400 | - /** | |
| 401 | - * @param mixed $value as passed via Request transfer options. | |
| 402 | - */ | |
| 403 | - private function add_cert(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void | |
| 404 | - { | |
| 405 | - if (\is_array($value)) { | |
| 406 | - $options['ssl']['passphrase'] = $value[1]; | |
| 407 | - $value = $value[0]; | |
| 408 | - } | |
| 409 | - if (!\file_exists($value)) { | |
| 410 | - throw new \RuntimeException("SSL certificate not found: {$value}"); | |
| 411 | - } | |
| 412 | - $options['ssl']['local_cert'] = $value; | |
| 413 | - } | |
| 414 | - /** | |
| 415 | - * @param mixed $value as passed via Request transfer options. | |
| 416 | - */ | |
| 417 | - private function add_progress(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void | |
| 418 | - { | |
| 419 | - self::addNotification($params, static function ($code, $a, $b, $c, $transferred, $total) use($value) { | |
| 420 | - if ($code == \STREAM_NOTIFY_PROGRESS) { | |
| 421 | - // The upload progress cannot be determined. Use 0 for cURL compatibility: | |
| 422 | - // https://curl.se/libcurl/c/CURLOPT_PROGRESSFUNCTION.html | |
| 423 | - $value($total, $transferred, 0, 0); | |
| 424 | - } | |
| 425 | - }); | |
| 426 | - } | |
| 427 | - /** | |
| 428 | - * @param mixed $value as passed via Request transfer options. | |
| 429 | - */ | |
| 430 | - private function add_debug(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void | |
| 431 | - { | |
| 432 | - if ($value === \false) { | |
| 433 | - return; | |
| 434 | - } | |
| 435 | - static $map = [\STREAM_NOTIFY_CONNECT => 'CONNECT', \STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', \STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', \STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', \STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', \STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', \STREAM_NOTIFY_PROGRESS => 'PROGRESS', \STREAM_NOTIFY_FAILURE => 'FAILURE', \STREAM_NOTIFY_COMPLETED => 'COMPLETED', \STREAM_NOTIFY_RESOLVE => 'RESOLVE']; | |
| 436 | - static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max']; | |
| 437 | - $value = \YoastSEO_Vendor\GuzzleHttp\Utils::debugResource($value); | |
| 438 | - $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment(''); | |
| 439 | - self::addNotification($params, static function (int $code, ...$passed) use($ident, $value, $map, $args) : void { | |
| 440 | - \fprintf($value, '<%s> [%s] ', $ident, $map[$code]); | |
| 441 | - foreach (\array_filter($passed) as $i => $v) { | |
| 442 | - \fwrite($value, $args[$i] . ': "' . $v . '" '); | |
| 443 | - } | |
| 444 | - \fwrite($value, "\n"); | |
| 445 | - }); | |
| 446 | - } | |
| 447 | - private static function addNotification(array &$params, callable $notify) : void | |
| 448 | - { | |
| 449 | - // Wrap the existing function if needed. | |
| 450 | - if (!isset($params['notification'])) { | |
| 451 | - $params['notification'] = $notify; | |
| 452 | - } else { | |
| 453 | - $params['notification'] = self::callArray([$params['notification'], $notify]); | |
| 454 | - } | |
| 455 | - } | |
| 456 | - private static function callArray(array $functions) : callable | |
| 457 | - { | |
| 458 | - return static function (...$args) use($functions) { | |
| 459 | - foreach ($functions as $fn) { | |
| 460 | - $fn(...$args); | |
| 461 | - } | |
| 462 | - }; | |
| 463 | - } | |
| 464 | -} | |