PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 2.8.16
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v2.8.16
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / vendor / symfony / http-client / AmpHttpClient.php

AmpHttpClient.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 2.8.16, at vendor/symfony/http-client/AmpHttpClient.php

182 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <[email protected]>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12 namespace Symfony\Component\HttpClient;
13
14 use Amp\CancelledException;
15 use Amp\Http\Client\DelegateHttpClient;
16 use Amp\Http\Client\InterceptedHttpClient;
17 use Amp\Http\Client\PooledHttpClient;
18 use Amp\Http\Client\Request;
19 use Amp\Http\Tunnel\Http1TunnelConnector;
20 use Amp\Promise;
21 use Psr\Log\LoggerAwareInterface;
22 use Psr\Log\LoggerAwareTrait;
23 use Symfony\Component\HttpClient\Exception\TransportException;
24 use Symfony\Component\HttpClient\Internal\AmpClientState;
25 use Symfony\Component\HttpClient\Response\AmpResponse;
26 use Symfony\Component\HttpClient\Response\ResponseStream;
27 use Symfony\Contracts\HttpClient\HttpClientInterface;
28 use Symfony\Contracts\HttpClient\ResponseInterface;
29 use Symfony\Contracts\HttpClient\ResponseStreamInterface;
30 use Symfony\Contracts\Service\ResetInterface;
31
32 if (!interface_exists(DelegateHttpClient::class)) {
33 throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client:^4.2.1".');
34 }
35
36 if (!interface_exists(Promise::class)) {
37 throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the installed "amphp/http-client" is not compatible with this version of "symfony/http-client". Try downgrading "amphp/http-client" to "^4.2.1".');
38 }
39
40 /**
41 * A portable implementation of the HttpClientInterface contracts based on Amp's HTTP client.
42 *
43 * @author Nicolas Grekas <[email protected]>
44 */
45 final class AmpHttpClient implements HttpClientInterface, LoggerAwareInterface, ResetInterface
46 {
47 use HttpClientTrait;
48 use LoggerAwareTrait;
49
50 private $defaultOptions = self::OPTIONS_DEFAULTS;
51 private static $emptyDefaults = self::OPTIONS_DEFAULTS;
52
53 /** @var AmpClientState */
54 private $multi;
55
56 /**
57 * @param array $defaultOptions Default requests' options
58 * @param callable|null $clientConfigurator A callable that builds a {@see DelegateHttpClient} from a {@see PooledHttpClient};
59 * passing null builds an {@see InterceptedHttpClient} with 2 retries on failures
60 * @param int $maxHostConnections The maximum number of connections to a single host
61 * @param int $maxPendingPushes The maximum number of pushed responses to accept in the queue
62 *
63 * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
64 */
65 public function __construct(array $defaultOptions = [], callable $clientConfigurator = null, int $maxHostConnections = 6, int $maxPendingPushes = 50)
66 {
67 $this->defaultOptions['buffer'] = $this->defaultOptions['buffer'] ?? \Closure::fromCallable([__CLASS__, 'shouldBuffer']);
68
69 if ($defaultOptions) {
70 [, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
71 }
72
73 $this->multi = new AmpClientState($clientConfigurator, $maxHostConnections, $maxPendingPushes, $this->logger);
74 }
75
76 /**
77 * @see HttpClientInterface::OPTIONS_DEFAULTS for available options
78 *
79 * {@inheritdoc}
80 */
81 public function request(string $method, string $url, array $options = []): ResponseInterface
82 {
83 [$url, $options] = self::prepareRequest($method, $url, $options, $this->defaultOptions);
84
85 $options['proxy'] = self::getProxy($options['proxy'], $url, $options['no_proxy']);
86
87 if (null !== $options['proxy'] && !class_exists(Http1TunnelConnector::class)) {
88 throw new \LogicException('You cannot use the "proxy" option as the "amphp/http-tunnel" package is not installed. Try running "composer require amphp/http-tunnel".');
89 }
90
91 if ($options['bindto']) {
92 if (0 === strpos($options['bindto'], 'if!')) {
93 throw new TransportException(__CLASS__.' cannot bind to network interfaces, use e.g. CurlHttpClient instead.');
94 }
95 if (0 === strpos($options['bindto'], 'host!')) {
96 $options['bindto'] = substr($options['bindto'], 5);
97 }
98 }
99
100 if (('' !== $options['body'] || 'POST' === $method || isset($options['normalized_headers']['content-length'])) && !isset($options['normalized_headers']['content-type'])) {
101 $options['headers'][] = 'Content-Type: application/x-www-form-urlencoded';
102 }
103
104 if (!isset($options['normalized_headers']['user-agent'])) {
105 $options['headers'][] = 'User-Agent: Symfony HttpClient/Amp';
106 }
107
108 if (0 < $options['max_duration']) {
109 $options['timeout'] = min($options['max_duration'], $options['timeout']);
110 }
111
112 if ($options['resolve']) {
113 $this->multi->dnsCache = $options['resolve'] + $this->multi->dnsCache;
114 }
115
116 if ($options['peer_fingerprint'] && !isset($options['peer_fingerprint']['pin-sha256'])) {
117 throw new TransportException(__CLASS__.' supports only "pin-sha256" fingerprints.');
118 }
119
120 $request = new Request(implode('', $url), $method);
121
122 if ($options['http_version']) {
123 switch ((float) $options['http_version']) {
124 case 1.0: $request->setProtocolVersions(['1.0']); break;
125 case 1.1: $request->setProtocolVersions(['1.1', '1.0']); break;
126 default: $request->setProtocolVersions(['2', '1.1', '1.0']); break;
127 }
128 }
129
130 foreach ($options['headers'] as $v) {
131 $h = explode(': ', $v, 2);
132 $request->addHeader($h[0], $h[1]);
133 }
134
135 $request->setTcpConnectTimeout(1000 * $options['timeout']);
136 $request->setTlsHandshakeTimeout(1000 * $options['timeout']);
137 $request->setTransferTimeout(1000 * $options['max_duration']);
138 if (method_exists($request, 'setInactivityTimeout')) {
139 $request->setInactivityTimeout(0);
140 }
141
142 if ('' !== $request->getUri()->getUserInfo() && !$request->hasHeader('authorization')) {
143 $auth = explode(':', $request->getUri()->getUserInfo(), 2);
144 $auth = array_map('rawurldecode', $auth) + [1 => ''];
145 $request->setHeader('Authorization', 'Basic '.base64_encode(implode(':', $auth)));
146 }
147
148 return new AmpResponse($this->multi, $request, $options, $this->logger);
149 }
150
151 /**
152 * {@inheritdoc}
153 */
154 public function stream($responses, float $timeout = null): ResponseStreamInterface
155 {
156 if ($responses instanceof AmpResponse) {
157 $responses = [$responses];
158 } elseif (!is_iterable($responses)) {
159 throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of AmpResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
160 }
161
162 return new ResponseStream(AmpResponse::stream($responses, $timeout));
163 }
164
165 public function reset()
166 {
167 $this->multi->dnsCache = [];
168
169 foreach ($this->multi->pushedResponses as $authority => $pushedResponses) {
170 foreach ($pushedResponses as [$pushedUrl, $pushDeferred]) {
171 $pushDeferred->fail(new CancelledException());
172
173 if ($this->logger) {
174 $this->logger->debug(sprintf('Unused pushed response: "%s"', $pushedUrl));
175 }
176 }
177 }
178
179 $this->multi->pushedResponses = [];
180 }
181 }
182