@@ -11,193 +11,193 @@ |
||
11 | 11 | */ |
12 | 12 | final class AppendStream implements StreamInterface |
13 | 13 | { |
14 | - /** @var StreamInterface[] Streams being decorated */ |
|
15 | - private $streams = []; |
|
16 | - /** @var bool */ |
|
17 | - private $seekable = \true; |
|
18 | - /** @var int */ |
|
19 | - private $current = 0; |
|
20 | - /** @var int */ |
|
21 | - private $pos = 0; |
|
22 | - /** |
|
23 | - * @param StreamInterface[] $streams Streams to decorate. Each stream must |
|
24 | - * be readable. |
|
25 | - */ |
|
26 | - public function __construct(array $streams = []) |
|
27 | - { |
|
28 | - foreach ($streams as $stream) { |
|
29 | - $this->addStream($stream); |
|
30 | - } |
|
31 | - } |
|
32 | - public function __toString() : string |
|
33 | - { |
|
34 | - try { |
|
35 | - $this->rewind(); |
|
36 | - return $this->getContents(); |
|
37 | - } catch (\Throwable $e) { |
|
38 | - if (\PHP_VERSION_ID >= 70400) { |
|
39 | - throw $e; |
|
40 | - } |
|
41 | - \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); |
|
42 | - return ''; |
|
43 | - } |
|
44 | - } |
|
45 | - /** |
|
46 | - * Add a stream to the AppendStream |
|
47 | - * |
|
48 | - * @param StreamInterface $stream Stream to append. Must be readable. |
|
49 | - * |
|
50 | - * @throws \InvalidArgumentException if the stream is not readable |
|
51 | - */ |
|
52 | - public function addStream(StreamInterface $stream) : void |
|
53 | - { |
|
54 | - if (!$stream->isReadable()) { |
|
55 | - throw new \InvalidArgumentException('Each stream must be readable'); |
|
56 | - } |
|
57 | - // The stream is only seekable if all streams are seekable |
|
58 | - if (!$stream->isSeekable()) { |
|
59 | - $this->seekable = \false; |
|
60 | - } |
|
61 | - $this->streams[] = $stream; |
|
62 | - } |
|
63 | - public function getContents() : string |
|
64 | - { |
|
65 | - return Utils::copyToString($this); |
|
66 | - } |
|
67 | - /** |
|
68 | - * Closes each attached stream. |
|
69 | - */ |
|
70 | - public function close() : void |
|
71 | - { |
|
72 | - $this->pos = $this->current = 0; |
|
73 | - $this->seekable = \true; |
|
74 | - foreach ($this->streams as $stream) { |
|
75 | - $stream->close(); |
|
76 | - } |
|
77 | - $this->streams = []; |
|
78 | - } |
|
79 | - /** |
|
80 | - * Detaches each attached stream. |
|
81 | - * |
|
82 | - * Returns null as it's not clear which underlying stream resource to return. |
|
83 | - */ |
|
84 | - public function detach() |
|
85 | - { |
|
86 | - $this->pos = $this->current = 0; |
|
87 | - $this->seekable = \true; |
|
88 | - foreach ($this->streams as $stream) { |
|
89 | - $stream->detach(); |
|
90 | - } |
|
91 | - $this->streams = []; |
|
92 | - return null; |
|
93 | - } |
|
94 | - public function tell() : int |
|
95 | - { |
|
96 | - return $this->pos; |
|
97 | - } |
|
98 | - /** |
|
99 | - * Tries to calculate the size by adding the size of each stream. |
|
100 | - * |
|
101 | - * If any of the streams do not return a valid number, then the size of the |
|
102 | - * append stream cannot be determined and null is returned. |
|
103 | - */ |
|
104 | - public function getSize() : ?int |
|
105 | - { |
|
106 | - $size = 0; |
|
107 | - foreach ($this->streams as $stream) { |
|
108 | - $s = $stream->getSize(); |
|
109 | - if ($s === null) { |
|
110 | - return null; |
|
111 | - } |
|
112 | - $size += $s; |
|
113 | - } |
|
114 | - return $size; |
|
115 | - } |
|
116 | - public function eof() : bool |
|
117 | - { |
|
118 | - return !$this->streams || $this->current >= \count($this->streams) - 1 && $this->streams[$this->current]->eof(); |
|
119 | - } |
|
120 | - public function rewind() : void |
|
121 | - { |
|
122 | - $this->seek(0); |
|
123 | - } |
|
124 | - /** |
|
125 | - * Attempts to seek to the given position. Only supports SEEK_SET. |
|
126 | - */ |
|
127 | - public function seek($offset, $whence = \SEEK_SET) : void |
|
128 | - { |
|
129 | - if (!$this->seekable) { |
|
130 | - throw new \RuntimeException('This AppendStream is not seekable'); |
|
131 | - } elseif ($whence !== \SEEK_SET) { |
|
132 | - throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); |
|
133 | - } |
|
134 | - $this->pos = $this->current = 0; |
|
135 | - // Rewind each stream |
|
136 | - foreach ($this->streams as $i => $stream) { |
|
137 | - try { |
|
138 | - $stream->rewind(); |
|
139 | - } catch (\Exception $e) { |
|
140 | - throw new \RuntimeException('Unable to seek stream ' . $i . ' of the AppendStream', 0, $e); |
|
141 | - } |
|
142 | - } |
|
143 | - // Seek to the actual position by reading from each stream |
|
144 | - while ($this->pos < $offset && !$this->eof()) { |
|
145 | - $result = $this->read(\min(8096, $offset - $this->pos)); |
|
146 | - if ($result === '') { |
|
147 | - break; |
|
148 | - } |
|
149 | - } |
|
150 | - } |
|
151 | - /** |
|
152 | - * Reads from all of the appended streams until the length is met or EOF. |
|
153 | - */ |
|
154 | - public function read($length) : string |
|
155 | - { |
|
156 | - $buffer = ''; |
|
157 | - $total = \count($this->streams) - 1; |
|
158 | - $remaining = $length; |
|
159 | - $progressToNext = \false; |
|
160 | - while ($remaining > 0) { |
|
161 | - // Progress to the next stream if needed. |
|
162 | - if ($progressToNext || $this->streams[$this->current]->eof()) { |
|
163 | - $progressToNext = \false; |
|
164 | - if ($this->current === $total) { |
|
165 | - break; |
|
166 | - } |
|
167 | - ++$this->current; |
|
168 | - } |
|
169 | - $result = $this->streams[$this->current]->read($remaining); |
|
170 | - if ($result === '') { |
|
171 | - $progressToNext = \true; |
|
172 | - continue; |
|
173 | - } |
|
174 | - $buffer .= $result; |
|
175 | - $remaining = $length - \strlen($buffer); |
|
176 | - } |
|
177 | - $this->pos += \strlen($buffer); |
|
178 | - return $buffer; |
|
179 | - } |
|
180 | - public function isReadable() : bool |
|
181 | - { |
|
182 | - return \true; |
|
183 | - } |
|
184 | - public function isWritable() : bool |
|
185 | - { |
|
186 | - return \false; |
|
187 | - } |
|
188 | - public function isSeekable() : bool |
|
189 | - { |
|
190 | - return $this->seekable; |
|
191 | - } |
|
192 | - public function write($string) : int |
|
193 | - { |
|
194 | - throw new \RuntimeException('Cannot write to an AppendStream'); |
|
195 | - } |
|
196 | - /** |
|
197 | - * @return mixed |
|
198 | - */ |
|
199 | - public function getMetadata($key = null) |
|
200 | - { |
|
201 | - return $key ? null : []; |
|
202 | - } |
|
14 | + /** @var StreamInterface[] Streams being decorated */ |
|
15 | + private $streams = []; |
|
16 | + /** @var bool */ |
|
17 | + private $seekable = \true; |
|
18 | + /** @var int */ |
|
19 | + private $current = 0; |
|
20 | + /** @var int */ |
|
21 | + private $pos = 0; |
|
22 | + /** |
|
23 | + * @param StreamInterface[] $streams Streams to decorate. Each stream must |
|
24 | + * be readable. |
|
25 | + */ |
|
26 | + public function __construct(array $streams = []) |
|
27 | + { |
|
28 | + foreach ($streams as $stream) { |
|
29 | + $this->addStream($stream); |
|
30 | + } |
|
31 | + } |
|
32 | + public function __toString() : string |
|
33 | + { |
|
34 | + try { |
|
35 | + $this->rewind(); |
|
36 | + return $this->getContents(); |
|
37 | + } catch (\Throwable $e) { |
|
38 | + if (\PHP_VERSION_ID >= 70400) { |
|
39 | + throw $e; |
|
40 | + } |
|
41 | + \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); |
|
42 | + return ''; |
|
43 | + } |
|
44 | + } |
|
45 | + /** |
|
46 | + * Add a stream to the AppendStream |
|
47 | + * |
|
48 | + * @param StreamInterface $stream Stream to append. Must be readable. |
|
49 | + * |
|
50 | + * @throws \InvalidArgumentException if the stream is not readable |
|
51 | + */ |
|
52 | + public function addStream(StreamInterface $stream) : void |
|
53 | + { |
|
54 | + if (!$stream->isReadable()) { |
|
55 | + throw new \InvalidArgumentException('Each stream must be readable'); |
|
56 | + } |
|
57 | + // The stream is only seekable if all streams are seekable |
|
58 | + if (!$stream->isSeekable()) { |
|
59 | + $this->seekable = \false; |
|
60 | + } |
|
61 | + $this->streams[] = $stream; |
|
62 | + } |
|
63 | + public function getContents() : string |
|
64 | + { |
|
65 | + return Utils::copyToString($this); |
|
66 | + } |
|
67 | + /** |
|
68 | + * Closes each attached stream. |
|
69 | + */ |
|
70 | + public function close() : void |
|
71 | + { |
|
72 | + $this->pos = $this->current = 0; |
|
73 | + $this->seekable = \true; |
|
74 | + foreach ($this->streams as $stream) { |
|
75 | + $stream->close(); |
|
76 | + } |
|
77 | + $this->streams = []; |
|
78 | + } |
|
79 | + /** |
|
80 | + * Detaches each attached stream. |
|
81 | + * |
|
82 | + * Returns null as it's not clear which underlying stream resource to return. |
|
83 | + */ |
|
84 | + public function detach() |
|
85 | + { |
|
86 | + $this->pos = $this->current = 0; |
|
87 | + $this->seekable = \true; |
|
88 | + foreach ($this->streams as $stream) { |
|
89 | + $stream->detach(); |
|
90 | + } |
|
91 | + $this->streams = []; |
|
92 | + return null; |
|
93 | + } |
|
94 | + public function tell() : int |
|
95 | + { |
|
96 | + return $this->pos; |
|
97 | + } |
|
98 | + /** |
|
99 | + * Tries to calculate the size by adding the size of each stream. |
|
100 | + * |
|
101 | + * If any of the streams do not return a valid number, then the size of the |
|
102 | + * append stream cannot be determined and null is returned. |
|
103 | + */ |
|
104 | + public function getSize() : ?int |
|
105 | + { |
|
106 | + $size = 0; |
|
107 | + foreach ($this->streams as $stream) { |
|
108 | + $s = $stream->getSize(); |
|
109 | + if ($s === null) { |
|
110 | + return null; |
|
111 | + } |
|
112 | + $size += $s; |
|
113 | + } |
|
114 | + return $size; |
|
115 | + } |
|
116 | + public function eof() : bool |
|
117 | + { |
|
118 | + return !$this->streams || $this->current >= \count($this->streams) - 1 && $this->streams[$this->current]->eof(); |
|
119 | + } |
|
120 | + public function rewind() : void |
|
121 | + { |
|
122 | + $this->seek(0); |
|
123 | + } |
|
124 | + /** |
|
125 | + * Attempts to seek to the given position. Only supports SEEK_SET. |
|
126 | + */ |
|
127 | + public function seek($offset, $whence = \SEEK_SET) : void |
|
128 | + { |
|
129 | + if (!$this->seekable) { |
|
130 | + throw new \RuntimeException('This AppendStream is not seekable'); |
|
131 | + } elseif ($whence !== \SEEK_SET) { |
|
132 | + throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); |
|
133 | + } |
|
134 | + $this->pos = $this->current = 0; |
|
135 | + // Rewind each stream |
|
136 | + foreach ($this->streams as $i => $stream) { |
|
137 | + try { |
|
138 | + $stream->rewind(); |
|
139 | + } catch (\Exception $e) { |
|
140 | + throw new \RuntimeException('Unable to seek stream ' . $i . ' of the AppendStream', 0, $e); |
|
141 | + } |
|
142 | + } |
|
143 | + // Seek to the actual position by reading from each stream |
|
144 | + while ($this->pos < $offset && !$this->eof()) { |
|
145 | + $result = $this->read(\min(8096, $offset - $this->pos)); |
|
146 | + if ($result === '') { |
|
147 | + break; |
|
148 | + } |
|
149 | + } |
|
150 | + } |
|
151 | + /** |
|
152 | + * Reads from all of the appended streams until the length is met or EOF. |
|
153 | + */ |
|
154 | + public function read($length) : string |
|
155 | + { |
|
156 | + $buffer = ''; |
|
157 | + $total = \count($this->streams) - 1; |
|
158 | + $remaining = $length; |
|
159 | + $progressToNext = \false; |
|
160 | + while ($remaining > 0) { |
|
161 | + // Progress to the next stream if needed. |
|
162 | + if ($progressToNext || $this->streams[$this->current]->eof()) { |
|
163 | + $progressToNext = \false; |
|
164 | + if ($this->current === $total) { |
|
165 | + break; |
|
166 | + } |
|
167 | + ++$this->current; |
|
168 | + } |
|
169 | + $result = $this->streams[$this->current]->read($remaining); |
|
170 | + if ($result === '') { |
|
171 | + $progressToNext = \true; |
|
172 | + continue; |
|
173 | + } |
|
174 | + $buffer .= $result; |
|
175 | + $remaining = $length - \strlen($buffer); |
|
176 | + } |
|
177 | + $this->pos += \strlen($buffer); |
|
178 | + return $buffer; |
|
179 | + } |
|
180 | + public function isReadable() : bool |
|
181 | + { |
|
182 | + return \true; |
|
183 | + } |
|
184 | + public function isWritable() : bool |
|
185 | + { |
|
186 | + return \false; |
|
187 | + } |
|
188 | + public function isSeekable() : bool |
|
189 | + { |
|
190 | + return $this->seekable; |
|
191 | + } |
|
192 | + public function write($string) : int |
|
193 | + { |
|
194 | + throw new \RuntimeException('Cannot write to an AppendStream'); |
|
195 | + } |
|
196 | + /** |
|
197 | + * @return mixed |
|
198 | + */ |
|
199 | + public function getMetadata($key = null) |
|
200 | + { |
|
201 | + return $key ? null : []; |
|
202 | + } |
|
203 | 203 | } |
@@ -1,6 +1,6 @@ discard block |
||
1 | 1 | <?php |
2 | 2 | |
3 | -declare (strict_types=1); |
|
3 | +declare(strict_types=1); |
|
4 | 4 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
5 | 5 | |
6 | 6 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\StreamInterface; |
@@ -38,7 +38,7 @@ discard block |
||
38 | 38 | if (\PHP_VERSION_ID >= 70400) { |
39 | 39 | throw $e; |
40 | 40 | } |
41 | - \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); |
|
41 | + \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string)$e), \E_USER_ERROR); |
|
42 | 42 | return ''; |
43 | 43 | } |
44 | 44 | } |
@@ -137,7 +137,7 @@ discard block |
||
137 | 137 | try { |
138 | 138 | $stream->rewind(); |
139 | 139 | } catch (\Exception $e) { |
140 | - throw new \RuntimeException('Unable to seek stream ' . $i . ' of the AppendStream', 0, $e); |
|
140 | + throw new \RuntimeException('Unable to seek stream '.$i.' of the AppendStream', 0, $e); |
|
141 | 141 | } |
142 | 142 | } |
143 | 143 | // Seek to the actual position by reading from each stream |
@@ -9,8 +9,7 @@ |
||
9 | 9 | * |
10 | 10 | * This is a read-only stream decorator. |
11 | 11 | */ |
12 | -final class AppendStream implements StreamInterface |
|
13 | -{ |
|
12 | +final class AppendStream implements StreamInterface { |
|
14 | 13 | /** @var StreamInterface[] Streams being decorated */ |
15 | 14 | private $streams = []; |
16 | 15 | /** @var bool */ |
@@ -12,113 +12,113 @@ |
||
12 | 12 | */ |
13 | 13 | class Request implements RequestInterface |
14 | 14 | { |
15 | - use MessageTrait; |
|
16 | - /** @var string */ |
|
17 | - private $method; |
|
18 | - /** @var string|null */ |
|
19 | - private $requestTarget; |
|
20 | - /** @var UriInterface */ |
|
21 | - private $uri; |
|
22 | - /** |
|
23 | - * @param string $method HTTP method |
|
24 | - * @param string|UriInterface $uri URI |
|
25 | - * @param (string|string[])[] $headers Request headers |
|
26 | - * @param string|resource|StreamInterface|null $body Request body |
|
27 | - * @param string $version Protocol version |
|
28 | - */ |
|
29 | - public function __construct(string $method, $uri, array $headers = [], $body = null, string $version = '1.1') |
|
30 | - { |
|
31 | - $this->assertMethod($method); |
|
32 | - if (!$uri instanceof UriInterface) { |
|
33 | - $uri = new Uri($uri); |
|
34 | - } |
|
35 | - $this->method = \strtoupper($method); |
|
36 | - $this->uri = $uri; |
|
37 | - $this->setHeaders($headers); |
|
38 | - $this->protocol = $version; |
|
39 | - if (!isset($this->headerNames['host'])) { |
|
40 | - $this->updateHostFromUri(); |
|
41 | - } |
|
42 | - if ($body !== '' && $body !== null) { |
|
43 | - $this->stream = Utils::streamFor($body); |
|
44 | - } |
|
45 | - } |
|
46 | - public function getRequestTarget() : string |
|
47 | - { |
|
48 | - if ($this->requestTarget !== null) { |
|
49 | - return $this->requestTarget; |
|
50 | - } |
|
51 | - $target = $this->uri->getPath(); |
|
52 | - if ($target === '') { |
|
53 | - $target = '/'; |
|
54 | - } |
|
55 | - if ($this->uri->getQuery() != '') { |
|
56 | - $target .= '?' . $this->uri->getQuery(); |
|
57 | - } |
|
58 | - return $target; |
|
59 | - } |
|
60 | - public function withRequestTarget($requestTarget) : RequestInterface |
|
61 | - { |
|
62 | - if (\preg_match('#\\s#', $requestTarget)) { |
|
63 | - throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace'); |
|
64 | - } |
|
65 | - $new = clone $this; |
|
66 | - $new->requestTarget = $requestTarget; |
|
67 | - return $new; |
|
68 | - } |
|
69 | - public function getMethod() : string |
|
70 | - { |
|
71 | - return $this->method; |
|
72 | - } |
|
73 | - public function withMethod($method) : RequestInterface |
|
74 | - { |
|
75 | - $this->assertMethod($method); |
|
76 | - $new = clone $this; |
|
77 | - $new->method = \strtoupper($method); |
|
78 | - return $new; |
|
79 | - } |
|
80 | - public function getUri() : UriInterface |
|
81 | - { |
|
82 | - return $this->uri; |
|
83 | - } |
|
84 | - public function withUri(UriInterface $uri, $preserveHost = \false) : RequestInterface |
|
85 | - { |
|
86 | - if ($uri === $this->uri) { |
|
87 | - return $this; |
|
88 | - } |
|
89 | - $new = clone $this; |
|
90 | - $new->uri = $uri; |
|
91 | - if (!$preserveHost || !isset($this->headerNames['host'])) { |
|
92 | - $new->updateHostFromUri(); |
|
93 | - } |
|
94 | - return $new; |
|
95 | - } |
|
96 | - private function updateHostFromUri() : void |
|
97 | - { |
|
98 | - $host = $this->uri->getHost(); |
|
99 | - if ($host == '') { |
|
100 | - return; |
|
101 | - } |
|
102 | - if (($port = $this->uri->getPort()) !== null) { |
|
103 | - $host .= ':' . $port; |
|
104 | - } |
|
105 | - if (isset($this->headerNames['host'])) { |
|
106 | - $header = $this->headerNames['host']; |
|
107 | - } else { |
|
108 | - $header = 'Host'; |
|
109 | - $this->headerNames['host'] = 'Host'; |
|
110 | - } |
|
111 | - // Ensure Host is the first header. |
|
112 | - // See: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 |
|
113 | - $this->headers = [$header => [$host]] + $this->headers; |
|
114 | - } |
|
115 | - /** |
|
116 | - * @param mixed $method |
|
117 | - */ |
|
118 | - private function assertMethod($method) : void |
|
119 | - { |
|
120 | - if (!\is_string($method) || $method === '') { |
|
121 | - throw new InvalidArgumentException('Method must be a non-empty string.'); |
|
122 | - } |
|
123 | - } |
|
15 | + use MessageTrait; |
|
16 | + /** @var string */ |
|
17 | + private $method; |
|
18 | + /** @var string|null */ |
|
19 | + private $requestTarget; |
|
20 | + /** @var UriInterface */ |
|
21 | + private $uri; |
|
22 | + /** |
|
23 | + * @param string $method HTTP method |
|
24 | + * @param string|UriInterface $uri URI |
|
25 | + * @param (string|string[])[] $headers Request headers |
|
26 | + * @param string|resource|StreamInterface|null $body Request body |
|
27 | + * @param string $version Protocol version |
|
28 | + */ |
|
29 | + public function __construct(string $method, $uri, array $headers = [], $body = null, string $version = '1.1') |
|
30 | + { |
|
31 | + $this->assertMethod($method); |
|
32 | + if (!$uri instanceof UriInterface) { |
|
33 | + $uri = new Uri($uri); |
|
34 | + } |
|
35 | + $this->method = \strtoupper($method); |
|
36 | + $this->uri = $uri; |
|
37 | + $this->setHeaders($headers); |
|
38 | + $this->protocol = $version; |
|
39 | + if (!isset($this->headerNames['host'])) { |
|
40 | + $this->updateHostFromUri(); |
|
41 | + } |
|
42 | + if ($body !== '' && $body !== null) { |
|
43 | + $this->stream = Utils::streamFor($body); |
|
44 | + } |
|
45 | + } |
|
46 | + public function getRequestTarget() : string |
|
47 | + { |
|
48 | + if ($this->requestTarget !== null) { |
|
49 | + return $this->requestTarget; |
|
50 | + } |
|
51 | + $target = $this->uri->getPath(); |
|
52 | + if ($target === '') { |
|
53 | + $target = '/'; |
|
54 | + } |
|
55 | + if ($this->uri->getQuery() != '') { |
|
56 | + $target .= '?' . $this->uri->getQuery(); |
|
57 | + } |
|
58 | + return $target; |
|
59 | + } |
|
60 | + public function withRequestTarget($requestTarget) : RequestInterface |
|
61 | + { |
|
62 | + if (\preg_match('#\\s#', $requestTarget)) { |
|
63 | + throw new InvalidArgumentException('Invalid request target provided; cannot contain whitespace'); |
|
64 | + } |
|
65 | + $new = clone $this; |
|
66 | + $new->requestTarget = $requestTarget; |
|
67 | + return $new; |
|
68 | + } |
|
69 | + public function getMethod() : string |
|
70 | + { |
|
71 | + return $this->method; |
|
72 | + } |
|
73 | + public function withMethod($method) : RequestInterface |
|
74 | + { |
|
75 | + $this->assertMethod($method); |
|
76 | + $new = clone $this; |
|
77 | + $new->method = \strtoupper($method); |
|
78 | + return $new; |
|
79 | + } |
|
80 | + public function getUri() : UriInterface |
|
81 | + { |
|
82 | + return $this->uri; |
|
83 | + } |
|
84 | + public function withUri(UriInterface $uri, $preserveHost = \false) : RequestInterface |
|
85 | + { |
|
86 | + if ($uri === $this->uri) { |
|
87 | + return $this; |
|
88 | + } |
|
89 | + $new = clone $this; |
|
90 | + $new->uri = $uri; |
|
91 | + if (!$preserveHost || !isset($this->headerNames['host'])) { |
|
92 | + $new->updateHostFromUri(); |
|
93 | + } |
|
94 | + return $new; |
|
95 | + } |
|
96 | + private function updateHostFromUri() : void |
|
97 | + { |
|
98 | + $host = $this->uri->getHost(); |
|
99 | + if ($host == '') { |
|
100 | + return; |
|
101 | + } |
|
102 | + if (($port = $this->uri->getPort()) !== null) { |
|
103 | + $host .= ':' . $port; |
|
104 | + } |
|
105 | + if (isset($this->headerNames['host'])) { |
|
106 | + $header = $this->headerNames['host']; |
|
107 | + } else { |
|
108 | + $header = 'Host'; |
|
109 | + $this->headerNames['host'] = 'Host'; |
|
110 | + } |
|
111 | + // Ensure Host is the first header. |
|
112 | + // See: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 |
|
113 | + $this->headers = [$header => [$host]] + $this->headers; |
|
114 | + } |
|
115 | + /** |
|
116 | + * @param mixed $method |
|
117 | + */ |
|
118 | + private function assertMethod($method) : void |
|
119 | + { |
|
120 | + if (!\is_string($method) || $method === '') { |
|
121 | + throw new InvalidArgumentException('Method must be a non-empty string.'); |
|
122 | + } |
|
123 | + } |
|
124 | 124 | } |
@@ -1,6 +1,6 @@ discard block |
||
1 | 1 | <?php |
2 | 2 | |
3 | -declare (strict_types=1); |
|
3 | +declare(strict_types=1); |
|
4 | 4 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
5 | 5 | |
6 | 6 | use InvalidArgumentException; |
@@ -53,7 +53,7 @@ discard block |
||
53 | 53 | $target = '/'; |
54 | 54 | } |
55 | 55 | if ($this->uri->getQuery() != '') { |
56 | - $target .= '?' . $this->uri->getQuery(); |
|
56 | + $target .= '?'.$this->uri->getQuery(); |
|
57 | 57 | } |
58 | 58 | return $target; |
59 | 59 | } |
@@ -100,7 +100,7 @@ discard block |
||
100 | 100 | return; |
101 | 101 | } |
102 | 102 | if (($port = $this->uri->getPort()) !== null) { |
103 | - $host .= ':' . $port; |
|
103 | + $host .= ':'.$port; |
|
104 | 104 | } |
105 | 105 | if (isset($this->headerNames['host'])) { |
106 | 106 | $header = $this->headerNames['host']; |
@@ -10,8 +10,7 @@ |
||
10 | 10 | /** |
11 | 11 | * PSR-7 request implementation. |
12 | 12 | */ |
13 | -class Request implements RequestInterface |
|
14 | -{ |
|
13 | +class Request implements RequestInterface { |
|
15 | 14 | use MessageTrait; |
16 | 15 | /** @var string */ |
17 | 16 | private $method; |
@@ -5,100 +5,100 @@ |
||
5 | 5 | |
6 | 6 | final class Query |
7 | 7 | { |
8 | - /** |
|
9 | - * Parse a query string into an associative array. |
|
10 | - * |
|
11 | - * If multiple values are found for the same key, the value of that key |
|
12 | - * value pair will become an array. This function does not parse nested |
|
13 | - * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` |
|
14 | - * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`. |
|
15 | - * |
|
16 | - * @param string $str Query string to parse |
|
17 | - * @param int|bool $urlEncoding How the query string is encoded |
|
18 | - */ |
|
19 | - public static function parse(string $str, $urlEncoding = \true) : array |
|
20 | - { |
|
21 | - $result = []; |
|
22 | - if ($str === '') { |
|
23 | - return $result; |
|
24 | - } |
|
25 | - if ($urlEncoding === \true) { |
|
26 | - $decoder = function ($value) { |
|
27 | - return \rawurldecode(\str_replace('+', ' ', (string) $value)); |
|
28 | - }; |
|
29 | - } elseif ($urlEncoding === \PHP_QUERY_RFC3986) { |
|
30 | - $decoder = 'rawurldecode'; |
|
31 | - } elseif ($urlEncoding === \PHP_QUERY_RFC1738) { |
|
32 | - $decoder = 'urldecode'; |
|
33 | - } else { |
|
34 | - $decoder = function ($str) { |
|
35 | - return $str; |
|
36 | - }; |
|
37 | - } |
|
38 | - foreach (\explode('&', $str) as $kvp) { |
|
39 | - $parts = \explode('=', $kvp, 2); |
|
40 | - $key = $decoder($parts[0]); |
|
41 | - $value = isset($parts[1]) ? $decoder($parts[1]) : null; |
|
42 | - if (!\array_key_exists($key, $result)) { |
|
43 | - $result[$key] = $value; |
|
44 | - } else { |
|
45 | - if (!\is_array($result[$key])) { |
|
46 | - $result[$key] = [$result[$key]]; |
|
47 | - } |
|
48 | - $result[$key][] = $value; |
|
49 | - } |
|
50 | - } |
|
51 | - return $result; |
|
52 | - } |
|
53 | - /** |
|
54 | - * Build a query string from an array of key value pairs. |
|
55 | - * |
|
56 | - * This function can use the return value of `parse()` to build a query |
|
57 | - * string. This function does not modify the provided keys when an array is |
|
58 | - * encountered (like `http_build_query()` would). |
|
59 | - * |
|
60 | - * @param array $params Query string parameters. |
|
61 | - * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 |
|
62 | - * to encode using RFC3986, or PHP_QUERY_RFC1738 |
|
63 | - * to encode using RFC1738. |
|
64 | - */ |
|
65 | - public static function build(array $params, $encoding = \PHP_QUERY_RFC3986) : string |
|
66 | - { |
|
67 | - if (!$params) { |
|
68 | - return ''; |
|
69 | - } |
|
70 | - if ($encoding === \false) { |
|
71 | - $encoder = function (string $str) : string { |
|
72 | - return $str; |
|
73 | - }; |
|
74 | - } elseif ($encoding === \PHP_QUERY_RFC3986) { |
|
75 | - $encoder = 'rawurlencode'; |
|
76 | - } elseif ($encoding === \PHP_QUERY_RFC1738) { |
|
77 | - $encoder = 'urlencode'; |
|
78 | - } else { |
|
79 | - throw new \InvalidArgumentException('Invalid type'); |
|
80 | - } |
|
81 | - $qs = ''; |
|
82 | - foreach ($params as $k => $v) { |
|
83 | - $k = $encoder((string) $k); |
|
84 | - if (!\is_array($v)) { |
|
85 | - $qs .= $k; |
|
86 | - $v = \is_bool($v) ? (int) $v : $v; |
|
87 | - if ($v !== null) { |
|
88 | - $qs .= '=' . $encoder((string) $v); |
|
89 | - } |
|
90 | - $qs .= '&'; |
|
91 | - } else { |
|
92 | - foreach ($v as $vv) { |
|
93 | - $qs .= $k; |
|
94 | - $vv = \is_bool($vv) ? (int) $vv : $vv; |
|
95 | - if ($vv !== null) { |
|
96 | - $qs .= '=' . $encoder((string) $vv); |
|
97 | - } |
|
98 | - $qs .= '&'; |
|
99 | - } |
|
100 | - } |
|
101 | - } |
|
102 | - return $qs ? (string) \substr($qs, 0, -1) : ''; |
|
103 | - } |
|
8 | + /** |
|
9 | + * Parse a query string into an associative array. |
|
10 | + * |
|
11 | + * If multiple values are found for the same key, the value of that key |
|
12 | + * value pair will become an array. This function does not parse nested |
|
13 | + * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` |
|
14 | + * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`. |
|
15 | + * |
|
16 | + * @param string $str Query string to parse |
|
17 | + * @param int|bool $urlEncoding How the query string is encoded |
|
18 | + */ |
|
19 | + public static function parse(string $str, $urlEncoding = \true) : array |
|
20 | + { |
|
21 | + $result = []; |
|
22 | + if ($str === '') { |
|
23 | + return $result; |
|
24 | + } |
|
25 | + if ($urlEncoding === \true) { |
|
26 | + $decoder = function ($value) { |
|
27 | + return \rawurldecode(\str_replace('+', ' ', (string) $value)); |
|
28 | + }; |
|
29 | + } elseif ($urlEncoding === \PHP_QUERY_RFC3986) { |
|
30 | + $decoder = 'rawurldecode'; |
|
31 | + } elseif ($urlEncoding === \PHP_QUERY_RFC1738) { |
|
32 | + $decoder = 'urldecode'; |
|
33 | + } else { |
|
34 | + $decoder = function ($str) { |
|
35 | + return $str; |
|
36 | + }; |
|
37 | + } |
|
38 | + foreach (\explode('&', $str) as $kvp) { |
|
39 | + $parts = \explode('=', $kvp, 2); |
|
40 | + $key = $decoder($parts[0]); |
|
41 | + $value = isset($parts[1]) ? $decoder($parts[1]) : null; |
|
42 | + if (!\array_key_exists($key, $result)) { |
|
43 | + $result[$key] = $value; |
|
44 | + } else { |
|
45 | + if (!\is_array($result[$key])) { |
|
46 | + $result[$key] = [$result[$key]]; |
|
47 | + } |
|
48 | + $result[$key][] = $value; |
|
49 | + } |
|
50 | + } |
|
51 | + return $result; |
|
52 | + } |
|
53 | + /** |
|
54 | + * Build a query string from an array of key value pairs. |
|
55 | + * |
|
56 | + * This function can use the return value of `parse()` to build a query |
|
57 | + * string. This function does not modify the provided keys when an array is |
|
58 | + * encountered (like `http_build_query()` would). |
|
59 | + * |
|
60 | + * @param array $params Query string parameters. |
|
61 | + * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986 |
|
62 | + * to encode using RFC3986, or PHP_QUERY_RFC1738 |
|
63 | + * to encode using RFC1738. |
|
64 | + */ |
|
65 | + public static function build(array $params, $encoding = \PHP_QUERY_RFC3986) : string |
|
66 | + { |
|
67 | + if (!$params) { |
|
68 | + return ''; |
|
69 | + } |
|
70 | + if ($encoding === \false) { |
|
71 | + $encoder = function (string $str) : string { |
|
72 | + return $str; |
|
73 | + }; |
|
74 | + } elseif ($encoding === \PHP_QUERY_RFC3986) { |
|
75 | + $encoder = 'rawurlencode'; |
|
76 | + } elseif ($encoding === \PHP_QUERY_RFC1738) { |
|
77 | + $encoder = 'urlencode'; |
|
78 | + } else { |
|
79 | + throw new \InvalidArgumentException('Invalid type'); |
|
80 | + } |
|
81 | + $qs = ''; |
|
82 | + foreach ($params as $k => $v) { |
|
83 | + $k = $encoder((string) $k); |
|
84 | + if (!\is_array($v)) { |
|
85 | + $qs .= $k; |
|
86 | + $v = \is_bool($v) ? (int) $v : $v; |
|
87 | + if ($v !== null) { |
|
88 | + $qs .= '=' . $encoder((string) $v); |
|
89 | + } |
|
90 | + $qs .= '&'; |
|
91 | + } else { |
|
92 | + foreach ($v as $vv) { |
|
93 | + $qs .= $k; |
|
94 | + $vv = \is_bool($vv) ? (int) $vv : $vv; |
|
95 | + if ($vv !== null) { |
|
96 | + $qs .= '=' . $encoder((string) $vv); |
|
97 | + } |
|
98 | + $qs .= '&'; |
|
99 | + } |
|
100 | + } |
|
101 | + } |
|
102 | + return $qs ? (string) \substr($qs, 0, -1) : ''; |
|
103 | + } |
|
104 | 104 | } |
@@ -1,6 +1,6 @@ discard block |
||
1 | 1 | <?php |
2 | 2 | |
3 | -declare (strict_types=1); |
|
3 | +declare(strict_types=1); |
|
4 | 4 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
5 | 5 | |
6 | 6 | final class Query |
@@ -23,15 +23,15 @@ discard block |
||
23 | 23 | return $result; |
24 | 24 | } |
25 | 25 | if ($urlEncoding === \true) { |
26 | - $decoder = function ($value) { |
|
27 | - return \rawurldecode(\str_replace('+', ' ', (string) $value)); |
|
26 | + $decoder = function($value) { |
|
27 | + return \rawurldecode(\str_replace('+', ' ', (string)$value)); |
|
28 | 28 | }; |
29 | 29 | } elseif ($urlEncoding === \PHP_QUERY_RFC3986) { |
30 | 30 | $decoder = 'rawurldecode'; |
31 | 31 | } elseif ($urlEncoding === \PHP_QUERY_RFC1738) { |
32 | 32 | $decoder = 'urldecode'; |
33 | 33 | } else { |
34 | - $decoder = function ($str) { |
|
34 | + $decoder = function($str) { |
|
35 | 35 | return $str; |
36 | 36 | }; |
37 | 37 | } |
@@ -68,7 +68,7 @@ discard block |
||
68 | 68 | return ''; |
69 | 69 | } |
70 | 70 | if ($encoding === \false) { |
71 | - $encoder = function (string $str) : string { |
|
71 | + $encoder = function(string $str) : string { |
|
72 | 72 | return $str; |
73 | 73 | }; |
74 | 74 | } elseif ($encoding === \PHP_QUERY_RFC3986) { |
@@ -80,25 +80,25 @@ discard block |
||
80 | 80 | } |
81 | 81 | $qs = ''; |
82 | 82 | foreach ($params as $k => $v) { |
83 | - $k = $encoder((string) $k); |
|
83 | + $k = $encoder((string)$k); |
|
84 | 84 | if (!\is_array($v)) { |
85 | 85 | $qs .= $k; |
86 | - $v = \is_bool($v) ? (int) $v : $v; |
|
86 | + $v = \is_bool($v) ? (int)$v : $v; |
|
87 | 87 | if ($v !== null) { |
88 | - $qs .= '=' . $encoder((string) $v); |
|
88 | + $qs .= '='.$encoder((string)$v); |
|
89 | 89 | } |
90 | 90 | $qs .= '&'; |
91 | 91 | } else { |
92 | 92 | foreach ($v as $vv) { |
93 | 93 | $qs .= $k; |
94 | - $vv = \is_bool($vv) ? (int) $vv : $vv; |
|
94 | + $vv = \is_bool($vv) ? (int)$vv : $vv; |
|
95 | 95 | if ($vv !== null) { |
96 | - $qs .= '=' . $encoder((string) $vv); |
|
96 | + $qs .= '='.$encoder((string)$vv); |
|
97 | 97 | } |
98 | 98 | $qs .= '&'; |
99 | 99 | } |
100 | 100 | } |
101 | 101 | } |
102 | - return $qs ? (string) \substr($qs, 0, -1) : ''; |
|
102 | + return $qs ? (string)\substr($qs, 0, -1) : ''; |
|
103 | 103 | } |
104 | 104 | } |
@@ -3,8 +3,7 @@ |
||
3 | 3 | declare (strict_types=1); |
4 | 4 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
5 | 5 | |
6 | -final class Query |
|
7 | -{ |
|
6 | +final class Query { |
|
8 | 7 | /** |
9 | 8 | * Parse a query string into an associative array. |
10 | 9 | * |
@@ -10,32 +10,32 @@ |
||
10 | 10 | */ |
11 | 11 | final class LazyOpenStream implements StreamInterface |
12 | 12 | { |
13 | - use StreamDecoratorTrait; |
|
14 | - /** @var string */ |
|
15 | - private $filename; |
|
16 | - /** @var string */ |
|
17 | - private $mode; |
|
18 | - /** |
|
19 | - * @var StreamInterface |
|
20 | - */ |
|
21 | - private $stream; |
|
22 | - /** |
|
23 | - * @param string $filename File to lazily open |
|
24 | - * @param string $mode fopen mode to use when opening the stream |
|
25 | - */ |
|
26 | - public function __construct(string $filename, string $mode) |
|
27 | - { |
|
28 | - $this->filename = $filename; |
|
29 | - $this->mode = $mode; |
|
30 | - // unsetting the property forces the first access to go through |
|
31 | - // __get(). |
|
32 | - unset($this->stream); |
|
33 | - } |
|
34 | - /** |
|
35 | - * Creates the underlying stream lazily when required. |
|
36 | - */ |
|
37 | - protected function createStream() : StreamInterface |
|
38 | - { |
|
39 | - return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode)); |
|
40 | - } |
|
13 | + use StreamDecoratorTrait; |
|
14 | + /** @var string */ |
|
15 | + private $filename; |
|
16 | + /** @var string */ |
|
17 | + private $mode; |
|
18 | + /** |
|
19 | + * @var StreamInterface |
|
20 | + */ |
|
21 | + private $stream; |
|
22 | + /** |
|
23 | + * @param string $filename File to lazily open |
|
24 | + * @param string $mode fopen mode to use when opening the stream |
|
25 | + */ |
|
26 | + public function __construct(string $filename, string $mode) |
|
27 | + { |
|
28 | + $this->filename = $filename; |
|
29 | + $this->mode = $mode; |
|
30 | + // unsetting the property forces the first access to go through |
|
31 | + // __get(). |
|
32 | + unset($this->stream); |
|
33 | + } |
|
34 | + /** |
|
35 | + * Creates the underlying stream lazily when required. |
|
36 | + */ |
|
37 | + protected function createStream() : StreamInterface |
|
38 | + { |
|
39 | + return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode)); |
|
40 | + } |
|
41 | 41 | } |
@@ -8,8 +8,7 @@ |
||
8 | 8 | * Lazily reads or writes to a file that is opened only after an IO operation |
9 | 9 | * take place on the stream. |
10 | 10 | */ |
11 | -final class LazyOpenStream implements StreamInterface |
|
12 | -{ |
|
11 | +final class LazyOpenStream implements StreamInterface { |
|
13 | 12 | use StreamDecoratorTrait; |
14 | 13 | /** @var string */ |
15 | 14 | private $filename; |
@@ -1,6 +1,6 @@ |
||
1 | 1 | <?php |
2 | 2 | |
3 | -declare (strict_types=1); |
|
3 | +declare(strict_types=1); |
|
4 | 4 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
5 | 5 | |
6 | 6 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\StreamInterface; |
@@ -10,31 +10,31 @@ |
||
10 | 10 | */ |
11 | 11 | final class DroppingStream implements StreamInterface |
12 | 12 | { |
13 | - use StreamDecoratorTrait; |
|
14 | - /** @var int */ |
|
15 | - private $maxLength; |
|
16 | - /** @var StreamInterface */ |
|
17 | - private $stream; |
|
18 | - /** |
|
19 | - * @param StreamInterface $stream Underlying stream to decorate. |
|
20 | - * @param int $maxLength Maximum size before dropping data. |
|
21 | - */ |
|
22 | - public function __construct(StreamInterface $stream, int $maxLength) |
|
23 | - { |
|
24 | - $this->stream = $stream; |
|
25 | - $this->maxLength = $maxLength; |
|
26 | - } |
|
27 | - public function write($string) : int |
|
28 | - { |
|
29 | - $diff = $this->maxLength - $this->stream->getSize(); |
|
30 | - // Begin returning 0 when the underlying stream is too large. |
|
31 | - if ($diff <= 0) { |
|
32 | - return 0; |
|
33 | - } |
|
34 | - // Write the stream or a subset of the stream if needed. |
|
35 | - if (\strlen($string) < $diff) { |
|
36 | - return $this->stream->write($string); |
|
37 | - } |
|
38 | - return $this->stream->write(\substr($string, 0, $diff)); |
|
39 | - } |
|
13 | + use StreamDecoratorTrait; |
|
14 | + /** @var int */ |
|
15 | + private $maxLength; |
|
16 | + /** @var StreamInterface */ |
|
17 | + private $stream; |
|
18 | + /** |
|
19 | + * @param StreamInterface $stream Underlying stream to decorate. |
|
20 | + * @param int $maxLength Maximum size before dropping data. |
|
21 | + */ |
|
22 | + public function __construct(StreamInterface $stream, int $maxLength) |
|
23 | + { |
|
24 | + $this->stream = $stream; |
|
25 | + $this->maxLength = $maxLength; |
|
26 | + } |
|
27 | + public function write($string) : int |
|
28 | + { |
|
29 | + $diff = $this->maxLength - $this->stream->getSize(); |
|
30 | + // Begin returning 0 when the underlying stream is too large. |
|
31 | + if ($diff <= 0) { |
|
32 | + return 0; |
|
33 | + } |
|
34 | + // Write the stream or a subset of the stream if needed. |
|
35 | + if (\strlen($string) < $diff) { |
|
36 | + return $this->stream->write($string); |
|
37 | + } |
|
38 | + return $this->stream->write(\substr($string, 0, $diff)); |
|
39 | + } |
|
40 | 40 | } |
@@ -8,8 +8,7 @@ |
||
8 | 8 | * Stream decorator that begins dropping data once the size of the underlying |
9 | 9 | * stream becomes too full. |
10 | 10 | */ |
11 | -final class DroppingStream implements StreamInterface |
|
12 | -{ |
|
11 | +final class DroppingStream implements StreamInterface { |
|
13 | 12 | use StreamDecoratorTrait; |
14 | 13 | /** @var int */ |
15 | 14 | private $maxLength; |
@@ -1,6 +1,6 @@ |
||
1 | 1 | <?php |
2 | 2 | |
3 | -declare (strict_types=1); |
|
3 | +declare(strict_types=1); |
|
4 | 4 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
5 | 5 | |
6 | 6 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\StreamInterface; |
@@ -9,367 +9,367 @@ |
||
9 | 9 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\UriInterface; |
10 | 10 | final class Utils |
11 | 11 | { |
12 | - /** |
|
13 | - * Remove the items given by the keys, case insensitively from the data. |
|
14 | - * |
|
15 | - * @param (string|int)[] $keys |
|
16 | - */ |
|
17 | - public static function caselessRemove(array $keys, array $data) : array |
|
18 | - { |
|
19 | - $result = []; |
|
20 | - foreach ($keys as &$key) { |
|
21 | - $key = \strtolower((string) $key); |
|
22 | - } |
|
23 | - foreach ($data as $k => $v) { |
|
24 | - if (!\in_array(\strtolower((string) $k), $keys)) { |
|
25 | - $result[$k] = $v; |
|
26 | - } |
|
27 | - } |
|
28 | - return $result; |
|
29 | - } |
|
30 | - /** |
|
31 | - * Copy the contents of a stream into another stream until the given number |
|
32 | - * of bytes have been read. |
|
33 | - * |
|
34 | - * @param StreamInterface $source Stream to read from |
|
35 | - * @param StreamInterface $dest Stream to write to |
|
36 | - * @param int $maxLen Maximum number of bytes to read. Pass -1 |
|
37 | - * to read the entire stream. |
|
38 | - * |
|
39 | - * @throws \RuntimeException on error. |
|
40 | - */ |
|
41 | - public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1) : void |
|
42 | - { |
|
43 | - $bufferSize = 8192; |
|
44 | - if ($maxLen === -1) { |
|
45 | - while (!$source->eof()) { |
|
46 | - if (!$dest->write($source->read($bufferSize))) { |
|
47 | - break; |
|
48 | - } |
|
49 | - } |
|
50 | - } else { |
|
51 | - $remaining = $maxLen; |
|
52 | - while ($remaining > 0 && !$source->eof()) { |
|
53 | - $buf = $source->read(\min($bufferSize, $remaining)); |
|
54 | - $len = \strlen($buf); |
|
55 | - if (!$len) { |
|
56 | - break; |
|
57 | - } |
|
58 | - $remaining -= $len; |
|
59 | - $dest->write($buf); |
|
60 | - } |
|
61 | - } |
|
62 | - } |
|
63 | - /** |
|
64 | - * Copy the contents of a stream into a string until the given number of |
|
65 | - * bytes have been read. |
|
66 | - * |
|
67 | - * @param StreamInterface $stream Stream to read |
|
68 | - * @param int $maxLen Maximum number of bytes to read. Pass -1 |
|
69 | - * to read the entire stream. |
|
70 | - * |
|
71 | - * @throws \RuntimeException on error. |
|
72 | - */ |
|
73 | - public static function copyToString(StreamInterface $stream, int $maxLen = -1) : string |
|
74 | - { |
|
75 | - $buffer = ''; |
|
76 | - if ($maxLen === -1) { |
|
77 | - while (!$stream->eof()) { |
|
78 | - $buf = $stream->read(1048576); |
|
79 | - if ($buf === '') { |
|
80 | - break; |
|
81 | - } |
|
82 | - $buffer .= $buf; |
|
83 | - } |
|
84 | - return $buffer; |
|
85 | - } |
|
86 | - $len = 0; |
|
87 | - while (!$stream->eof() && $len < $maxLen) { |
|
88 | - $buf = $stream->read($maxLen - $len); |
|
89 | - if ($buf === '') { |
|
90 | - break; |
|
91 | - } |
|
92 | - $buffer .= $buf; |
|
93 | - $len = \strlen($buffer); |
|
94 | - } |
|
95 | - return $buffer; |
|
96 | - } |
|
97 | - /** |
|
98 | - * Calculate a hash of a stream. |
|
99 | - * |
|
100 | - * This method reads the entire stream to calculate a rolling hash, based |
|
101 | - * on PHP's `hash_init` functions. |
|
102 | - * |
|
103 | - * @param StreamInterface $stream Stream to calculate the hash for |
|
104 | - * @param string $algo Hash algorithm (e.g. md5, crc32, etc) |
|
105 | - * @param bool $rawOutput Whether or not to use raw output |
|
106 | - * |
|
107 | - * @throws \RuntimeException on error. |
|
108 | - */ |
|
109 | - public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = \false) : string |
|
110 | - { |
|
111 | - $pos = $stream->tell(); |
|
112 | - if ($pos > 0) { |
|
113 | - $stream->rewind(); |
|
114 | - } |
|
115 | - $ctx = \hash_init($algo); |
|
116 | - while (!$stream->eof()) { |
|
117 | - \hash_update($ctx, $stream->read(1048576)); |
|
118 | - } |
|
119 | - $out = \hash_final($ctx, $rawOutput); |
|
120 | - $stream->seek($pos); |
|
121 | - return $out; |
|
122 | - } |
|
123 | - /** |
|
124 | - * Clone and modify a request with the given changes. |
|
125 | - * |
|
126 | - * This method is useful for reducing the number of clones needed to mutate |
|
127 | - * a message. |
|
128 | - * |
|
129 | - * The changes can be one of: |
|
130 | - * - method: (string) Changes the HTTP method. |
|
131 | - * - set_headers: (array) Sets the given headers. |
|
132 | - * - remove_headers: (array) Remove the given headers. |
|
133 | - * - body: (mixed) Sets the given body. |
|
134 | - * - uri: (UriInterface) Set the URI. |
|
135 | - * - query: (string) Set the query string value of the URI. |
|
136 | - * - version: (string) Set the protocol version. |
|
137 | - * |
|
138 | - * @param RequestInterface $request Request to clone and modify. |
|
139 | - * @param array $changes Changes to apply. |
|
140 | - */ |
|
141 | - public static function modifyRequest(RequestInterface $request, array $changes) : RequestInterface |
|
142 | - { |
|
143 | - if (!$changes) { |
|
144 | - return $request; |
|
145 | - } |
|
146 | - $headers = $request->getHeaders(); |
|
147 | - if (!isset($changes['uri'])) { |
|
148 | - $uri = $request->getUri(); |
|
149 | - } else { |
|
150 | - // Remove the host header if one is on the URI |
|
151 | - if ($host = $changes['uri']->getHost()) { |
|
152 | - $changes['set_headers']['Host'] = $host; |
|
153 | - if ($port = $changes['uri']->getPort()) { |
|
154 | - $standardPorts = ['http' => 80, 'https' => 443]; |
|
155 | - $scheme = $changes['uri']->getScheme(); |
|
156 | - if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { |
|
157 | - $changes['set_headers']['Host'] .= ':' . $port; |
|
158 | - } |
|
159 | - } |
|
160 | - } |
|
161 | - $uri = $changes['uri']; |
|
162 | - } |
|
163 | - if (!empty($changes['remove_headers'])) { |
|
164 | - $headers = self::caselessRemove($changes['remove_headers'], $headers); |
|
165 | - } |
|
166 | - if (!empty($changes['set_headers'])) { |
|
167 | - $headers = self::caselessRemove(\array_keys($changes['set_headers']), $headers); |
|
168 | - $headers = $changes['set_headers'] + $headers; |
|
169 | - } |
|
170 | - if (isset($changes['query'])) { |
|
171 | - $uri = $uri->withQuery($changes['query']); |
|
172 | - } |
|
173 | - if ($request instanceof ServerRequestInterface) { |
|
174 | - $new = (new ServerRequest($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion(), $request->getServerParams()))->withParsedBody($request->getParsedBody())->withQueryParams($request->getQueryParams())->withCookieParams($request->getCookieParams())->withUploadedFiles($request->getUploadedFiles()); |
|
175 | - foreach ($request->getAttributes() as $key => $value) { |
|
176 | - $new = $new->withAttribute($key, $value); |
|
177 | - } |
|
178 | - return $new; |
|
179 | - } |
|
180 | - return new Request($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion()); |
|
181 | - } |
|
182 | - /** |
|
183 | - * Read a line from the stream up to the maximum allowed buffer length. |
|
184 | - * |
|
185 | - * @param StreamInterface $stream Stream to read from |
|
186 | - * @param int|null $maxLength Maximum buffer length |
|
187 | - */ |
|
188 | - public static function readLine(StreamInterface $stream, int $maxLength = null) : string |
|
189 | - { |
|
190 | - $buffer = ''; |
|
191 | - $size = 0; |
|
192 | - while (!$stream->eof()) { |
|
193 | - if ('' === ($byte = $stream->read(1))) { |
|
194 | - return $buffer; |
|
195 | - } |
|
196 | - $buffer .= $byte; |
|
197 | - // Break when a new line is found or the max length - 1 is reached |
|
198 | - if ($byte === "\n" || ++$size === $maxLength - 1) { |
|
199 | - break; |
|
200 | - } |
|
201 | - } |
|
202 | - return $buffer; |
|
203 | - } |
|
204 | - /** |
|
205 | - * Create a new stream based on the input type. |
|
206 | - * |
|
207 | - * Options is an associative array that can contain the following keys: |
|
208 | - * - metadata: Array of custom metadata. |
|
209 | - * - size: Size of the stream. |
|
210 | - * |
|
211 | - * This method accepts the following `$resource` types: |
|
212 | - * - `Psr\Http\Message\StreamInterface`: Returns the value as-is. |
|
213 | - * - `string`: Creates a stream object that uses the given string as the contents. |
|
214 | - * - `resource`: Creates a stream object that wraps the given PHP stream resource. |
|
215 | - * - `Iterator`: If the provided value implements `Iterator`, then a read-only |
|
216 | - * stream object will be created that wraps the given iterable. Each time the |
|
217 | - * stream is read from, data from the iterator will fill a buffer and will be |
|
218 | - * continuously called until the buffer is equal to the requested read size. |
|
219 | - * Subsequent read calls will first read from the buffer and then call `next` |
|
220 | - * on the underlying iterator until it is exhausted. |
|
221 | - * - `object` with `__toString()`: If the object has the `__toString()` method, |
|
222 | - * the object will be cast to a string and then a stream will be returned that |
|
223 | - * uses the string value. |
|
224 | - * - `NULL`: When `null` is passed, an empty stream object is returned. |
|
225 | - * - `callable` When a callable is passed, a read-only stream object will be |
|
226 | - * created that invokes the given callable. The callable is invoked with the |
|
227 | - * number of suggested bytes to read. The callable can return any number of |
|
228 | - * bytes, but MUST return `false` when there is no more data to return. The |
|
229 | - * stream object that wraps the callable will invoke the callable until the |
|
230 | - * number of requested bytes are available. Any additional bytes will be |
|
231 | - * buffered and used in subsequent reads. |
|
232 | - * |
|
233 | - * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data |
|
234 | - * @param array{size?: int, metadata?: array} $options Additional options |
|
235 | - * |
|
236 | - * @throws \InvalidArgumentException if the $resource arg is not valid. |
|
237 | - */ |
|
238 | - public static function streamFor($resource = '', array $options = []) : StreamInterface |
|
239 | - { |
|
240 | - if (\is_scalar($resource)) { |
|
241 | - $stream = self::tryFopen('php://temp', 'r+'); |
|
242 | - if ($resource !== '') { |
|
243 | - \fwrite($stream, (string) $resource); |
|
244 | - \fseek($stream, 0); |
|
245 | - } |
|
246 | - return new Stream($stream, $options); |
|
247 | - } |
|
248 | - switch (\gettype($resource)) { |
|
249 | - case 'resource': |
|
250 | - /* |
|
12 | + /** |
|
13 | + * Remove the items given by the keys, case insensitively from the data. |
|
14 | + * |
|
15 | + * @param (string|int)[] $keys |
|
16 | + */ |
|
17 | + public static function caselessRemove(array $keys, array $data) : array |
|
18 | + { |
|
19 | + $result = []; |
|
20 | + foreach ($keys as &$key) { |
|
21 | + $key = \strtolower((string) $key); |
|
22 | + } |
|
23 | + foreach ($data as $k => $v) { |
|
24 | + if (!\in_array(\strtolower((string) $k), $keys)) { |
|
25 | + $result[$k] = $v; |
|
26 | + } |
|
27 | + } |
|
28 | + return $result; |
|
29 | + } |
|
30 | + /** |
|
31 | + * Copy the contents of a stream into another stream until the given number |
|
32 | + * of bytes have been read. |
|
33 | + * |
|
34 | + * @param StreamInterface $source Stream to read from |
|
35 | + * @param StreamInterface $dest Stream to write to |
|
36 | + * @param int $maxLen Maximum number of bytes to read. Pass -1 |
|
37 | + * to read the entire stream. |
|
38 | + * |
|
39 | + * @throws \RuntimeException on error. |
|
40 | + */ |
|
41 | + public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1) : void |
|
42 | + { |
|
43 | + $bufferSize = 8192; |
|
44 | + if ($maxLen === -1) { |
|
45 | + while (!$source->eof()) { |
|
46 | + if (!$dest->write($source->read($bufferSize))) { |
|
47 | + break; |
|
48 | + } |
|
49 | + } |
|
50 | + } else { |
|
51 | + $remaining = $maxLen; |
|
52 | + while ($remaining > 0 && !$source->eof()) { |
|
53 | + $buf = $source->read(\min($bufferSize, $remaining)); |
|
54 | + $len = \strlen($buf); |
|
55 | + if (!$len) { |
|
56 | + break; |
|
57 | + } |
|
58 | + $remaining -= $len; |
|
59 | + $dest->write($buf); |
|
60 | + } |
|
61 | + } |
|
62 | + } |
|
63 | + /** |
|
64 | + * Copy the contents of a stream into a string until the given number of |
|
65 | + * bytes have been read. |
|
66 | + * |
|
67 | + * @param StreamInterface $stream Stream to read |
|
68 | + * @param int $maxLen Maximum number of bytes to read. Pass -1 |
|
69 | + * to read the entire stream. |
|
70 | + * |
|
71 | + * @throws \RuntimeException on error. |
|
72 | + */ |
|
73 | + public static function copyToString(StreamInterface $stream, int $maxLen = -1) : string |
|
74 | + { |
|
75 | + $buffer = ''; |
|
76 | + if ($maxLen === -1) { |
|
77 | + while (!$stream->eof()) { |
|
78 | + $buf = $stream->read(1048576); |
|
79 | + if ($buf === '') { |
|
80 | + break; |
|
81 | + } |
|
82 | + $buffer .= $buf; |
|
83 | + } |
|
84 | + return $buffer; |
|
85 | + } |
|
86 | + $len = 0; |
|
87 | + while (!$stream->eof() && $len < $maxLen) { |
|
88 | + $buf = $stream->read($maxLen - $len); |
|
89 | + if ($buf === '') { |
|
90 | + break; |
|
91 | + } |
|
92 | + $buffer .= $buf; |
|
93 | + $len = \strlen($buffer); |
|
94 | + } |
|
95 | + return $buffer; |
|
96 | + } |
|
97 | + /** |
|
98 | + * Calculate a hash of a stream. |
|
99 | + * |
|
100 | + * This method reads the entire stream to calculate a rolling hash, based |
|
101 | + * on PHP's `hash_init` functions. |
|
102 | + * |
|
103 | + * @param StreamInterface $stream Stream to calculate the hash for |
|
104 | + * @param string $algo Hash algorithm (e.g. md5, crc32, etc) |
|
105 | + * @param bool $rawOutput Whether or not to use raw output |
|
106 | + * |
|
107 | + * @throws \RuntimeException on error. |
|
108 | + */ |
|
109 | + public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = \false) : string |
|
110 | + { |
|
111 | + $pos = $stream->tell(); |
|
112 | + if ($pos > 0) { |
|
113 | + $stream->rewind(); |
|
114 | + } |
|
115 | + $ctx = \hash_init($algo); |
|
116 | + while (!$stream->eof()) { |
|
117 | + \hash_update($ctx, $stream->read(1048576)); |
|
118 | + } |
|
119 | + $out = \hash_final($ctx, $rawOutput); |
|
120 | + $stream->seek($pos); |
|
121 | + return $out; |
|
122 | + } |
|
123 | + /** |
|
124 | + * Clone and modify a request with the given changes. |
|
125 | + * |
|
126 | + * This method is useful for reducing the number of clones needed to mutate |
|
127 | + * a message. |
|
128 | + * |
|
129 | + * The changes can be one of: |
|
130 | + * - method: (string) Changes the HTTP method. |
|
131 | + * - set_headers: (array) Sets the given headers. |
|
132 | + * - remove_headers: (array) Remove the given headers. |
|
133 | + * - body: (mixed) Sets the given body. |
|
134 | + * - uri: (UriInterface) Set the URI. |
|
135 | + * - query: (string) Set the query string value of the URI. |
|
136 | + * - version: (string) Set the protocol version. |
|
137 | + * |
|
138 | + * @param RequestInterface $request Request to clone and modify. |
|
139 | + * @param array $changes Changes to apply. |
|
140 | + */ |
|
141 | + public static function modifyRequest(RequestInterface $request, array $changes) : RequestInterface |
|
142 | + { |
|
143 | + if (!$changes) { |
|
144 | + return $request; |
|
145 | + } |
|
146 | + $headers = $request->getHeaders(); |
|
147 | + if (!isset($changes['uri'])) { |
|
148 | + $uri = $request->getUri(); |
|
149 | + } else { |
|
150 | + // Remove the host header if one is on the URI |
|
151 | + if ($host = $changes['uri']->getHost()) { |
|
152 | + $changes['set_headers']['Host'] = $host; |
|
153 | + if ($port = $changes['uri']->getPort()) { |
|
154 | + $standardPorts = ['http' => 80, 'https' => 443]; |
|
155 | + $scheme = $changes['uri']->getScheme(); |
|
156 | + if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { |
|
157 | + $changes['set_headers']['Host'] .= ':' . $port; |
|
158 | + } |
|
159 | + } |
|
160 | + } |
|
161 | + $uri = $changes['uri']; |
|
162 | + } |
|
163 | + if (!empty($changes['remove_headers'])) { |
|
164 | + $headers = self::caselessRemove($changes['remove_headers'], $headers); |
|
165 | + } |
|
166 | + if (!empty($changes['set_headers'])) { |
|
167 | + $headers = self::caselessRemove(\array_keys($changes['set_headers']), $headers); |
|
168 | + $headers = $changes['set_headers'] + $headers; |
|
169 | + } |
|
170 | + if (isset($changes['query'])) { |
|
171 | + $uri = $uri->withQuery($changes['query']); |
|
172 | + } |
|
173 | + if ($request instanceof ServerRequestInterface) { |
|
174 | + $new = (new ServerRequest($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion(), $request->getServerParams()))->withParsedBody($request->getParsedBody())->withQueryParams($request->getQueryParams())->withCookieParams($request->getCookieParams())->withUploadedFiles($request->getUploadedFiles()); |
|
175 | + foreach ($request->getAttributes() as $key => $value) { |
|
176 | + $new = $new->withAttribute($key, $value); |
|
177 | + } |
|
178 | + return $new; |
|
179 | + } |
|
180 | + return new Request($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion()); |
|
181 | + } |
|
182 | + /** |
|
183 | + * Read a line from the stream up to the maximum allowed buffer length. |
|
184 | + * |
|
185 | + * @param StreamInterface $stream Stream to read from |
|
186 | + * @param int|null $maxLength Maximum buffer length |
|
187 | + */ |
|
188 | + public static function readLine(StreamInterface $stream, int $maxLength = null) : string |
|
189 | + { |
|
190 | + $buffer = ''; |
|
191 | + $size = 0; |
|
192 | + while (!$stream->eof()) { |
|
193 | + if ('' === ($byte = $stream->read(1))) { |
|
194 | + return $buffer; |
|
195 | + } |
|
196 | + $buffer .= $byte; |
|
197 | + // Break when a new line is found or the max length - 1 is reached |
|
198 | + if ($byte === "\n" || ++$size === $maxLength - 1) { |
|
199 | + break; |
|
200 | + } |
|
201 | + } |
|
202 | + return $buffer; |
|
203 | + } |
|
204 | + /** |
|
205 | + * Create a new stream based on the input type. |
|
206 | + * |
|
207 | + * Options is an associative array that can contain the following keys: |
|
208 | + * - metadata: Array of custom metadata. |
|
209 | + * - size: Size of the stream. |
|
210 | + * |
|
211 | + * This method accepts the following `$resource` types: |
|
212 | + * - `Psr\Http\Message\StreamInterface`: Returns the value as-is. |
|
213 | + * - `string`: Creates a stream object that uses the given string as the contents. |
|
214 | + * - `resource`: Creates a stream object that wraps the given PHP stream resource. |
|
215 | + * - `Iterator`: If the provided value implements `Iterator`, then a read-only |
|
216 | + * stream object will be created that wraps the given iterable. Each time the |
|
217 | + * stream is read from, data from the iterator will fill a buffer and will be |
|
218 | + * continuously called until the buffer is equal to the requested read size. |
|
219 | + * Subsequent read calls will first read from the buffer and then call `next` |
|
220 | + * on the underlying iterator until it is exhausted. |
|
221 | + * - `object` with `__toString()`: If the object has the `__toString()` method, |
|
222 | + * the object will be cast to a string and then a stream will be returned that |
|
223 | + * uses the string value. |
|
224 | + * - `NULL`: When `null` is passed, an empty stream object is returned. |
|
225 | + * - `callable` When a callable is passed, a read-only stream object will be |
|
226 | + * created that invokes the given callable. The callable is invoked with the |
|
227 | + * number of suggested bytes to read. The callable can return any number of |
|
228 | + * bytes, but MUST return `false` when there is no more data to return. The |
|
229 | + * stream object that wraps the callable will invoke the callable until the |
|
230 | + * number of requested bytes are available. Any additional bytes will be |
|
231 | + * buffered and used in subsequent reads. |
|
232 | + * |
|
233 | + * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data |
|
234 | + * @param array{size?: int, metadata?: array} $options Additional options |
|
235 | + * |
|
236 | + * @throws \InvalidArgumentException if the $resource arg is not valid. |
|
237 | + */ |
|
238 | + public static function streamFor($resource = '', array $options = []) : StreamInterface |
|
239 | + { |
|
240 | + if (\is_scalar($resource)) { |
|
241 | + $stream = self::tryFopen('php://temp', 'r+'); |
|
242 | + if ($resource !== '') { |
|
243 | + \fwrite($stream, (string) $resource); |
|
244 | + \fseek($stream, 0); |
|
245 | + } |
|
246 | + return new Stream($stream, $options); |
|
247 | + } |
|
248 | + switch (\gettype($resource)) { |
|
249 | + case 'resource': |
|
250 | + /* |
|
251 | 251 | * The 'php://input' is a special stream with quirks and inconsistencies. |
252 | 252 | * We avoid using that stream by reading it into php://temp |
253 | 253 | */ |
254 | - /** @var resource $resource */ |
|
255 | - if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') { |
|
256 | - $stream = self::tryFopen('php://temp', 'w+'); |
|
257 | - \stream_copy_to_stream($resource, $stream); |
|
258 | - \fseek($stream, 0); |
|
259 | - $resource = $stream; |
|
260 | - } |
|
261 | - return new Stream($resource, $options); |
|
262 | - case 'object': |
|
263 | - /** @var object $resource */ |
|
264 | - if ($resource instanceof StreamInterface) { |
|
265 | - return $resource; |
|
266 | - } elseif ($resource instanceof \Iterator) { |
|
267 | - return new PumpStream(function () use($resource) { |
|
268 | - if (!$resource->valid()) { |
|
269 | - return \false; |
|
270 | - } |
|
271 | - $result = $resource->current(); |
|
272 | - $resource->next(); |
|
273 | - return $result; |
|
274 | - }, $options); |
|
275 | - } elseif (\method_exists($resource, '__toString')) { |
|
276 | - return self::streamFor((string) $resource, $options); |
|
277 | - } |
|
278 | - break; |
|
279 | - case 'NULL': |
|
280 | - return new Stream(self::tryFopen('php://temp', 'r+'), $options); |
|
281 | - } |
|
282 | - if (\is_callable($resource)) { |
|
283 | - return new PumpStream($resource, $options); |
|
284 | - } |
|
285 | - throw new \InvalidArgumentException('Invalid resource type: ' . \gettype($resource)); |
|
286 | - } |
|
287 | - /** |
|
288 | - * Safely opens a PHP stream resource using a filename. |
|
289 | - * |
|
290 | - * When fopen fails, PHP normally raises a warning. This function adds an |
|
291 | - * error handler that checks for errors and throws an exception instead. |
|
292 | - * |
|
293 | - * @param string $filename File to open |
|
294 | - * @param string $mode Mode used to open the file |
|
295 | - * |
|
296 | - * @return resource |
|
297 | - * |
|
298 | - * @throws \RuntimeException if the file cannot be opened |
|
299 | - */ |
|
300 | - public static function tryFopen(string $filename, string $mode) |
|
301 | - { |
|
302 | - $ex = null; |
|
303 | - \set_error_handler(static function (int $errno, string $errstr) use($filename, $mode, &$ex) : bool { |
|
304 | - $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $errstr)); |
|
305 | - return \true; |
|
306 | - }); |
|
307 | - try { |
|
308 | - /** @var resource $handle */ |
|
309 | - $handle = \fopen($filename, $mode); |
|
310 | - } catch (\Throwable $e) { |
|
311 | - $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $e->getMessage()), 0, $e); |
|
312 | - } |
|
313 | - \restore_error_handler(); |
|
314 | - if ($ex) { |
|
315 | - /** @var $ex \RuntimeException */ |
|
316 | - throw $ex; |
|
317 | - } |
|
318 | - return $handle; |
|
319 | - } |
|
320 | - /** |
|
321 | - * Safely gets the contents of a given stream. |
|
322 | - * |
|
323 | - * When stream_get_contents fails, PHP normally raises a warning. This |
|
324 | - * function adds an error handler that checks for errors and throws an |
|
325 | - * exception instead. |
|
326 | - * |
|
327 | - * @param resource $stream |
|
328 | - * |
|
329 | - * @throws \RuntimeException if the stream cannot be read |
|
330 | - */ |
|
331 | - public static function tryGetContents($stream) : string |
|
332 | - { |
|
333 | - $ex = null; |
|
334 | - \set_error_handler(static function (int $errno, string $errstr) use(&$ex) : bool { |
|
335 | - $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $errstr)); |
|
336 | - return \true; |
|
337 | - }); |
|
338 | - try { |
|
339 | - /** @var string|false $contents */ |
|
340 | - $contents = \stream_get_contents($stream); |
|
341 | - if ($contents === \false) { |
|
342 | - $ex = new \RuntimeException('Unable to read stream contents'); |
|
343 | - } |
|
344 | - } catch (\Throwable $e) { |
|
345 | - $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $e->getMessage()), 0, $e); |
|
346 | - } |
|
347 | - \restore_error_handler(); |
|
348 | - if ($ex) { |
|
349 | - /** @var $ex \RuntimeException */ |
|
350 | - throw $ex; |
|
351 | - } |
|
352 | - return $contents; |
|
353 | - } |
|
354 | - /** |
|
355 | - * Returns a UriInterface for the given value. |
|
356 | - * |
|
357 | - * This function accepts a string or UriInterface and returns a |
|
358 | - * UriInterface for the given value. If the value is already a |
|
359 | - * UriInterface, it is returned as-is. |
|
360 | - * |
|
361 | - * @param string|UriInterface $uri |
|
362 | - * |
|
363 | - * @throws \InvalidArgumentException |
|
364 | - */ |
|
365 | - public static function uriFor($uri) : UriInterface |
|
366 | - { |
|
367 | - if ($uri instanceof UriInterface) { |
|
368 | - return $uri; |
|
369 | - } |
|
370 | - if (\is_string($uri)) { |
|
371 | - return new Uri($uri); |
|
372 | - } |
|
373 | - throw new \InvalidArgumentException('URI must be a string or UriInterface'); |
|
374 | - } |
|
254 | + /** @var resource $resource */ |
|
255 | + if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') { |
|
256 | + $stream = self::tryFopen('php://temp', 'w+'); |
|
257 | + \stream_copy_to_stream($resource, $stream); |
|
258 | + \fseek($stream, 0); |
|
259 | + $resource = $stream; |
|
260 | + } |
|
261 | + return new Stream($resource, $options); |
|
262 | + case 'object': |
|
263 | + /** @var object $resource */ |
|
264 | + if ($resource instanceof StreamInterface) { |
|
265 | + return $resource; |
|
266 | + } elseif ($resource instanceof \Iterator) { |
|
267 | + return new PumpStream(function () use($resource) { |
|
268 | + if (!$resource->valid()) { |
|
269 | + return \false; |
|
270 | + } |
|
271 | + $result = $resource->current(); |
|
272 | + $resource->next(); |
|
273 | + return $result; |
|
274 | + }, $options); |
|
275 | + } elseif (\method_exists($resource, '__toString')) { |
|
276 | + return self::streamFor((string) $resource, $options); |
|
277 | + } |
|
278 | + break; |
|
279 | + case 'NULL': |
|
280 | + return new Stream(self::tryFopen('php://temp', 'r+'), $options); |
|
281 | + } |
|
282 | + if (\is_callable($resource)) { |
|
283 | + return new PumpStream($resource, $options); |
|
284 | + } |
|
285 | + throw new \InvalidArgumentException('Invalid resource type: ' . \gettype($resource)); |
|
286 | + } |
|
287 | + /** |
|
288 | + * Safely opens a PHP stream resource using a filename. |
|
289 | + * |
|
290 | + * When fopen fails, PHP normally raises a warning. This function adds an |
|
291 | + * error handler that checks for errors and throws an exception instead. |
|
292 | + * |
|
293 | + * @param string $filename File to open |
|
294 | + * @param string $mode Mode used to open the file |
|
295 | + * |
|
296 | + * @return resource |
|
297 | + * |
|
298 | + * @throws \RuntimeException if the file cannot be opened |
|
299 | + */ |
|
300 | + public static function tryFopen(string $filename, string $mode) |
|
301 | + { |
|
302 | + $ex = null; |
|
303 | + \set_error_handler(static function (int $errno, string $errstr) use($filename, $mode, &$ex) : bool { |
|
304 | + $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $errstr)); |
|
305 | + return \true; |
|
306 | + }); |
|
307 | + try { |
|
308 | + /** @var resource $handle */ |
|
309 | + $handle = \fopen($filename, $mode); |
|
310 | + } catch (\Throwable $e) { |
|
311 | + $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $e->getMessage()), 0, $e); |
|
312 | + } |
|
313 | + \restore_error_handler(); |
|
314 | + if ($ex) { |
|
315 | + /** @var $ex \RuntimeException */ |
|
316 | + throw $ex; |
|
317 | + } |
|
318 | + return $handle; |
|
319 | + } |
|
320 | + /** |
|
321 | + * Safely gets the contents of a given stream. |
|
322 | + * |
|
323 | + * When stream_get_contents fails, PHP normally raises a warning. This |
|
324 | + * function adds an error handler that checks for errors and throws an |
|
325 | + * exception instead. |
|
326 | + * |
|
327 | + * @param resource $stream |
|
328 | + * |
|
329 | + * @throws \RuntimeException if the stream cannot be read |
|
330 | + */ |
|
331 | + public static function tryGetContents($stream) : string |
|
332 | + { |
|
333 | + $ex = null; |
|
334 | + \set_error_handler(static function (int $errno, string $errstr) use(&$ex) : bool { |
|
335 | + $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $errstr)); |
|
336 | + return \true; |
|
337 | + }); |
|
338 | + try { |
|
339 | + /** @var string|false $contents */ |
|
340 | + $contents = \stream_get_contents($stream); |
|
341 | + if ($contents === \false) { |
|
342 | + $ex = new \RuntimeException('Unable to read stream contents'); |
|
343 | + } |
|
344 | + } catch (\Throwable $e) { |
|
345 | + $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $e->getMessage()), 0, $e); |
|
346 | + } |
|
347 | + \restore_error_handler(); |
|
348 | + if ($ex) { |
|
349 | + /** @var $ex \RuntimeException */ |
|
350 | + throw $ex; |
|
351 | + } |
|
352 | + return $contents; |
|
353 | + } |
|
354 | + /** |
|
355 | + * Returns a UriInterface for the given value. |
|
356 | + * |
|
357 | + * This function accepts a string or UriInterface and returns a |
|
358 | + * UriInterface for the given value. If the value is already a |
|
359 | + * UriInterface, it is returned as-is. |
|
360 | + * |
|
361 | + * @param string|UriInterface $uri |
|
362 | + * |
|
363 | + * @throws \InvalidArgumentException |
|
364 | + */ |
|
365 | + public static function uriFor($uri) : UriInterface |
|
366 | + { |
|
367 | + if ($uri instanceof UriInterface) { |
|
368 | + return $uri; |
|
369 | + } |
|
370 | + if (\is_string($uri)) { |
|
371 | + return new Uri($uri); |
|
372 | + } |
|
373 | + throw new \InvalidArgumentException('URI must be a string or UriInterface'); |
|
374 | + } |
|
375 | 375 | } |
@@ -1,6 +1,6 @@ discard block |
||
1 | 1 | <?php |
2 | 2 | |
3 | -declare (strict_types=1); |
|
3 | +declare(strict_types=1); |
|
4 | 4 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7; |
5 | 5 | |
6 | 6 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\RequestInterface; |
@@ -18,10 +18,10 @@ discard block |
||
18 | 18 | { |
19 | 19 | $result = []; |
20 | 20 | foreach ($keys as &$key) { |
21 | - $key = \strtolower((string) $key); |
|
21 | + $key = \strtolower((string)$key); |
|
22 | 22 | } |
23 | 23 | foreach ($data as $k => $v) { |
24 | - if (!\in_array(\strtolower((string) $k), $keys)) { |
|
24 | + if (!\in_array(\strtolower((string)$k), $keys)) { |
|
25 | 25 | $result[$k] = $v; |
26 | 26 | } |
27 | 27 | } |
@@ -154,7 +154,7 @@ discard block |
||
154 | 154 | $standardPorts = ['http' => 80, 'https' => 443]; |
155 | 155 | $scheme = $changes['uri']->getScheme(); |
156 | 156 | if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) { |
157 | - $changes['set_headers']['Host'] .= ':' . $port; |
|
157 | + $changes['set_headers']['Host'] .= ':'.$port; |
|
158 | 158 | } |
159 | 159 | } |
160 | 160 | } |
@@ -240,7 +240,7 @@ discard block |
||
240 | 240 | if (\is_scalar($resource)) { |
241 | 241 | $stream = self::tryFopen('php://temp', 'r+'); |
242 | 242 | if ($resource !== '') { |
243 | - \fwrite($stream, (string) $resource); |
|
243 | + \fwrite($stream, (string)$resource); |
|
244 | 244 | \fseek($stream, 0); |
245 | 245 | } |
246 | 246 | return new Stream($stream, $options); |
@@ -264,7 +264,7 @@ discard block |
||
264 | 264 | if ($resource instanceof StreamInterface) { |
265 | 265 | return $resource; |
266 | 266 | } elseif ($resource instanceof \Iterator) { |
267 | - return new PumpStream(function () use($resource) { |
|
267 | + return new PumpStream(function() use($resource) { |
|
268 | 268 | if (!$resource->valid()) { |
269 | 269 | return \false; |
270 | 270 | } |
@@ -273,7 +273,7 @@ discard block |
||
273 | 273 | return $result; |
274 | 274 | }, $options); |
275 | 275 | } elseif (\method_exists($resource, '__toString')) { |
276 | - return self::streamFor((string) $resource, $options); |
|
276 | + return self::streamFor((string)$resource, $options); |
|
277 | 277 | } |
278 | 278 | break; |
279 | 279 | case 'NULL': |
@@ -282,7 +282,7 @@ discard block |
||
282 | 282 | if (\is_callable($resource)) { |
283 | 283 | return new PumpStream($resource, $options); |
284 | 284 | } |
285 | - throw new \InvalidArgumentException('Invalid resource type: ' . \gettype($resource)); |
|
285 | + throw new \InvalidArgumentException('Invalid resource type: '.\gettype($resource)); |
|
286 | 286 | } |
287 | 287 | /** |
288 | 288 | * Safely opens a PHP stream resource using a filename. |
@@ -300,7 +300,7 @@ discard block |
||
300 | 300 | public static function tryFopen(string $filename, string $mode) |
301 | 301 | { |
302 | 302 | $ex = null; |
303 | - \set_error_handler(static function (int $errno, string $errstr) use($filename, $mode, &$ex) : bool { |
|
303 | + \set_error_handler(static function(int $errno, string $errstr) use($filename, $mode, &$ex) : bool { |
|
304 | 304 | $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $errstr)); |
305 | 305 | return \true; |
306 | 306 | }); |
@@ -331,7 +331,7 @@ discard block |
||
331 | 331 | public static function tryGetContents($stream) : string |
332 | 332 | { |
333 | 333 | $ex = null; |
334 | - \set_error_handler(static function (int $errno, string $errstr) use(&$ex) : bool { |
|
334 | + \set_error_handler(static function(int $errno, string $errstr) use(&$ex) : bool { |
|
335 | 335 | $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $errstr)); |
336 | 336 | return \true; |
337 | 337 | }); |
@@ -7,8 +7,7 @@ |
||
7 | 7 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\ServerRequestInterface; |
8 | 8 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\StreamInterface; |
9 | 9 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Message\UriInterface; |
10 | -final class Utils |
|
11 | -{ |
|
10 | +final class Utils { |
|
12 | 11 | /** |
13 | 12 | * Remove the items given by the keys, case insensitively from the data. |
14 | 13 | * |
@@ -9,23 +9,23 @@ |
||
9 | 9 | */ |
10 | 10 | class BadResponseException extends RequestException |
11 | 11 | { |
12 | - public function __construct(string $message, RequestInterface $request, ResponseInterface $response, \Throwable $previous = null, array $handlerContext = []) |
|
13 | - { |
|
14 | - parent::__construct($message, $request, $response, $previous, $handlerContext); |
|
15 | - } |
|
16 | - /** |
|
17 | - * Current exception and the ones that extend it will always have a response. |
|
18 | - */ |
|
19 | - public function hasResponse() : bool |
|
20 | - { |
|
21 | - return \true; |
|
22 | - } |
|
23 | - /** |
|
24 | - * This function narrows the return type from the parent class and does not allow it to be nullable. |
|
25 | - */ |
|
26 | - public function getResponse() : ResponseInterface |
|
27 | - { |
|
28 | - /** @var ResponseInterface */ |
|
29 | - return parent::getResponse(); |
|
30 | - } |
|
12 | + public function __construct(string $message, RequestInterface $request, ResponseInterface $response, \Throwable $previous = null, array $handlerContext = []) |
|
13 | + { |
|
14 | + parent::__construct($message, $request, $response, $previous, $handlerContext); |
|
15 | + } |
|
16 | + /** |
|
17 | + * Current exception and the ones that extend it will always have a response. |
|
18 | + */ |
|
19 | + public function hasResponse() : bool |
|
20 | + { |
|
21 | + return \true; |
|
22 | + } |
|
23 | + /** |
|
24 | + * This function narrows the return type from the parent class and does not allow it to be nullable. |
|
25 | + */ |
|
26 | + public function getResponse() : ResponseInterface |
|
27 | + { |
|
28 | + /** @var ResponseInterface */ |
|
29 | + return parent::getResponse(); |
|
30 | + } |
|
31 | 31 | } |
@@ -7,8 +7,7 @@ |
||
7 | 7 | /** |
8 | 8 | * Exception when an HTTP error occurs (4xx or 5xx error) |
9 | 9 | */ |
10 | -class BadResponseException extends RequestException |
|
11 | -{ |
|
10 | +class BadResponseException extends RequestException { |
|
12 | 11 | public function __construct(string $message, RequestInterface $request, ResponseInterface $response, \Throwable $previous = null, array $handlerContext = []) |
13 | 12 | { |
14 | 13 | parent::__construct($message, $request, $response, $previous, $handlerContext); |
@@ -3,6 +3,5 @@ |
||
3 | 3 | namespace OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Exception; |
4 | 4 | |
5 | 5 | use OCA\FullTextSearch_Elasticsearch\Vendor\Psr\Http\Client\ClientExceptionInterface; |
6 | -interface GuzzleException extends ClientExceptionInterface |
|
7 | -{ |
|
6 | +interface GuzzleException extends ClientExceptionInterface { |
|
8 | 7 | } |
@@ -18,479 +18,479 @@ |
||
18 | 18 | */ |
19 | 19 | class CurlFactory implements CurlFactoryInterface |
20 | 20 | { |
21 | - public const CURL_VERSION_STR = 'curl_version'; |
|
22 | - /** |
|
23 | - * @deprecated |
|
24 | - */ |
|
25 | - public const LOW_CURL_VERSION_NUMBER = '7.21.2'; |
|
26 | - /** |
|
27 | - * @var resource[]|\CurlHandle[] |
|
28 | - */ |
|
29 | - private $handles = []; |
|
30 | - /** |
|
31 | - * @var int Total number of idle handles to keep in cache |
|
32 | - */ |
|
33 | - private $maxHandles; |
|
34 | - /** |
|
35 | - * @param int $maxHandles Maximum number of idle handles. |
|
36 | - */ |
|
37 | - public function __construct(int $maxHandles) |
|
38 | - { |
|
39 | - $this->maxHandles = $maxHandles; |
|
40 | - } |
|
41 | - public function create(RequestInterface $request, array $options) : EasyHandle |
|
42 | - { |
|
43 | - if (isset($options['curl']['body_as_string'])) { |
|
44 | - $options['_body_as_string'] = $options['curl']['body_as_string']; |
|
45 | - unset($options['curl']['body_as_string']); |
|
46 | - } |
|
47 | - $easy = new EasyHandle(); |
|
48 | - $easy->request = $request; |
|
49 | - $easy->options = $options; |
|
50 | - $conf = $this->getDefaultConf($easy); |
|
51 | - $this->applyMethod($easy, $conf); |
|
52 | - $this->applyHandlerOptions($easy, $conf); |
|
53 | - $this->applyHeaders($easy, $conf); |
|
54 | - unset($conf['_headers']); |
|
55 | - // Add handler options from the request configuration options |
|
56 | - if (isset($options['curl'])) { |
|
57 | - $conf = \array_replace($conf, $options['curl']); |
|
58 | - } |
|
59 | - $conf[\CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); |
|
60 | - $easy->handle = $this->handles ? \array_pop($this->handles) : \curl_init(); |
|
61 | - \curl_setopt_array($easy->handle, $conf); |
|
62 | - return $easy; |
|
63 | - } |
|
64 | - public function release(EasyHandle $easy) : void |
|
65 | - { |
|
66 | - $resource = $easy->handle; |
|
67 | - unset($easy->handle); |
|
68 | - if (\count($this->handles) >= $this->maxHandles) { |
|
69 | - \curl_close($resource); |
|
70 | - } else { |
|
71 | - // Remove all callback functions as they can hold onto references |
|
72 | - // and are not cleaned up by curl_reset. Using curl_setopt_array |
|
73 | - // does not work for some reason, so removing each one |
|
74 | - // individually. |
|
75 | - \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null); |
|
76 | - \curl_setopt($resource, \CURLOPT_READFUNCTION, null); |
|
77 | - \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null); |
|
78 | - \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null); |
|
79 | - \curl_reset($resource); |
|
80 | - $this->handles[] = $resource; |
|
81 | - } |
|
82 | - } |
|
83 | - /** |
|
84 | - * Completes a cURL transaction, either returning a response promise or a |
|
85 | - * rejected promise. |
|
86 | - * |
|
87 | - * @param callable(RequestInterface, array): PromiseInterface $handler |
|
88 | - * @param CurlFactoryInterface $factory Dictates how the handle is released |
|
89 | - */ |
|
90 | - public static function finish(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory) : PromiseInterface |
|
91 | - { |
|
92 | - if (isset($easy->options['on_stats'])) { |
|
93 | - self::invokeStats($easy); |
|
94 | - } |
|
95 | - if (!$easy->response || $easy->errno) { |
|
96 | - return self::finishError($handler, $easy, $factory); |
|
97 | - } |
|
98 | - // Return the response if it is present and there is no error. |
|
99 | - $factory->release($easy); |
|
100 | - // Rewind the body of the response if possible. |
|
101 | - $body = $easy->response->getBody(); |
|
102 | - if ($body->isSeekable()) { |
|
103 | - $body->rewind(); |
|
104 | - } |
|
105 | - return new FulfilledPromise($easy->response); |
|
106 | - } |
|
107 | - private static function invokeStats(EasyHandle $easy) : void |
|
108 | - { |
|
109 | - $curlStats = \curl_getinfo($easy->handle); |
|
110 | - $curlStats['appconnect_time'] = \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME); |
|
111 | - $stats = new TransferStats($easy->request, $easy->response, $curlStats['total_time'], $easy->errno, $curlStats); |
|
112 | - $easy->options['on_stats']($stats); |
|
113 | - } |
|
114 | - /** |
|
115 | - * @param callable(RequestInterface, array): PromiseInterface $handler |
|
116 | - */ |
|
117 | - private static function finishError(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory) : PromiseInterface |
|
118 | - { |
|
119 | - // Get error information and release the handle to the factory. |
|
120 | - $ctx = ['errno' => $easy->errno, 'error' => \curl_error($easy->handle), 'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME)] + \curl_getinfo($easy->handle); |
|
121 | - $ctx[self::CURL_VERSION_STR] = \curl_version()['version']; |
|
122 | - $factory->release($easy); |
|
123 | - // Retry when nothing is present or when curl failed to rewind. |
|
124 | - if (empty($easy->options['_err_message']) && (!$easy->errno || $easy->errno == 65)) { |
|
125 | - return self::retryFailedRewind($handler, $easy, $ctx); |
|
126 | - } |
|
127 | - return self::createRejection($easy, $ctx); |
|
128 | - } |
|
129 | - private static function createRejection(EasyHandle $easy, array $ctx) : PromiseInterface |
|
130 | - { |
|
131 | - static $connectionErrors = [\CURLE_OPERATION_TIMEOUTED => \true, \CURLE_COULDNT_RESOLVE_HOST => \true, \CURLE_COULDNT_CONNECT => \true, \CURLE_SSL_CONNECT_ERROR => \true, \CURLE_GOT_NOTHING => \true]; |
|
132 | - if ($easy->createResponseException) { |
|
133 | - return P\Create::rejectionFor(new RequestException('An error was encountered while creating the response', $easy->request, $easy->response, $easy->createResponseException, $ctx)); |
|
134 | - } |
|
135 | - // If an exception was encountered during the onHeaders event, then |
|
136 | - // return a rejected promise that wraps that exception. |
|
137 | - if ($easy->onHeadersException) { |
|
138 | - return P\Create::rejectionFor(new RequestException('An error was encountered during the on_headers event', $easy->request, $easy->response, $easy->onHeadersException, $ctx)); |
|
139 | - } |
|
140 | - $message = \sprintf('cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'); |
|
141 | - $uriString = (string) $easy->request->getUri(); |
|
142 | - if ($uriString !== '' && \false === \strpos($ctx['error'], $uriString)) { |
|
143 | - $message .= \sprintf(' for %s', $uriString); |
|
144 | - } |
|
145 | - // Create a connection exception if it was a specific error code. |
|
146 | - $error = isset($connectionErrors[$easy->errno]) ? new ConnectException($message, $easy->request, null, $ctx) : new RequestException($message, $easy->request, $easy->response, null, $ctx); |
|
147 | - return P\Create::rejectionFor($error); |
|
148 | - } |
|
149 | - /** |
|
150 | - * @return array<int|string, mixed> |
|
151 | - */ |
|
152 | - private function getDefaultConf(EasyHandle $easy) : array |
|
153 | - { |
|
154 | - $conf = ['_headers' => $easy->request->getHeaders(), \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), \CURLOPT_RETURNTRANSFER => \false, \CURLOPT_HEADER => \false, \CURLOPT_CONNECTTIMEOUT => 300]; |
|
155 | - if (\defined('CURLOPT_PROTOCOLS')) { |
|
156 | - $conf[\CURLOPT_PROTOCOLS] = \CURLPROTO_HTTP | \CURLPROTO_HTTPS; |
|
157 | - } |
|
158 | - $version = $easy->request->getProtocolVersion(); |
|
159 | - if ($version == 1.1) { |
|
160 | - $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1; |
|
161 | - } elseif ($version == 2.0) { |
|
162 | - $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0; |
|
163 | - } else { |
|
164 | - $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0; |
|
165 | - } |
|
166 | - return $conf; |
|
167 | - } |
|
168 | - private function applyMethod(EasyHandle $easy, array &$conf) : void |
|
169 | - { |
|
170 | - $body = $easy->request->getBody(); |
|
171 | - $size = $body->getSize(); |
|
172 | - if ($size === null || $size > 0) { |
|
173 | - $this->applyBody($easy->request, $easy->options, $conf); |
|
174 | - return; |
|
175 | - } |
|
176 | - $method = $easy->request->getMethod(); |
|
177 | - if ($method === 'PUT' || $method === 'POST') { |
|
178 | - // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 |
|
179 | - if (!$easy->request->hasHeader('Content-Length')) { |
|
180 | - $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; |
|
181 | - } |
|
182 | - } elseif ($method === 'HEAD') { |
|
183 | - $conf[\CURLOPT_NOBODY] = \true; |
|
184 | - unset($conf[\CURLOPT_WRITEFUNCTION], $conf[\CURLOPT_READFUNCTION], $conf[\CURLOPT_FILE], $conf[\CURLOPT_INFILE]); |
|
185 | - } |
|
186 | - } |
|
187 | - private function applyBody(RequestInterface $request, array $options, array &$conf) : void |
|
188 | - { |
|
189 | - $size = $request->hasHeader('Content-Length') ? (int) $request->getHeaderLine('Content-Length') : null; |
|
190 | - // Send the body as a string if the size is less than 1MB OR if the |
|
191 | - // [curl][body_as_string] request value is set. |
|
192 | - if ($size !== null && $size < 1000000 || !empty($options['_body_as_string'])) { |
|
193 | - $conf[\CURLOPT_POSTFIELDS] = (string) $request->getBody(); |
|
194 | - // Don't duplicate the Content-Length header |
|
195 | - $this->removeHeader('Content-Length', $conf); |
|
196 | - $this->removeHeader('Transfer-Encoding', $conf); |
|
197 | - } else { |
|
198 | - $conf[\CURLOPT_UPLOAD] = \true; |
|
199 | - if ($size !== null) { |
|
200 | - $conf[\CURLOPT_INFILESIZE] = $size; |
|
201 | - $this->removeHeader('Content-Length', $conf); |
|
202 | - } |
|
203 | - $body = $request->getBody(); |
|
204 | - if ($body->isSeekable()) { |
|
205 | - $body->rewind(); |
|
206 | - } |
|
207 | - $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use($body) { |
|
208 | - return $body->read($length); |
|
209 | - }; |
|
210 | - } |
|
211 | - // If the Expect header is not present, prevent curl from adding it |
|
212 | - if (!$request->hasHeader('Expect')) { |
|
213 | - $conf[\CURLOPT_HTTPHEADER][] = 'Expect:'; |
|
214 | - } |
|
215 | - // cURL sometimes adds a content-type by default. Prevent this. |
|
216 | - if (!$request->hasHeader('Content-Type')) { |
|
217 | - $conf[\CURLOPT_HTTPHEADER][] = 'Content-Type:'; |
|
218 | - } |
|
219 | - } |
|
220 | - private function applyHeaders(EasyHandle $easy, array &$conf) : void |
|
221 | - { |
|
222 | - foreach ($conf['_headers'] as $name => $values) { |
|
223 | - foreach ($values as $value) { |
|
224 | - $value = (string) $value; |
|
225 | - if ($value === '') { |
|
226 | - // cURL requires a special format for empty headers. |
|
227 | - // See https://github.com/guzzle/guzzle/issues/1882 for more details. |
|
228 | - $conf[\CURLOPT_HTTPHEADER][] = "{$name};"; |
|
229 | - } else { |
|
230 | - $conf[\CURLOPT_HTTPHEADER][] = "{$name}: {$value}"; |
|
231 | - } |
|
232 | - } |
|
233 | - } |
|
234 | - // Remove the Accept header if one was not set |
|
235 | - if (!$easy->request->hasHeader('Accept')) { |
|
236 | - $conf[\CURLOPT_HTTPHEADER][] = 'Accept:'; |
|
237 | - } |
|
238 | - } |
|
239 | - /** |
|
240 | - * Remove a header from the options array. |
|
241 | - * |
|
242 | - * @param string $name Case-insensitive header to remove |
|
243 | - * @param array $options Array of options to modify |
|
244 | - */ |
|
245 | - private function removeHeader(string $name, array &$options) : void |
|
246 | - { |
|
247 | - foreach (\array_keys($options['_headers']) as $key) { |
|
248 | - if (!\strcasecmp($key, $name)) { |
|
249 | - unset($options['_headers'][$key]); |
|
250 | - return; |
|
251 | - } |
|
252 | - } |
|
253 | - } |
|
254 | - private function applyHandlerOptions(EasyHandle $easy, array &$conf) : void |
|
255 | - { |
|
256 | - $options = $easy->options; |
|
257 | - if (isset($options['verify'])) { |
|
258 | - if ($options['verify'] === \false) { |
|
259 | - unset($conf[\CURLOPT_CAINFO]); |
|
260 | - $conf[\CURLOPT_SSL_VERIFYHOST] = 0; |
|
261 | - $conf[\CURLOPT_SSL_VERIFYPEER] = \false; |
|
262 | - } else { |
|
263 | - $conf[\CURLOPT_SSL_VERIFYHOST] = 2; |
|
264 | - $conf[\CURLOPT_SSL_VERIFYPEER] = \true; |
|
265 | - if (\is_string($options['verify'])) { |
|
266 | - // Throw an error if the file/folder/link path is not valid or doesn't exist. |
|
267 | - if (!\file_exists($options['verify'])) { |
|
268 | - throw new \InvalidArgumentException("SSL CA bundle not found: {$options['verify']}"); |
|
269 | - } |
|
270 | - // If it's a directory or a link to a directory use CURLOPT_CAPATH. |
|
271 | - // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. |
|
272 | - if (\is_dir($options['verify']) || \is_link($options['verify']) === \true && ($verifyLink = \readlink($options['verify'])) !== \false && \is_dir($verifyLink)) { |
|
273 | - $conf[\CURLOPT_CAPATH] = $options['verify']; |
|
274 | - } else { |
|
275 | - $conf[\CURLOPT_CAINFO] = $options['verify']; |
|
276 | - } |
|
277 | - } |
|
278 | - } |
|
279 | - } |
|
280 | - if (!isset($options['curl'][\CURLOPT_ENCODING]) && !empty($options['decode_content'])) { |
|
281 | - $accept = $easy->request->getHeaderLine('Accept-Encoding'); |
|
282 | - if ($accept) { |
|
283 | - $conf[\CURLOPT_ENCODING] = $accept; |
|
284 | - } else { |
|
285 | - // The empty string enables all available decoders and implicitly |
|
286 | - // sets a matching 'Accept-Encoding' header. |
|
287 | - $conf[\CURLOPT_ENCODING] = ''; |
|
288 | - // But as the user did not specify any acceptable encodings we need |
|
289 | - // to overwrite this implicit header with an empty one. |
|
290 | - $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; |
|
291 | - } |
|
292 | - } |
|
293 | - if (!isset($options['sink'])) { |
|
294 | - // Use a default temp stream if no sink was set. |
|
295 | - $options['sink'] = \OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+'); |
|
296 | - } |
|
297 | - $sink = $options['sink']; |
|
298 | - if (!\is_string($sink)) { |
|
299 | - $sink = \OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7\Utils::streamFor($sink); |
|
300 | - } elseif (!\is_dir(\dirname($sink))) { |
|
301 | - // Ensure that the directory exists before failing in curl. |
|
302 | - throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink)); |
|
303 | - } else { |
|
304 | - $sink = new LazyOpenStream($sink, 'w+'); |
|
305 | - } |
|
306 | - $easy->sink = $sink; |
|
307 | - $conf[\CURLOPT_WRITEFUNCTION] = static function ($ch, $write) use($sink) : int { |
|
308 | - return $sink->write($write); |
|
309 | - }; |
|
310 | - $timeoutRequiresNoSignal = \false; |
|
311 | - if (isset($options['timeout'])) { |
|
312 | - $timeoutRequiresNoSignal |= $options['timeout'] < 1; |
|
313 | - $conf[\CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; |
|
314 | - } |
|
315 | - // CURL default value is CURL_IPRESOLVE_WHATEVER |
|
316 | - if (isset($options['force_ip_resolve'])) { |
|
317 | - if ('v4' === $options['force_ip_resolve']) { |
|
318 | - $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V4; |
|
319 | - } elseif ('v6' === $options['force_ip_resolve']) { |
|
320 | - $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V6; |
|
321 | - } |
|
322 | - } |
|
323 | - if (isset($options['connect_timeout'])) { |
|
324 | - $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; |
|
325 | - $conf[\CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; |
|
326 | - } |
|
327 | - if ($timeoutRequiresNoSignal && \strtoupper(\substr(\PHP_OS, 0, 3)) !== 'WIN') { |
|
328 | - $conf[\CURLOPT_NOSIGNAL] = \true; |
|
329 | - } |
|
330 | - if (isset($options['proxy'])) { |
|
331 | - if (!\is_array($options['proxy'])) { |
|
332 | - $conf[\CURLOPT_PROXY] = $options['proxy']; |
|
333 | - } else { |
|
334 | - $scheme = $easy->request->getUri()->getScheme(); |
|
335 | - if (isset($options['proxy'][$scheme])) { |
|
336 | - $host = $easy->request->getUri()->getHost(); |
|
337 | - if (isset($options['proxy']['no']) && Utils::isHostInNoProxy($host, $options['proxy']['no'])) { |
|
338 | - unset($conf[\CURLOPT_PROXY]); |
|
339 | - } else { |
|
340 | - $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme]; |
|
341 | - } |
|
342 | - } |
|
343 | - } |
|
344 | - } |
|
345 | - if (isset($options['crypto_method'])) { |
|
346 | - if (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) { |
|
347 | - if (!\defined('CURL_SSLVERSION_TLSv1_0')) { |
|
348 | - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.0 not supported by your version of cURL'); |
|
349 | - } |
|
350 | - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0; |
|
351 | - } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) { |
|
352 | - if (!\defined('CURL_SSLVERSION_TLSv1_1')) { |
|
353 | - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.1 not supported by your version of cURL'); |
|
354 | - } |
|
355 | - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1; |
|
356 | - } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) { |
|
357 | - if (!\defined('CURL_SSLVERSION_TLSv1_2')) { |
|
358 | - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL'); |
|
359 | - } |
|
360 | - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; |
|
361 | - } elseif (\defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { |
|
362 | - if (!\defined('CURL_SSLVERSION_TLSv1_3')) { |
|
363 | - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); |
|
364 | - } |
|
365 | - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; |
|
366 | - } else { |
|
367 | - throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); |
|
368 | - } |
|
369 | - } |
|
370 | - if (isset($options['cert'])) { |
|
371 | - $cert = $options['cert']; |
|
372 | - if (\is_array($cert)) { |
|
373 | - $conf[\CURLOPT_SSLCERTPASSWD] = $cert[1]; |
|
374 | - $cert = $cert[0]; |
|
375 | - } |
|
376 | - if (!\file_exists($cert)) { |
|
377 | - throw new \InvalidArgumentException("SSL certificate not found: {$cert}"); |
|
378 | - } |
|
379 | - // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files. |
|
380 | - // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html |
|
381 | - $ext = \pathinfo($cert, \PATHINFO_EXTENSION); |
|
382 | - if (\preg_match('#^(der|p12)$#i', $ext)) { |
|
383 | - $conf[\CURLOPT_SSLCERTTYPE] = \strtoupper($ext); |
|
384 | - } |
|
385 | - $conf[\CURLOPT_SSLCERT] = $cert; |
|
386 | - } |
|
387 | - if (isset($options['ssl_key'])) { |
|
388 | - if (\is_array($options['ssl_key'])) { |
|
389 | - if (\count($options['ssl_key']) === 2) { |
|
390 | - [$sslKey, $conf[\CURLOPT_SSLKEYPASSWD]] = $options['ssl_key']; |
|
391 | - } else { |
|
392 | - [$sslKey] = $options['ssl_key']; |
|
393 | - } |
|
394 | - } |
|
395 | - $sslKey = $sslKey ?? $options['ssl_key']; |
|
396 | - if (!\file_exists($sslKey)) { |
|
397 | - throw new \InvalidArgumentException("SSL private key not found: {$sslKey}"); |
|
398 | - } |
|
399 | - $conf[\CURLOPT_SSLKEY] = $sslKey; |
|
400 | - } |
|
401 | - if (isset($options['progress'])) { |
|
402 | - $progress = $options['progress']; |
|
403 | - if (!\is_callable($progress)) { |
|
404 | - throw new \InvalidArgumentException('progress client option must be callable'); |
|
405 | - } |
|
406 | - $conf[\CURLOPT_NOPROGRESS] = \false; |
|
407 | - $conf[\CURLOPT_PROGRESSFUNCTION] = static function ($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use($progress) { |
|
408 | - $progress($downloadSize, $downloaded, $uploadSize, $uploaded); |
|
409 | - }; |
|
410 | - } |
|
411 | - if (!empty($options['debug'])) { |
|
412 | - $conf[\CURLOPT_STDERR] = Utils::debugResource($options['debug']); |
|
413 | - $conf[\CURLOPT_VERBOSE] = \true; |
|
414 | - } |
|
415 | - } |
|
416 | - /** |
|
417 | - * This function ensures that a response was set on a transaction. If one |
|
418 | - * was not set, then the request is retried if possible. This error |
|
419 | - * typically means you are sending a payload, curl encountered a |
|
420 | - * "Connection died, retrying a fresh connect" error, tried to rewind the |
|
421 | - * stream, and then encountered a "necessary data rewind wasn't possible" |
|
422 | - * error, causing the request to be sent through curl_multi_info_read() |
|
423 | - * without an error status. |
|
424 | - * |
|
425 | - * @param callable(RequestInterface, array): PromiseInterface $handler |
|
426 | - */ |
|
427 | - private static function retryFailedRewind(callable $handler, EasyHandle $easy, array $ctx) : PromiseInterface |
|
428 | - { |
|
429 | - try { |
|
430 | - // Only rewind if the body has been read from. |
|
431 | - $body = $easy->request->getBody(); |
|
432 | - if ($body->tell() > 0) { |
|
433 | - $body->rewind(); |
|
434 | - } |
|
435 | - } catch (\RuntimeException $e) { |
|
436 | - $ctx['error'] = 'The connection unexpectedly failed without ' . 'providing an error. The request would have been retried, ' . 'but attempting to rewind the request body failed. ' . 'Exception: ' . $e; |
|
437 | - return self::createRejection($easy, $ctx); |
|
438 | - } |
|
439 | - // Retry no more than 3 times before giving up. |
|
440 | - if (!isset($easy->options['_curl_retries'])) { |
|
441 | - $easy->options['_curl_retries'] = 1; |
|
442 | - } elseif ($easy->options['_curl_retries'] == 2) { |
|
443 | - $ctx['error'] = 'The cURL request was retried 3 times ' . 'and did not succeed. The most likely reason for the failure ' . 'is that cURL was unable to rewind the body of the request ' . 'and subsequent retries resulted in the same error. Turn on ' . 'the debug option to see what went wrong. See ' . 'https://bugs.php.net/bug.php?id=47204 for more information.'; |
|
444 | - return self::createRejection($easy, $ctx); |
|
445 | - } else { |
|
446 | - ++$easy->options['_curl_retries']; |
|
447 | - } |
|
448 | - return $handler($easy->request, $easy->options); |
|
449 | - } |
|
450 | - private function createHeaderFn(EasyHandle $easy) : callable |
|
451 | - { |
|
452 | - if (isset($easy->options['on_headers'])) { |
|
453 | - $onHeaders = $easy->options['on_headers']; |
|
454 | - if (!\is_callable($onHeaders)) { |
|
455 | - throw new \InvalidArgumentException('on_headers must be callable'); |
|
456 | - } |
|
457 | - } else { |
|
458 | - $onHeaders = null; |
|
459 | - } |
|
460 | - return static function ($ch, $h) use($onHeaders, $easy, &$startingResponse) { |
|
461 | - $value = \trim($h); |
|
462 | - if ($value === '') { |
|
463 | - $startingResponse = \true; |
|
464 | - try { |
|
465 | - $easy->createResponse(); |
|
466 | - } catch (\Exception $e) { |
|
467 | - $easy->createResponseException = $e; |
|
468 | - return -1; |
|
469 | - } |
|
470 | - if ($onHeaders !== null) { |
|
471 | - try { |
|
472 | - $onHeaders($easy->response); |
|
473 | - } catch (\Exception $e) { |
|
474 | - // Associate the exception with the handle and trigger |
|
475 | - // a curl header write error by returning 0. |
|
476 | - $easy->onHeadersException = $e; |
|
477 | - return -1; |
|
478 | - } |
|
479 | - } |
|
480 | - } elseif ($startingResponse) { |
|
481 | - $startingResponse = \false; |
|
482 | - $easy->headers = [$value]; |
|
483 | - } else { |
|
484 | - $easy->headers[] = $value; |
|
485 | - } |
|
486 | - return \strlen($h); |
|
487 | - }; |
|
488 | - } |
|
489 | - public function __destruct() |
|
490 | - { |
|
491 | - foreach ($this->handles as $id => $handle) { |
|
492 | - \curl_close($handle); |
|
493 | - unset($this->handles[$id]); |
|
494 | - } |
|
495 | - } |
|
21 | + public const CURL_VERSION_STR = 'curl_version'; |
|
22 | + /** |
|
23 | + * @deprecated |
|
24 | + */ |
|
25 | + public const LOW_CURL_VERSION_NUMBER = '7.21.2'; |
|
26 | + /** |
|
27 | + * @var resource[]|\CurlHandle[] |
|
28 | + */ |
|
29 | + private $handles = []; |
|
30 | + /** |
|
31 | + * @var int Total number of idle handles to keep in cache |
|
32 | + */ |
|
33 | + private $maxHandles; |
|
34 | + /** |
|
35 | + * @param int $maxHandles Maximum number of idle handles. |
|
36 | + */ |
|
37 | + public function __construct(int $maxHandles) |
|
38 | + { |
|
39 | + $this->maxHandles = $maxHandles; |
|
40 | + } |
|
41 | + public function create(RequestInterface $request, array $options) : EasyHandle |
|
42 | + { |
|
43 | + if (isset($options['curl']['body_as_string'])) { |
|
44 | + $options['_body_as_string'] = $options['curl']['body_as_string']; |
|
45 | + unset($options['curl']['body_as_string']); |
|
46 | + } |
|
47 | + $easy = new EasyHandle(); |
|
48 | + $easy->request = $request; |
|
49 | + $easy->options = $options; |
|
50 | + $conf = $this->getDefaultConf($easy); |
|
51 | + $this->applyMethod($easy, $conf); |
|
52 | + $this->applyHandlerOptions($easy, $conf); |
|
53 | + $this->applyHeaders($easy, $conf); |
|
54 | + unset($conf['_headers']); |
|
55 | + // Add handler options from the request configuration options |
|
56 | + if (isset($options['curl'])) { |
|
57 | + $conf = \array_replace($conf, $options['curl']); |
|
58 | + } |
|
59 | + $conf[\CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); |
|
60 | + $easy->handle = $this->handles ? \array_pop($this->handles) : \curl_init(); |
|
61 | + \curl_setopt_array($easy->handle, $conf); |
|
62 | + return $easy; |
|
63 | + } |
|
64 | + public function release(EasyHandle $easy) : void |
|
65 | + { |
|
66 | + $resource = $easy->handle; |
|
67 | + unset($easy->handle); |
|
68 | + if (\count($this->handles) >= $this->maxHandles) { |
|
69 | + \curl_close($resource); |
|
70 | + } else { |
|
71 | + // Remove all callback functions as they can hold onto references |
|
72 | + // and are not cleaned up by curl_reset. Using curl_setopt_array |
|
73 | + // does not work for some reason, so removing each one |
|
74 | + // individually. |
|
75 | + \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null); |
|
76 | + \curl_setopt($resource, \CURLOPT_READFUNCTION, null); |
|
77 | + \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null); |
|
78 | + \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null); |
|
79 | + \curl_reset($resource); |
|
80 | + $this->handles[] = $resource; |
|
81 | + } |
|
82 | + } |
|
83 | + /** |
|
84 | + * Completes a cURL transaction, either returning a response promise or a |
|
85 | + * rejected promise. |
|
86 | + * |
|
87 | + * @param callable(RequestInterface, array): PromiseInterface $handler |
|
88 | + * @param CurlFactoryInterface $factory Dictates how the handle is released |
|
89 | + */ |
|
90 | + public static function finish(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory) : PromiseInterface |
|
91 | + { |
|
92 | + if (isset($easy->options['on_stats'])) { |
|
93 | + self::invokeStats($easy); |
|
94 | + } |
|
95 | + if (!$easy->response || $easy->errno) { |
|
96 | + return self::finishError($handler, $easy, $factory); |
|
97 | + } |
|
98 | + // Return the response if it is present and there is no error. |
|
99 | + $factory->release($easy); |
|
100 | + // Rewind the body of the response if possible. |
|
101 | + $body = $easy->response->getBody(); |
|
102 | + if ($body->isSeekable()) { |
|
103 | + $body->rewind(); |
|
104 | + } |
|
105 | + return new FulfilledPromise($easy->response); |
|
106 | + } |
|
107 | + private static function invokeStats(EasyHandle $easy) : void |
|
108 | + { |
|
109 | + $curlStats = \curl_getinfo($easy->handle); |
|
110 | + $curlStats['appconnect_time'] = \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME); |
|
111 | + $stats = new TransferStats($easy->request, $easy->response, $curlStats['total_time'], $easy->errno, $curlStats); |
|
112 | + $easy->options['on_stats']($stats); |
|
113 | + } |
|
114 | + /** |
|
115 | + * @param callable(RequestInterface, array): PromiseInterface $handler |
|
116 | + */ |
|
117 | + private static function finishError(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory) : PromiseInterface |
|
118 | + { |
|
119 | + // Get error information and release the handle to the factory. |
|
120 | + $ctx = ['errno' => $easy->errno, 'error' => \curl_error($easy->handle), 'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME)] + \curl_getinfo($easy->handle); |
|
121 | + $ctx[self::CURL_VERSION_STR] = \curl_version()['version']; |
|
122 | + $factory->release($easy); |
|
123 | + // Retry when nothing is present or when curl failed to rewind. |
|
124 | + if (empty($easy->options['_err_message']) && (!$easy->errno || $easy->errno == 65)) { |
|
125 | + return self::retryFailedRewind($handler, $easy, $ctx); |
|
126 | + } |
|
127 | + return self::createRejection($easy, $ctx); |
|
128 | + } |
|
129 | + private static function createRejection(EasyHandle $easy, array $ctx) : PromiseInterface |
|
130 | + { |
|
131 | + static $connectionErrors = [\CURLE_OPERATION_TIMEOUTED => \true, \CURLE_COULDNT_RESOLVE_HOST => \true, \CURLE_COULDNT_CONNECT => \true, \CURLE_SSL_CONNECT_ERROR => \true, \CURLE_GOT_NOTHING => \true]; |
|
132 | + if ($easy->createResponseException) { |
|
133 | + return P\Create::rejectionFor(new RequestException('An error was encountered while creating the response', $easy->request, $easy->response, $easy->createResponseException, $ctx)); |
|
134 | + } |
|
135 | + // If an exception was encountered during the onHeaders event, then |
|
136 | + // return a rejected promise that wraps that exception. |
|
137 | + if ($easy->onHeadersException) { |
|
138 | + return P\Create::rejectionFor(new RequestException('An error was encountered during the on_headers event', $easy->request, $easy->response, $easy->onHeadersException, $ctx)); |
|
139 | + } |
|
140 | + $message = \sprintf('cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'); |
|
141 | + $uriString = (string) $easy->request->getUri(); |
|
142 | + if ($uriString !== '' && \false === \strpos($ctx['error'], $uriString)) { |
|
143 | + $message .= \sprintf(' for %s', $uriString); |
|
144 | + } |
|
145 | + // Create a connection exception if it was a specific error code. |
|
146 | + $error = isset($connectionErrors[$easy->errno]) ? new ConnectException($message, $easy->request, null, $ctx) : new RequestException($message, $easy->request, $easy->response, null, $ctx); |
|
147 | + return P\Create::rejectionFor($error); |
|
148 | + } |
|
149 | + /** |
|
150 | + * @return array<int|string, mixed> |
|
151 | + */ |
|
152 | + private function getDefaultConf(EasyHandle $easy) : array |
|
153 | + { |
|
154 | + $conf = ['_headers' => $easy->request->getHeaders(), \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), \CURLOPT_RETURNTRANSFER => \false, \CURLOPT_HEADER => \false, \CURLOPT_CONNECTTIMEOUT => 300]; |
|
155 | + if (\defined('CURLOPT_PROTOCOLS')) { |
|
156 | + $conf[\CURLOPT_PROTOCOLS] = \CURLPROTO_HTTP | \CURLPROTO_HTTPS; |
|
157 | + } |
|
158 | + $version = $easy->request->getProtocolVersion(); |
|
159 | + if ($version == 1.1) { |
|
160 | + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1; |
|
161 | + } elseif ($version == 2.0) { |
|
162 | + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0; |
|
163 | + } else { |
|
164 | + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0; |
|
165 | + } |
|
166 | + return $conf; |
|
167 | + } |
|
168 | + private function applyMethod(EasyHandle $easy, array &$conf) : void |
|
169 | + { |
|
170 | + $body = $easy->request->getBody(); |
|
171 | + $size = $body->getSize(); |
|
172 | + if ($size === null || $size > 0) { |
|
173 | + $this->applyBody($easy->request, $easy->options, $conf); |
|
174 | + return; |
|
175 | + } |
|
176 | + $method = $easy->request->getMethod(); |
|
177 | + if ($method === 'PUT' || $method === 'POST') { |
|
178 | + // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 |
|
179 | + if (!$easy->request->hasHeader('Content-Length')) { |
|
180 | + $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; |
|
181 | + } |
|
182 | + } elseif ($method === 'HEAD') { |
|
183 | + $conf[\CURLOPT_NOBODY] = \true; |
|
184 | + unset($conf[\CURLOPT_WRITEFUNCTION], $conf[\CURLOPT_READFUNCTION], $conf[\CURLOPT_FILE], $conf[\CURLOPT_INFILE]); |
|
185 | + } |
|
186 | + } |
|
187 | + private function applyBody(RequestInterface $request, array $options, array &$conf) : void |
|
188 | + { |
|
189 | + $size = $request->hasHeader('Content-Length') ? (int) $request->getHeaderLine('Content-Length') : null; |
|
190 | + // Send the body as a string if the size is less than 1MB OR if the |
|
191 | + // [curl][body_as_string] request value is set. |
|
192 | + if ($size !== null && $size < 1000000 || !empty($options['_body_as_string'])) { |
|
193 | + $conf[\CURLOPT_POSTFIELDS] = (string) $request->getBody(); |
|
194 | + // Don't duplicate the Content-Length header |
|
195 | + $this->removeHeader('Content-Length', $conf); |
|
196 | + $this->removeHeader('Transfer-Encoding', $conf); |
|
197 | + } else { |
|
198 | + $conf[\CURLOPT_UPLOAD] = \true; |
|
199 | + if ($size !== null) { |
|
200 | + $conf[\CURLOPT_INFILESIZE] = $size; |
|
201 | + $this->removeHeader('Content-Length', $conf); |
|
202 | + } |
|
203 | + $body = $request->getBody(); |
|
204 | + if ($body->isSeekable()) { |
|
205 | + $body->rewind(); |
|
206 | + } |
|
207 | + $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use($body) { |
|
208 | + return $body->read($length); |
|
209 | + }; |
|
210 | + } |
|
211 | + // If the Expect header is not present, prevent curl from adding it |
|
212 | + if (!$request->hasHeader('Expect')) { |
|
213 | + $conf[\CURLOPT_HTTPHEADER][] = 'Expect:'; |
|
214 | + } |
|
215 | + // cURL sometimes adds a content-type by default. Prevent this. |
|
216 | + if (!$request->hasHeader('Content-Type')) { |
|
217 | + $conf[\CURLOPT_HTTPHEADER][] = 'Content-Type:'; |
|
218 | + } |
|
219 | + } |
|
220 | + private function applyHeaders(EasyHandle $easy, array &$conf) : void |
|
221 | + { |
|
222 | + foreach ($conf['_headers'] as $name => $values) { |
|
223 | + foreach ($values as $value) { |
|
224 | + $value = (string) $value; |
|
225 | + if ($value === '') { |
|
226 | + // cURL requires a special format for empty headers. |
|
227 | + // See https://github.com/guzzle/guzzle/issues/1882 for more details. |
|
228 | + $conf[\CURLOPT_HTTPHEADER][] = "{$name};"; |
|
229 | + } else { |
|
230 | + $conf[\CURLOPT_HTTPHEADER][] = "{$name}: {$value}"; |
|
231 | + } |
|
232 | + } |
|
233 | + } |
|
234 | + // Remove the Accept header if one was not set |
|
235 | + if (!$easy->request->hasHeader('Accept')) { |
|
236 | + $conf[\CURLOPT_HTTPHEADER][] = 'Accept:'; |
|
237 | + } |
|
238 | + } |
|
239 | + /** |
|
240 | + * Remove a header from the options array. |
|
241 | + * |
|
242 | + * @param string $name Case-insensitive header to remove |
|
243 | + * @param array $options Array of options to modify |
|
244 | + */ |
|
245 | + private function removeHeader(string $name, array &$options) : void |
|
246 | + { |
|
247 | + foreach (\array_keys($options['_headers']) as $key) { |
|
248 | + if (!\strcasecmp($key, $name)) { |
|
249 | + unset($options['_headers'][$key]); |
|
250 | + return; |
|
251 | + } |
|
252 | + } |
|
253 | + } |
|
254 | + private function applyHandlerOptions(EasyHandle $easy, array &$conf) : void |
|
255 | + { |
|
256 | + $options = $easy->options; |
|
257 | + if (isset($options['verify'])) { |
|
258 | + if ($options['verify'] === \false) { |
|
259 | + unset($conf[\CURLOPT_CAINFO]); |
|
260 | + $conf[\CURLOPT_SSL_VERIFYHOST] = 0; |
|
261 | + $conf[\CURLOPT_SSL_VERIFYPEER] = \false; |
|
262 | + } else { |
|
263 | + $conf[\CURLOPT_SSL_VERIFYHOST] = 2; |
|
264 | + $conf[\CURLOPT_SSL_VERIFYPEER] = \true; |
|
265 | + if (\is_string($options['verify'])) { |
|
266 | + // Throw an error if the file/folder/link path is not valid or doesn't exist. |
|
267 | + if (!\file_exists($options['verify'])) { |
|
268 | + throw new \InvalidArgumentException("SSL CA bundle not found: {$options['verify']}"); |
|
269 | + } |
|
270 | + // If it's a directory or a link to a directory use CURLOPT_CAPATH. |
|
271 | + // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. |
|
272 | + if (\is_dir($options['verify']) || \is_link($options['verify']) === \true && ($verifyLink = \readlink($options['verify'])) !== \false && \is_dir($verifyLink)) { |
|
273 | + $conf[\CURLOPT_CAPATH] = $options['verify']; |
|
274 | + } else { |
|
275 | + $conf[\CURLOPT_CAINFO] = $options['verify']; |
|
276 | + } |
|
277 | + } |
|
278 | + } |
|
279 | + } |
|
280 | + if (!isset($options['curl'][\CURLOPT_ENCODING]) && !empty($options['decode_content'])) { |
|
281 | + $accept = $easy->request->getHeaderLine('Accept-Encoding'); |
|
282 | + if ($accept) { |
|
283 | + $conf[\CURLOPT_ENCODING] = $accept; |
|
284 | + } else { |
|
285 | + // The empty string enables all available decoders and implicitly |
|
286 | + // sets a matching 'Accept-Encoding' header. |
|
287 | + $conf[\CURLOPT_ENCODING] = ''; |
|
288 | + // But as the user did not specify any acceptable encodings we need |
|
289 | + // to overwrite this implicit header with an empty one. |
|
290 | + $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; |
|
291 | + } |
|
292 | + } |
|
293 | + if (!isset($options['sink'])) { |
|
294 | + // Use a default temp stream if no sink was set. |
|
295 | + $options['sink'] = \OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+'); |
|
296 | + } |
|
297 | + $sink = $options['sink']; |
|
298 | + if (!\is_string($sink)) { |
|
299 | + $sink = \OCA\FullTextSearch_Elasticsearch\Vendor\GuzzleHttp\Psr7\Utils::streamFor($sink); |
|
300 | + } elseif (!\is_dir(\dirname($sink))) { |
|
301 | + // Ensure that the directory exists before failing in curl. |
|
302 | + throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink)); |
|
303 | + } else { |
|
304 | + $sink = new LazyOpenStream($sink, 'w+'); |
|
305 | + } |
|
306 | + $easy->sink = $sink; |
|
307 | + $conf[\CURLOPT_WRITEFUNCTION] = static function ($ch, $write) use($sink) : int { |
|
308 | + return $sink->write($write); |
|
309 | + }; |
|
310 | + $timeoutRequiresNoSignal = \false; |
|
311 | + if (isset($options['timeout'])) { |
|
312 | + $timeoutRequiresNoSignal |= $options['timeout'] < 1; |
|
313 | + $conf[\CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; |
|
314 | + } |
|
315 | + // CURL default value is CURL_IPRESOLVE_WHATEVER |
|
316 | + if (isset($options['force_ip_resolve'])) { |
|
317 | + if ('v4' === $options['force_ip_resolve']) { |
|
318 | + $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V4; |
|
319 | + } elseif ('v6' === $options['force_ip_resolve']) { |
|
320 | + $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V6; |
|
321 | + } |
|
322 | + } |
|
323 | + if (isset($options['connect_timeout'])) { |
|
324 | + $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; |
|
325 | + $conf[\CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; |
|
326 | + } |
|
327 | + if ($timeoutRequiresNoSignal && \strtoupper(\substr(\PHP_OS, 0, 3)) !== 'WIN') { |
|
328 | + $conf[\CURLOPT_NOSIGNAL] = \true; |
|
329 | + } |
|
330 | + if (isset($options['proxy'])) { |
|
331 | + if (!\is_array($options['proxy'])) { |
|
332 | + $conf[\CURLOPT_PROXY] = $options['proxy']; |
|
333 | + } else { |
|
334 | + $scheme = $easy->request->getUri()->getScheme(); |
|
335 | + if (isset($options['proxy'][$scheme])) { |
|
336 | + $host = $easy->request->getUri()->getHost(); |
|
337 | + if (isset($options['proxy']['no']) && Utils::isHostInNoProxy($host, $options['proxy']['no'])) { |
|
338 | + unset($conf[\CURLOPT_PROXY]); |
|
339 | + } else { |
|
340 | + $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme]; |
|
341 | + } |
|
342 | + } |
|
343 | + } |
|
344 | + } |
|
345 | + if (isset($options['crypto_method'])) { |
|
346 | + if (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) { |
|
347 | + if (!\defined('CURL_SSLVERSION_TLSv1_0')) { |
|
348 | + throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.0 not supported by your version of cURL'); |
|
349 | + } |
|
350 | + $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0; |
|
351 | + } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) { |
|
352 | + if (!\defined('CURL_SSLVERSION_TLSv1_1')) { |
|
353 | + throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.1 not supported by your version of cURL'); |
|
354 | + } |
|
355 | + $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1; |
|
356 | + } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) { |
|
357 | + if (!\defined('CURL_SSLVERSION_TLSv1_2')) { |
|
358 | + throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL'); |
|
359 | + } |
|
360 | + $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; |
|
361 | + } elseif (\defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { |
|
362 | + if (!\defined('CURL_SSLVERSION_TLSv1_3')) { |
|
363 | + throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); |
|
364 | + } |
|
365 | + $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; |
|
366 | + } else { |
|
367 | + throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); |
|
368 | + } |
|
369 | + } |
|
370 | + if (isset($options['cert'])) { |
|
371 | + $cert = $options['cert']; |
|
372 | + if (\is_array($cert)) { |
|
373 | + $conf[\CURLOPT_SSLCERTPASSWD] = $cert[1]; |
|
374 | + $cert = $cert[0]; |
|
375 | + } |
|
376 | + if (!\file_exists($cert)) { |
|
377 | + throw new \InvalidArgumentException("SSL certificate not found: {$cert}"); |
|
378 | + } |
|
379 | + // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files. |
|
380 | + // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html |
|
381 | + $ext = \pathinfo($cert, \PATHINFO_EXTENSION); |
|
382 | + if (\preg_match('#^(der|p12)$#i', $ext)) { |
|
383 | + $conf[\CURLOPT_SSLCERTTYPE] = \strtoupper($ext); |
|
384 | + } |
|
385 | + $conf[\CURLOPT_SSLCERT] = $cert; |
|
386 | + } |
|
387 | + if (isset($options['ssl_key'])) { |
|
388 | + if (\is_array($options['ssl_key'])) { |
|
389 | + if (\count($options['ssl_key']) === 2) { |
|
390 | + [$sslKey, $conf[\CURLOPT_SSLKEYPASSWD]] = $options['ssl_key']; |
|
391 | + } else { |
|
392 | + [$sslKey] = $options['ssl_key']; |
|
393 | + } |
|
394 | + } |
|
395 | + $sslKey = $sslKey ?? $options['ssl_key']; |
|
396 | + if (!\file_exists($sslKey)) { |
|
397 | + throw new \InvalidArgumentException("SSL private key not found: {$sslKey}"); |
|
398 | + } |
|
399 | + $conf[\CURLOPT_SSLKEY] = $sslKey; |
|
400 | + } |
|
401 | + if (isset($options['progress'])) { |
|
402 | + $progress = $options['progress']; |
|
403 | + if (!\is_callable($progress)) { |
|
404 | + throw new \InvalidArgumentException('progress client option must be callable'); |
|
405 | + } |
|
406 | + $conf[\CURLOPT_NOPROGRESS] = \false; |
|
407 | + $conf[\CURLOPT_PROGRESSFUNCTION] = static function ($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use($progress) { |
|
408 | + $progress($downloadSize, $downloaded, $uploadSize, $uploaded); |
|
409 | + }; |
|
410 | + } |
|
411 | + if (!empty($options['debug'])) { |
|
412 | + $conf[\CURLOPT_STDERR] = Utils::debugResource($options['debug']); |
|
413 | + $conf[\CURLOPT_VERBOSE] = \true; |
|
414 | + } |
|
415 | + } |
|
416 | + /** |
|
417 | + * This function ensures that a response was set on a transaction. If one |
|
418 | + * was not set, then the request is retried if possible. This error |
|
419 | + * typically means you are sending a payload, curl encountered a |
|
420 | + * "Connection died, retrying a fresh connect" error, tried to rewind the |
|
421 | + * stream, and then encountered a "necessary data rewind wasn't possible" |
|
422 | + * error, causing the request to be sent through curl_multi_info_read() |
|
423 | + * without an error status. |
|
424 | + * |
|
425 | + * @param callable(RequestInterface, array): PromiseInterface $handler |
|
426 | + */ |
|
427 | + private static function retryFailedRewind(callable $handler, EasyHandle $easy, array $ctx) : PromiseInterface |
|
428 | + { |
|
429 | + try { |
|
430 | + // Only rewind if the body has been read from. |
|
431 | + $body = $easy->request->getBody(); |
|
432 | + if ($body->tell() > 0) { |
|
433 | + $body->rewind(); |
|
434 | + } |
|
435 | + } catch (\RuntimeException $e) { |
|
436 | + $ctx['error'] = 'The connection unexpectedly failed without ' . 'providing an error. The request would have been retried, ' . 'but attempting to rewind the request body failed. ' . 'Exception: ' . $e; |
|
437 | + return self::createRejection($easy, $ctx); |
|
438 | + } |
|
439 | + // Retry no more than 3 times before giving up. |
|
440 | + if (!isset($easy->options['_curl_retries'])) { |
|
441 | + $easy->options['_curl_retries'] = 1; |
|
442 | + } elseif ($easy->options['_curl_retries'] == 2) { |
|
443 | + $ctx['error'] = 'The cURL request was retried 3 times ' . 'and did not succeed. The most likely reason for the failure ' . 'is that cURL was unable to rewind the body of the request ' . 'and subsequent retries resulted in the same error. Turn on ' . 'the debug option to see what went wrong. See ' . 'https://bugs.php.net/bug.php?id=47204 for more information.'; |
|
444 | + return self::createRejection($easy, $ctx); |
|
445 | + } else { |
|
446 | + ++$easy->options['_curl_retries']; |
|
447 | + } |
|
448 | + return $handler($easy->request, $easy->options); |
|
449 | + } |
|
450 | + private function createHeaderFn(EasyHandle $easy) : callable |
|
451 | + { |
|
452 | + if (isset($easy->options['on_headers'])) { |
|
453 | + $onHeaders = $easy->options['on_headers']; |
|
454 | + if (!\is_callable($onHeaders)) { |
|
455 | + throw new \InvalidArgumentException('on_headers must be callable'); |
|
456 | + } |
|
457 | + } else { |
|
458 | + $onHeaders = null; |
|
459 | + } |
|
460 | + return static function ($ch, $h) use($onHeaders, $easy, &$startingResponse) { |
|
461 | + $value = \trim($h); |
|
462 | + if ($value === '') { |
|
463 | + $startingResponse = \true; |
|
464 | + try { |
|
465 | + $easy->createResponse(); |
|
466 | + } catch (\Exception $e) { |
|
467 | + $easy->createResponseException = $e; |
|
468 | + return -1; |
|
469 | + } |
|
470 | + if ($onHeaders !== null) { |
|
471 | + try { |
|
472 | + $onHeaders($easy->response); |
|
473 | + } catch (\Exception $e) { |
|
474 | + // Associate the exception with the handle and trigger |
|
475 | + // a curl header write error by returning 0. |
|
476 | + $easy->onHeadersException = $e; |
|
477 | + return -1; |
|
478 | + } |
|
479 | + } |
|
480 | + } elseif ($startingResponse) { |
|
481 | + $startingResponse = \false; |
|
482 | + $easy->headers = [$value]; |
|
483 | + } else { |
|
484 | + $easy->headers[] = $value; |
|
485 | + } |
|
486 | + return \strlen($h); |
|
487 | + }; |
|
488 | + } |
|
489 | + public function __destruct() |
|
490 | + { |
|
491 | + foreach ($this->handles as $id => $handle) { |
|
492 | + \curl_close($handle); |
|
493 | + unset($this->handles[$id]); |
|
494 | + } |
|
495 | + } |
|
496 | 496 | } |
@@ -138,7 +138,7 @@ discard block |
||
138 | 138 | return P\Create::rejectionFor(new RequestException('An error was encountered during the on_headers event', $easy->request, $easy->response, $easy->onHeadersException, $ctx)); |
139 | 139 | } |
140 | 140 | $message = \sprintf('cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'); |
141 | - $uriString = (string) $easy->request->getUri(); |
|
141 | + $uriString = (string)$easy->request->getUri(); |
|
142 | 142 | if ($uriString !== '' && \false === \strpos($ctx['error'], $uriString)) { |
143 | 143 | $message .= \sprintf(' for %s', $uriString); |
144 | 144 | } |
@@ -151,7 +151,7 @@ discard block |
||
151 | 151 | */ |
152 | 152 | private function getDefaultConf(EasyHandle $easy) : array |
153 | 153 | { |
154 | - $conf = ['_headers' => $easy->request->getHeaders(), \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), \CURLOPT_RETURNTRANSFER => \false, \CURLOPT_HEADER => \false, \CURLOPT_CONNECTTIMEOUT => 300]; |
|
154 | + $conf = ['_headers' => $easy->request->getHeaders(), \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), \CURLOPT_URL => (string)$easy->request->getUri()->withFragment(''), \CURLOPT_RETURNTRANSFER => \false, \CURLOPT_HEADER => \false, \CURLOPT_CONNECTTIMEOUT => 300]; |
|
155 | 155 | if (\defined('CURLOPT_PROTOCOLS')) { |
156 | 156 | $conf[\CURLOPT_PROTOCOLS] = \CURLPROTO_HTTP | \CURLPROTO_HTTPS; |
157 | 157 | } |
@@ -186,11 +186,11 @@ discard block |
||
186 | 186 | } |
187 | 187 | private function applyBody(RequestInterface $request, array $options, array &$conf) : void |
188 | 188 | { |
189 | - $size = $request->hasHeader('Content-Length') ? (int) $request->getHeaderLine('Content-Length') : null; |
|
189 | + $size = $request->hasHeader('Content-Length') ? (int)$request->getHeaderLine('Content-Length') : null; |
|
190 | 190 | // Send the body as a string if the size is less than 1MB OR if the |
191 | 191 | // [curl][body_as_string] request value is set. |
192 | 192 | if ($size !== null && $size < 1000000 || !empty($options['_body_as_string'])) { |
193 | - $conf[\CURLOPT_POSTFIELDS] = (string) $request->getBody(); |
|
193 | + $conf[\CURLOPT_POSTFIELDS] = (string)$request->getBody(); |
|
194 | 194 | // Don't duplicate the Content-Length header |
195 | 195 | $this->removeHeader('Content-Length', $conf); |
196 | 196 | $this->removeHeader('Transfer-Encoding', $conf); |
@@ -204,7 +204,7 @@ discard block |
||
204 | 204 | if ($body->isSeekable()) { |
205 | 205 | $body->rewind(); |
206 | 206 | } |
207 | - $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use($body) { |
|
207 | + $conf[\CURLOPT_READFUNCTION] = static function($ch, $fd, $length) use($body) { |
|
208 | 208 | return $body->read($length); |
209 | 209 | }; |
210 | 210 | } |
@@ -221,7 +221,7 @@ discard block |
||
221 | 221 | { |
222 | 222 | foreach ($conf['_headers'] as $name => $values) { |
223 | 223 | foreach ($values as $value) { |
224 | - $value = (string) $value; |
|
224 | + $value = (string)$value; |
|
225 | 225 | if ($value === '') { |
226 | 226 | // cURL requires a special format for empty headers. |
227 | 227 | // See https://github.com/guzzle/guzzle/issues/1882 for more details. |
@@ -304,7 +304,7 @@ discard block |
||
304 | 304 | $sink = new LazyOpenStream($sink, 'w+'); |
305 | 305 | } |
306 | 306 | $easy->sink = $sink; |
307 | - $conf[\CURLOPT_WRITEFUNCTION] = static function ($ch, $write) use($sink) : int { |
|
307 | + $conf[\CURLOPT_WRITEFUNCTION] = static function($ch, $write) use($sink) : int { |
|
308 | 308 | return $sink->write($write); |
309 | 309 | }; |
310 | 310 | $timeoutRequiresNoSignal = \false; |
@@ -404,7 +404,7 @@ discard block |
||
404 | 404 | throw new \InvalidArgumentException('progress client option must be callable'); |
405 | 405 | } |
406 | 406 | $conf[\CURLOPT_NOPROGRESS] = \false; |
407 | - $conf[\CURLOPT_PROGRESSFUNCTION] = static function ($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use($progress) { |
|
407 | + $conf[\CURLOPT_PROGRESSFUNCTION] = static function($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use($progress) { |
|
408 | 408 | $progress($downloadSize, $downloaded, $uploadSize, $uploaded); |
409 | 409 | }; |
410 | 410 | } |
@@ -433,14 +433,14 @@ discard block |
||
433 | 433 | $body->rewind(); |
434 | 434 | } |
435 | 435 | } catch (\RuntimeException $e) { |
436 | - $ctx['error'] = 'The connection unexpectedly failed without ' . 'providing an error. The request would have been retried, ' . 'but attempting to rewind the request body failed. ' . 'Exception: ' . $e; |
|
436 | + $ctx['error'] = 'The connection unexpectedly failed without '.'providing an error. The request would have been retried, '.'but attempting to rewind the request body failed. '.'Exception: '.$e; |
|
437 | 437 | return self::createRejection($easy, $ctx); |
438 | 438 | } |
439 | 439 | // Retry no more than 3 times before giving up. |
440 | 440 | if (!isset($easy->options['_curl_retries'])) { |
441 | 441 | $easy->options['_curl_retries'] = 1; |
442 | 442 | } elseif ($easy->options['_curl_retries'] == 2) { |
443 | - $ctx['error'] = 'The cURL request was retried 3 times ' . 'and did not succeed. The most likely reason for the failure ' . 'is that cURL was unable to rewind the body of the request ' . 'and subsequent retries resulted in the same error. Turn on ' . 'the debug option to see what went wrong. See ' . 'https://bugs.php.net/bug.php?id=47204 for more information.'; |
|
443 | + $ctx['error'] = 'The cURL request was retried 3 times '.'and did not succeed. The most likely reason for the failure '.'is that cURL was unable to rewind the body of the request '.'and subsequent retries resulted in the same error. Turn on '.'the debug option to see what went wrong. See '.'https://bugs.php.net/bug.php?id=47204 for more information.'; |
|
444 | 444 | return self::createRejection($easy, $ctx); |
445 | 445 | } else { |
446 | 446 | ++$easy->options['_curl_retries']; |
@@ -457,7 +457,7 @@ discard block |
||
457 | 457 | } else { |
458 | 458 | $onHeaders = null; |
459 | 459 | } |
460 | - return static function ($ch, $h) use($onHeaders, $easy, &$startingResponse) { |
|
460 | + return static function($ch, $h) use($onHeaders, $easy, &$startingResponse) { |
|
461 | 461 | $value = \trim($h); |
462 | 462 | if ($value === '') { |
463 | 463 | $startingResponse = \true; |
@@ -16,8 +16,7 @@ |
||
16 | 16 | * |
17 | 17 | * @final |
18 | 18 | */ |
19 | -class CurlFactory implements CurlFactoryInterface |
|
20 | -{ |
|
19 | +class CurlFactory implements CurlFactoryInterface { |
|
21 | 20 | public const CURL_VERSION_STR = 'curl_version'; |
22 | 21 | /** |
23 | 22 | * @deprecated |