PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
← All changes | vendor_prefixed/guzzlehttp/guzzle/src/Handler/StreamHandler.php +0 -377 19.0 → trunk View file →
@@ -1,377 +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\FulfilledPromise;
8 -use YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface;
9 -use YoastSEO_Vendor\GuzzleHttp\Psr7;
10 -use YoastSEO_Vendor\GuzzleHttp\TransferStats;
11 -use YoastSEO_Vendor\GuzzleHttp\Utils;
12 -use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
13 -use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface;
14 -use YoastSEO_Vendor\Psr\Http\Message\StreamInterface;
15 -/**
16 - * HTTP handler that uses PHP's HTTP stream wrapper.
17 - */
18 -class StreamHandler
19 -{
20 - private $lastHeaders = [];
21 - /**
22 - * Sends an HTTP request.
23 - *
24 - * @param RequestInterface $request Request to send.
25 - * @param array $options Request transfer options.
26 - *
27 - * @return PromiseInterface
28 - */
29 - public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options)
30 - {
31 - // Sleep if there is a delay specified.
32 - if (isset($options['delay'])) {
33 - \usleep($options['delay'] * 1000);
34 - }
35 - $startTime = isset($options['on_stats']) ? \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime() : null;
36 - try {
37 - // Does not support the expect header.
38 - $request = $request->withoutHeader('Expect');
39 - // Append a content-length header if body size is zero to match
40 - // cURL's behavior.
41 - if (0 === $request->getBody()->getSize()) {
42 - $request = $request->withHeader('Content-Length', '0');
43 - }
44 - return $this->createResponse($request, $options, $this->createStream($request, $options), $startTime);
45 - } catch (\InvalidArgumentException $e) {
46 - throw $e;
47 - } catch (\Exception $e) {
48 - // Determine if the error was a networking error.
49 - $message = $e->getMessage();
50 - // This list can probably get more comprehensive.
51 - if (\strpos($message, 'getaddrinfo') || \strpos($message, 'Connection refused') || \strpos($message, "couldn't connect to host") || \strpos($message, "connection attempt failed")) {
52 - $e = new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException($e->getMessage(), $request, $e);
53 - }
54 - $e = \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException::wrapException($request, $e);
55 - $this->invokeStats($options, $request, $startTime, null, $e);
56 - return \YoastSEO_Vendor\GuzzleHttp\Promise\rejection_for($e);
57 - }
58 - }
59 - private function invokeStats(array $options, \YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, $startTime, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, $error = null)
60 - {
61 - if (isset($options['on_stats'])) {
62 - $stats = new \YoastSEO_Vendor\GuzzleHttp\TransferStats($request, $response, \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime() - $startTime, $error, []);
63 - \call_user_func($options['on_stats'], $stats);
64 - }
65 - }
66 - private function createResponse(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, $stream, $startTime)
67 - {
68 - $hdrs = $this->lastHeaders;
69 - $this->lastHeaders = [];
70 - $parts = \explode(' ', \array_shift($hdrs), 3);
71 - $ver = \explode('/', $parts[0])[1];
72 - $status = $parts[1];
73 - $reason = isset($parts[2]) ? $parts[2] : null;
74 - $headers = \YoastSEO_Vendor\GuzzleHttp\headers_from_lines($hdrs);
75 - list($stream, $headers) = $this->checkDecode($options, $headers, $stream);
76 - $stream = \YoastSEO_Vendor\GuzzleHttp\Psr7\stream_for($stream);
77 - $sink = $stream;
78 - if (\strcasecmp('HEAD', $request->getMethod())) {
79 - $sink = $this->createSink($stream, $options);
80 - }
81 - $response = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Response($status, $headers, $sink, $ver, $reason);
82 - if (isset($options['on_headers'])) {
83 - try {
84 - $options['on_headers']($response);
85 - } catch (\Exception $e) {
86 - $msg = 'An error was encountered during the on_headers event';
87 - $ex = new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException($msg, $request, $response, $e);
88 - return \YoastSEO_Vendor\GuzzleHttp\Promise\rejection_for($ex);
89 - }
90 - }
91 - // Do not drain when the request is a HEAD request because they have
92 - // no body.
93 - if ($sink !== $stream) {
94 - $this->drain($stream, $sink, $response->getHeaderLine('Content-Length'));
95 - }
96 - $this->invokeStats($options, $request, $startTime, $response, null);
97 - return new \YoastSEO_Vendor\GuzzleHttp\Promise\FulfilledPromise($response);
98 - }
99 - private function createSink(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, array $options)
100 - {
101 - if (!empty($options['stream'])) {
102 - return $stream;
103 - }
104 - $sink = isset($options['sink']) ? $options['sink'] : \fopen('php://temp', 'r+');
105 - return \is_string($sink) ? new \YoastSEO_Vendor\GuzzleHttp\Psr7\LazyOpenStream($sink, 'w+') : \YoastSEO_Vendor\GuzzleHttp\Psr7\stream_for($sink);
106 - }
107 - private function checkDecode(array $options, array $headers, $stream)
108 - {
109 - // Automatically decode responses when instructed.
110 - if (!empty($options['decode_content'])) {
111 - $normalizedKeys = \YoastSEO_Vendor\GuzzleHttp\normalize_header_keys($headers);
112 - if (isset($normalizedKeys['content-encoding'])) {
113 - $encoding = $headers[$normalizedKeys['content-encoding']];
114 - if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') {
115 - $stream = new \YoastSEO_Vendor\GuzzleHttp\Psr7\InflateStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\stream_for($stream));
116 - $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']];
117 - // Remove content-encoding header
118 - unset($headers[$normalizedKeys['content-encoding']]);
119 - // Fix content-length header
120 - if (isset($normalizedKeys['content-length'])) {
121 - $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']];
122 - $length = (int) $stream->getSize();
123 - if ($length === 0) {
124 - unset($headers[$normalizedKeys['content-length']]);
125 - } else {
126 - $headers[$normalizedKeys['content-length']] = [$length];
127 - }
128 - }
129 - }
130 - }
131 - }
132 - return [$stream, $headers];
133 - }
134 - /**
135 - * Drains the source stream into the "sink" client option.
136 - *
137 - * @param StreamInterface $source
138 - * @param StreamInterface $sink
139 - * @param string $contentLength Header specifying the amount of
140 - * data to read.
141 - *
142 - * @return StreamInterface
143 - * @throws \RuntimeException when the sink option is invalid.
144 - */
145 - private function drain(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $source, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $sink, $contentLength)
146 - {
147 - // If a content-length header is provided, then stop reading once
148 - // that number of bytes has been read. This can prevent infinitely
149 - // reading from a stream when dealing with servers that do not honor
150 - // Connection: Close headers.
151 - \YoastSEO_Vendor\GuzzleHttp\Psr7\copy_to_stream($source, $sink, \strlen($contentLength) > 0 && (int) $contentLength > 0 ? (int) $contentLength : -1);
152 - $sink->seek(0);
153 - $source->close();
154 - return $sink;
155 - }
156 - /**
157 - * Create a resource and check to ensure it was created successfully
158 - *
159 - * @param callable $callback Callable that returns stream resource
160 - *
161 - * @return resource
162 - * @throws \RuntimeException on error
163 - */
164 - private function createResource(callable $callback)
165 - {
166 - $errors = null;
167 - \set_error_handler(function ($_, $msg, $file, $line) use(&$errors) {
168 - $errors[] = ['message' => $msg, 'file' => $file, 'line' => $line];
169 - return \true;
170 - });
171 - $resource = $callback();
172 - \restore_error_handler();
173 - if (!$resource) {
174 - $message = 'Error creating resource: ';
175 - foreach ($errors as $err) {
176 - foreach ($err as $key => $value) {
177 - $message .= "[{$key}] {$value}" . \PHP_EOL;
178 - }
179 - }
180 - throw new \RuntimeException(\trim($message));
181 - }
182 - return $resource;
183 - }
184 - private function createStream(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options)
185 - {
186 - static $methods;
187 - if (!$methods) {
188 - $methods = \array_flip(\get_class_methods(__CLASS__));
189 - }
190 - // HTTP/1.1 streams using the PHP stream wrapper require a
191 - // Connection: close header
192 - if ($request->getProtocolVersion() == '1.1' && !$request->hasHeader('Connection')) {
193 - $request = $request->withHeader('Connection', 'close');
194 - }
195 - // Ensure SSL is verified by default
196 - if (!isset($options['verify'])) {
197 - $options['verify'] = \true;
198 - }
199 - $params = [];
200 - $context = $this->getDefaultContext($request);
201 - if (isset($options['on_headers']) && !\is_callable($options['on_headers'])) {
202 - throw new \InvalidArgumentException('on_headers must be callable');
203 - }
204 - if (!empty($options)) {
205 - foreach ($options as $key => $value) {
206 - $method = "add_{$key}";
207 - if (isset($methods[$method])) {
208 - $this->{$method}($request, $context, $value, $params);
209 - }
210 - }
211 - }
212 - if (isset($options['stream_context'])) {
213 - if (!\is_array($options['stream_context'])) {
214 - throw new \InvalidArgumentException('stream_context must be an array');
215 - }
216 - $context = \array_replace_recursive($context, $options['stream_context']);
217 - }
218 - // Microsoft NTLM authentication only supported with curl handler
219 - if (isset($options['auth']) && \is_array($options['auth']) && isset($options['auth'][2]) && 'ntlm' == $options['auth'][2]) {
220 - throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler');
221 - }
222 - $uri = $this->resolveHost($request, $options);
223 - $context = $this->createResource(function () use($context, $params) {
224 - return \stream_context_create($context, $params);
225 - });
226 - return $this->createResource(function () use($uri, &$http_response_header, $context, $options) {
227 - $resource = \fopen((string) $uri, 'r', null, $context);
228 - $this->lastHeaders = $http_response_header;
229 - if (isset($options['read_timeout'])) {
230 - $readTimeout = $options['read_timeout'];
231 - $sec = (int) $readTimeout;
232 - $usec = ($readTimeout - $sec) * 100000;
233 - \stream_set_timeout($resource, $sec, $usec);
234 - }
235 - return $resource;
236 - });
237 - }
238 - private function resolveHost(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options)
239 - {
240 - $uri = $request->getUri();
241 - if (isset($options['force_ip_resolve']) && !\filter_var($uri->getHost(), \FILTER_VALIDATE_IP)) {
242 - if ('v4' === $options['force_ip_resolve']) {
243 - $records = \dns_get_record($uri->getHost(), \DNS_A);
244 - if (!isset($records[0]['ip'])) {
245 - throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request);
246 - }
247 - $uri = $uri->withHost($records[0]['ip']);
248 - } elseif ('v6' === $options['force_ip_resolve']) {
249 - $records = \dns_get_record($uri->getHost(), \DNS_AAAA);
250 - if (!isset($records[0]['ipv6'])) {
251 - throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request);
252 - }
253 - $uri = $uri->withHost('[' . $records[0]['ipv6'] . ']');
254 - }
255 - }
256 - return $uri;
257 - }
258 - private function getDefaultContext(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request)
259 - {
260 - $headers = '';
261 - foreach ($request->getHeaders() as $name => $value) {
262 - foreach ($value as $val) {
263 - $headers .= "{$name}: {$val}\r\n";
264 - }
265 - }
266 - $context = ['http' => ['method' => $request->getMethod(), 'header' => $headers, 'protocol_version' => $request->getProtocolVersion(), 'ignore_errors' => \true, 'follow_location' => 0]];
267 - $body = (string) $request->getBody();
268 - if (!empty($body)) {
269 - $context['http']['content'] = $body;
270 - // Prevent the HTTP handler from adding a Content-Type header.
271 - if (!$request->hasHeader('Content-Type')) {
272 - $context['http']['header'] .= "Content-Type:\r\n";
273 - }
274 - }
275 - $context['http']['header'] = \rtrim($context['http']['header']);
276 - return $context;
277 - }
278 - private function add_proxy(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, &$options, $value, &$params)
279 - {
280 - if (!\is_array($value)) {
281 - $options['http']['proxy'] = $value;
282 - } else {
283 - $scheme = $request->getUri()->getScheme();
284 - if (isset($value[$scheme])) {
285 - if (!isset($value['no']) || !\YoastSEO_Vendor\GuzzleHttp\is_host_in_noproxy($request->getUri()->getHost(), $value['no'])) {
286 - $options['http']['proxy'] = $value[$scheme];
287 - }
288 - }
289 - }
290 - }
291 - private function add_timeout(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, &$options, $value, &$params)
292 - {
293 - if ($value > 0) {
294 - $options['http']['timeout'] = $value;
295 - }
296 - }
297 - private function add_verify(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, &$options, $value, &$params)
298 - {
299 - if ($value === \true) {
300 - // PHP 5.6 or greater will find the system cert by default. When
301 - // < 5.6, use the Guzzle bundled cacert.
302 - if (\PHP_VERSION_ID < 50600) {
303 - $options['ssl']['cafile'] = \YoastSEO_Vendor\GuzzleHttp\default_ca_bundle();
304 - }
305 - } elseif (\is_string($value)) {
306 - $options['ssl']['cafile'] = $value;
307 - if (!\file_exists($value)) {
308 - throw new \RuntimeException("SSL CA bundle not found: {$value}");
309 - }
310 - } elseif ($value === \false) {
311 - $options['ssl']['verify_peer'] = \false;
312 - $options['ssl']['verify_peer_name'] = \false;
313 - return;
314 - } else {
315 - throw new \InvalidArgumentException('Invalid verify request option');
316 - }
317 - $options['ssl']['verify_peer'] = \true;
318 - $options['ssl']['verify_peer_name'] = \true;
319 - $options['ssl']['allow_self_signed'] = \false;
320 - }
321 - private function add_cert(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, &$options, $value, &$params)
322 - {
323 - if (\is_array($value)) {
324 - $options['ssl']['passphrase'] = $value[1];
325 - $value = $value[0];
326 - }
327 - if (!\file_exists($value)) {
328 - throw new \RuntimeException("SSL certificate not found: {$value}");
329 - }
330 - $options['ssl']['local_cert'] = $value;
331 - }
332 - private function add_progress(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, &$options, $value, &$params)
333 - {
334 - $this->addNotification($params, function ($code, $a, $b, $c, $transferred, $total) use($value) {
335 - if ($code == \STREAM_NOTIFY_PROGRESS) {
336 - $value($total, $transferred, null, null);
337 - }
338 - });
339 - }
340 - private function add_debug(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, &$options, $value, &$params)
341 - {
342 - if ($value === \false) {
343 - return;
344 - }
345 - 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'];
346 - static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max'];
347 - $value = \YoastSEO_Vendor\GuzzleHttp\debug_resource($value);
348 - $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment('');
349 - $this->addNotification($params, function () use($ident, $value, $map, $args) {
350 - $passed = \func_get_args();
351 - $code = \array_shift($passed);
352 - \fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
353 - foreach (\array_filter($passed) as $i => $v) {
354 - \fwrite($value, $args[$i] . ': "' . $v . '" ');
355 - }
356 - \fwrite($value, "\n");
357 - });
358 - }
359 - private function addNotification(array &$params, callable $notify)
360 - {
361 - // Wrap the existing function if needed.
362 - if (!isset($params['notification'])) {
363 - $params['notification'] = $notify;
364 - } else {
365 - $params['notification'] = $this->callArray([$params['notification'], $notify]);
366 - }
367 - }
368 - private function callArray(array $functions)
369 - {
370 - return function () use($functions) {
371 - $args = \func_get_args();
372 - foreach ($functions as $fn) {
373 - \call_user_func_array($fn, $args);
374 - }
375 - };
376 - }
377 -}