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