diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:38:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:38:42 +0000 |
commit | c3ca98e1b35123f226c7f4c596b5dee78caa4223 (patch) | |
tree | 9b6eb109283da55e7d9064baa9fac795a40264cb /vendor/react/http/src/Client | |
parent | Initial commit. (diff) | |
download | icinga-php-thirdparty-c3ca98e1b35123f226c7f4c596b5dee78caa4223.tar.xz icinga-php-thirdparty-c3ca98e1b35123f226c7f4c596b5dee78caa4223.zip |
Adding upstream version 0.11.0.upstream/0.11.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/react/http/src/Client')
-rw-r--r-- | vendor/react/http/src/Client/Client.php | 31 | ||||
-rw-r--r-- | vendor/react/http/src/Client/Request.php | 237 | ||||
-rw-r--r-- | vendor/react/http/src/Client/RequestData.php | 128 |
3 files changed, 396 insertions, 0 deletions
diff --git a/vendor/react/http/src/Client/Client.php b/vendor/react/http/src/Client/Client.php new file mode 100644 index 0000000..7a97349 --- /dev/null +++ b/vendor/react/http/src/Client/Client.php @@ -0,0 +1,31 @@ +<?php + +namespace React\Http\Client; + +use React\EventLoop\LoopInterface; +use React\Socket\ConnectorInterface; +use React\Socket\Connector; + +/** + * @internal + */ +class Client +{ + private $connector; + + public function __construct(LoopInterface $loop, ConnectorInterface $connector = null) + { + if ($connector === null) { + $connector = new Connector(array(), $loop); + } + + $this->connector = $connector; + } + + public function request($method, $url, array $headers = array(), $protocolVersion = '1.0') + { + $requestData = new RequestData($method, $url, $headers, $protocolVersion); + + return new Request($this->connector, $requestData); + } +} diff --git a/vendor/react/http/src/Client/Request.php b/vendor/react/http/src/Client/Request.php new file mode 100644 index 0000000..51e0331 --- /dev/null +++ b/vendor/react/http/src/Client/Request.php @@ -0,0 +1,237 @@ +<?php + +namespace React\Http\Client; + +use Evenement\EventEmitter; +use React\Promise; +use React\Socket\ConnectionInterface; +use React\Socket\ConnectorInterface; +use React\Stream\WritableStreamInterface; +use RingCentral\Psr7 as gPsr; + +/** + * @event response + * @event drain + * @event error + * @event end + * @internal + */ +class Request extends EventEmitter implements WritableStreamInterface +{ + const STATE_INIT = 0; + const STATE_WRITING_HEAD = 1; + const STATE_HEAD_WRITTEN = 2; + const STATE_END = 3; + + private $connector; + private $requestData; + + private $stream; + private $buffer; + private $responseFactory; + private $state = self::STATE_INIT; + private $ended = false; + + private $pendingWrites = ''; + + public function __construct(ConnectorInterface $connector, RequestData $requestData) + { + $this->connector = $connector; + $this->requestData = $requestData; + } + + public function isWritable() + { + return self::STATE_END > $this->state && !$this->ended; + } + + private function writeHead() + { + $this->state = self::STATE_WRITING_HEAD; + + $requestData = $this->requestData; + $streamRef = &$this->stream; + $stateRef = &$this->state; + $pendingWrites = &$this->pendingWrites; + $that = $this; + + $promise = $this->connect(); + $promise->then( + function (ConnectionInterface $stream) use ($requestData, &$streamRef, &$stateRef, &$pendingWrites, $that) { + $streamRef = $stream; + + $stream->on('drain', array($that, 'handleDrain')); + $stream->on('data', array($that, 'handleData')); + $stream->on('end', array($that, 'handleEnd')); + $stream->on('error', array($that, 'handleError')); + $stream->on('close', array($that, 'handleClose')); + + $headers = (string) $requestData; + + $more = $stream->write($headers . $pendingWrites); + + $stateRef = Request::STATE_HEAD_WRITTEN; + + // clear pending writes if non-empty + if ($pendingWrites !== '') { + $pendingWrites = ''; + + if ($more) { + $that->emit('drain'); + } + } + }, + array($this, 'closeError') + ); + + $this->on('close', function() use ($promise) { + $promise->cancel(); + }); + } + + public function write($data) + { + if (!$this->isWritable()) { + return false; + } + + // write directly to connection stream if already available + if (self::STATE_HEAD_WRITTEN <= $this->state) { + return $this->stream->write($data); + } + + // otherwise buffer and try to establish connection + $this->pendingWrites .= $data; + if (self::STATE_WRITING_HEAD > $this->state) { + $this->writeHead(); + } + + return false; + } + + public function end($data = null) + { + if (!$this->isWritable()) { + return; + } + + if (null !== $data) { + $this->write($data); + } else if (self::STATE_WRITING_HEAD > $this->state) { + $this->writeHead(); + } + + $this->ended = true; + } + + /** @internal */ + public function handleDrain() + { + $this->emit('drain'); + } + + /** @internal */ + public function handleData($data) + { + $this->buffer .= $data; + + // buffer until double CRLF (or double LF for compatibility with legacy servers) + if (false !== strpos($this->buffer, "\r\n\r\n") || false !== strpos($this->buffer, "\n\n")) { + try { + $response = gPsr\parse_response($this->buffer); + $bodyChunk = (string) $response->getBody(); + } catch (\InvalidArgumentException $exception) { + $this->emit('error', array($exception)); + } + + $this->buffer = null; + + $this->stream->removeListener('drain', array($this, 'handleDrain')); + $this->stream->removeListener('data', array($this, 'handleData')); + $this->stream->removeListener('end', array($this, 'handleEnd')); + $this->stream->removeListener('error', array($this, 'handleError')); + $this->stream->removeListener('close', array($this, 'handleClose')); + + if (!isset($response)) { + return; + } + + $this->stream->on('close', array($this, 'handleClose')); + + $this->emit('response', array($response, $this->stream)); + + $this->stream->emit('data', array($bodyChunk)); + } + } + + /** @internal */ + public function handleEnd() + { + $this->closeError(new \RuntimeException( + "Connection ended before receiving response" + )); + } + + /** @internal */ + public function handleError(\Exception $error) + { + $this->closeError(new \RuntimeException( + "An error occurred in the underlying stream", + 0, + $error + )); + } + + /** @internal */ + public function handleClose() + { + $this->close(); + } + + /** @internal */ + public function closeError(\Exception $error) + { + if (self::STATE_END <= $this->state) { + return; + } + $this->emit('error', array($error)); + $this->close(); + } + + public function close() + { + if (self::STATE_END <= $this->state) { + return; + } + + $this->state = self::STATE_END; + $this->pendingWrites = ''; + + if ($this->stream) { + $this->stream->close(); + } + + $this->emit('close'); + $this->removeAllListeners(); + } + + protected function connect() + { + $scheme = $this->requestData->getScheme(); + if ($scheme !== 'https' && $scheme !== 'http') { + return Promise\reject( + new \InvalidArgumentException('Invalid request URL given') + ); + } + + $host = $this->requestData->getHost(); + $port = $this->requestData->getPort(); + + if ($scheme === 'https') { + $host = 'tls://' . $host; + } + + return $this->connector + ->connect($host . ':' . $port); + } +} diff --git a/vendor/react/http/src/Client/RequestData.php b/vendor/react/http/src/Client/RequestData.php new file mode 100644 index 0000000..a5908a0 --- /dev/null +++ b/vendor/react/http/src/Client/RequestData.php @@ -0,0 +1,128 @@ +<?php + +namespace React\Http\Client; + +/** + * @internal + */ +class RequestData +{ + private $method; + private $url; + private $headers; + private $protocolVersion; + + public function __construct($method, $url, array $headers = array(), $protocolVersion = '1.0') + { + $this->method = $method; + $this->url = $url; + $this->headers = $headers; + $this->protocolVersion = $protocolVersion; + } + + private function mergeDefaultheaders(array $headers) + { + $port = ($this->getDefaultPort() === $this->getPort()) ? '' : ":{$this->getPort()}"; + $connectionHeaders = ('1.1' === $this->protocolVersion) ? array('Connection' => 'close') : array(); + $authHeaders = $this->getAuthHeaders(); + + $defaults = array_merge( + array( + 'Host' => $this->getHost().$port, + 'User-Agent' => 'ReactPHP/1', + ), + $connectionHeaders, + $authHeaders + ); + + // remove all defaults that already exist in $headers + $lower = array_change_key_case($headers, CASE_LOWER); + foreach ($defaults as $key => $_) { + if (isset($lower[strtolower($key)])) { + unset($defaults[$key]); + } + } + + return array_merge($defaults, $headers); + } + + public function getScheme() + { + return parse_url($this->url, PHP_URL_SCHEME); + } + + public function getHost() + { + return parse_url($this->url, PHP_URL_HOST); + } + + public function getPort() + { + return (int) parse_url($this->url, PHP_URL_PORT) ?: $this->getDefaultPort(); + } + + public function getDefaultPort() + { + return ('https' === $this->getScheme()) ? 443 : 80; + } + + public function getPath() + { + $path = parse_url($this->url, PHP_URL_PATH); + $queryString = parse_url($this->url, PHP_URL_QUERY); + + // assume "/" path by default, but allow "OPTIONS *" + if ($path === null) { + $path = ($this->method === 'OPTIONS' && $queryString === null) ? '*': '/'; + } + if ($queryString !== null) { + $path .= '?' . $queryString; + } + + return $path; + } + + public function setProtocolVersion($version) + { + $this->protocolVersion = $version; + } + + public function __toString() + { + $headers = $this->mergeDefaultheaders($this->headers); + + $data = ''; + $data .= "{$this->method} {$this->getPath()} HTTP/{$this->protocolVersion}\r\n"; + foreach ($headers as $name => $values) { + foreach ((array)$values as $value) { + $data .= "$name: $value\r\n"; + } + } + $data .= "\r\n"; + + return $data; + } + + private function getUrlUserPass() + { + $components = parse_url($this->url); + + if (isset($components['user'])) { + return array( + 'user' => $components['user'], + 'pass' => isset($components['pass']) ? $components['pass'] : null, + ); + } + } + + private function getAuthHeaders() + { + if (null !== $auth = $this->getUrlUserPass()) { + return array( + 'Authorization' => 'Basic ' . base64_encode($auth['user'].':'.$auth['pass']), + ); + } + + return array(); + } +} |