summaryrefslogtreecommitdiffstats
path: root/vendor/react/http
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/react/http')
-rw-r--r--vendor/react/http/LICENSE21
-rw-r--r--vendor/react/http/composer.json53
-rw-r--r--vendor/react/http/src/Browser.php790
-rw-r--r--vendor/react/http/src/Client/Client.php31
-rw-r--r--vendor/react/http/src/Client/Request.php237
-rw-r--r--vendor/react/http/src/Client/RequestData.php128
-rw-r--r--vendor/react/http/src/HttpServer.php351
-rw-r--r--vendor/react/http/src/Io/BufferedBody.php179
-rw-r--r--vendor/react/http/src/Io/ChunkedDecoder.php175
-rw-r--r--vendor/react/http/src/Io/ChunkedEncoder.php92
-rw-r--r--vendor/react/http/src/Io/CloseProtectionStream.php111
-rw-r--r--vendor/react/http/src/Io/EmptyBodyStream.php142
-rw-r--r--vendor/react/http/src/Io/HttpBodyStream.php182
-rw-r--r--vendor/react/http/src/Io/IniUtil.php48
-rw-r--r--vendor/react/http/src/Io/LengthLimitedStream.php108
-rw-r--r--vendor/react/http/src/Io/MiddlewareRunner.php61
-rw-r--r--vendor/react/http/src/Io/MultipartParser.php328
-rw-r--r--vendor/react/http/src/Io/PauseBufferStream.php188
-rw-r--r--vendor/react/http/src/Io/ReadableBodyStream.php153
-rw-r--r--vendor/react/http/src/Io/RequestHeaderParser.php281
-rw-r--r--vendor/react/http/src/Io/Sender.php160
-rw-r--r--vendor/react/http/src/Io/StreamingServer.php383
-rw-r--r--vendor/react/http/src/Io/Transaction.php303
-rw-r--r--vendor/react/http/src/Io/UploadedFile.php130
-rw-r--r--vendor/react/http/src/Message/Response.php291
-rw-r--r--vendor/react/http/src/Message/ResponseException.php43
-rw-r--r--vendor/react/http/src/Message/ServerRequest.php197
-rw-r--r--vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php211
-rw-r--r--vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php70
-rw-r--r--vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php46
-rw-r--r--vendor/react/http/src/Middleware/StreamingRequestMiddleware.php69
-rw-r--r--vendor/react/http/src/Server.php18
32 files changed, 5580 insertions, 0 deletions
diff --git a/vendor/react/http/LICENSE b/vendor/react/http/LICENSE
new file mode 100644
index 0000000..d6f8901
--- /dev/null
+++ b/vendor/react/http/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2012 Christian Lück, Cees-Jan Kiewiet, Jan Sorgalla, Chris Boden, Igor Wiedler
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/react/http/composer.json b/vendor/react/http/composer.json
new file mode 100644
index 0000000..4c9a038
--- /dev/null
+++ b/vendor/react/http/composer.json
@@ -0,0 +1,53 @@
+{
+ "name": "react/http",
+ "description": "Event-driven, streaming HTTP client and server implementation for ReactPHP",
+ "keywords": ["HTTP client", "HTTP server", "HTTP", "HTTPS", "event-driven", "streaming", "client", "server", "PSR-7", "async", "ReactPHP"],
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Christian Lück",
+ "homepage": "https://clue.engineering/",
+ "email": "christian@clue.engineering"
+ },
+ {
+ "name": "Cees-Jan Kiewiet",
+ "homepage": "https://wyrihaximus.net/",
+ "email": "reactphp@ceesjankiewiet.nl"
+ },
+ {
+ "name": "Jan Sorgalla",
+ "homepage": "https://sorgalla.com/",
+ "email": "jsorgalla@gmail.com"
+ },
+ {
+ "name": "Chris Boden",
+ "homepage": "https://cboden.dev/",
+ "email": "cboden@gmail.com"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.0",
+ "evenement/evenement": "^3.0 || ^2.0 || ^1.0",
+ "fig/http-message-util": "^1.1",
+ "psr/http-message": "^1.0",
+ "react/event-loop": "^1.2",
+ "react/promise": "^2.3 || ^1.2.1",
+ "react/promise-stream": "^1.1",
+ "react/socket": "^1.9",
+ "react/stream": "^1.2",
+ "ringcentral/psr7": "^1.2"
+ },
+ "require-dev": {
+ "clue/block-react": "^1.5",
+ "clue/http-proxy-react": "^1.7",
+ "clue/reactphp-ssh-proxy": "^1.3",
+ "clue/socks-react": "^1.3",
+ "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
+ },
+ "autoload": {
+ "psr-4": { "React\\Http\\": "src" }
+ },
+ "autoload-dev": {
+ "psr-4": { "React\\Tests\\Http\\": "tests" }
+ }
+}
diff --git a/vendor/react/http/src/Browser.php b/vendor/react/http/src/Browser.php
new file mode 100644
index 0000000..72847f6
--- /dev/null
+++ b/vendor/react/http/src/Browser.php
@@ -0,0 +1,790 @@
+<?php
+
+namespace React\Http;
+
+use Psr\Http\Message\ResponseInterface;
+use RingCentral\Psr7\Request;
+use RingCentral\Psr7\Uri;
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+use React\Http\Io\ReadableBodyStream;
+use React\Http\Io\Sender;
+use React\Http\Io\Transaction;
+use React\Promise\PromiseInterface;
+use React\Socket\ConnectorInterface;
+use React\Stream\ReadableStreamInterface;
+use InvalidArgumentException;
+
+/**
+ * @final This class is final and shouldn't be extended as it is likely to be marked final in a future release.
+ */
+class Browser
+{
+ private $transaction;
+ private $baseUrl;
+ private $protocolVersion = '1.1';
+
+ /**
+ * The `Browser` is responsible for sending HTTP requests to your HTTP server
+ * and keeps track of pending incoming HTTP responses.
+ *
+ * ```php
+ * $browser = new React\Http\Browser();
+ * ```
+ *
+ * This class takes two optional arguments for more advanced usage:
+ *
+ * ```php
+ * // constructor signature as of v1.5.0
+ * $browser = new React\Http\Browser(?ConnectorInterface $connector = null, ?LoopInterface $loop = null);
+ *
+ * // legacy constructor signature before v1.5.0
+ * $browser = new React\Http\Browser(?LoopInterface $loop = null, ?ConnectorInterface $connector = null);
+ * ```
+ *
+ * If you need custom connector settings (DNS resolution, TLS parameters, timeouts,
+ * proxy servers etc.), you can explicitly pass a custom instance of the
+ * [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface):
+ *
+ * ```php
+ * $connector = new React\Socket\Connector(array(
+ * 'dns' => '127.0.0.1',
+ * 'tcp' => array(
+ * 'bindto' => '192.168.10.1:0'
+ * ),
+ * 'tls' => array(
+ * 'verify_peer' => false,
+ * 'verify_peer_name' => false
+ * )
+ * ));
+ *
+ * $browser = new React\Http\Browser($connector);
+ * ```
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * @param null|ConnectorInterface|LoopInterface $connector
+ * @param null|LoopInterface|ConnectorInterface $loop
+ * @throws \InvalidArgumentException for invalid arguments
+ */
+ public function __construct($connector = null, $loop = null)
+ {
+ // swap arguments for legacy constructor signature
+ if (($connector instanceof LoopInterface || $connector === null) && ($loop instanceof ConnectorInterface || $loop === null)) {
+ $swap = $loop;
+ $loop = $connector;
+ $connector = $swap;
+ }
+
+ if (($connector !== null && !$connector instanceof ConnectorInterface) || ($loop !== null && !$loop instanceof LoopInterface)) {
+ throw new \InvalidArgumentException('Expected "?ConnectorInterface $connector" and "?LoopInterface $loop" arguments');
+ }
+
+ $loop = $loop ?: Loop::get();
+ $this->transaction = new Transaction(
+ Sender::createFromLoop($loop, $connector),
+ $loop
+ );
+ }
+
+ /**
+ * Sends an HTTP GET request
+ *
+ * ```php
+ * $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * var_dump((string)$response->getBody());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [GET request client example](../examples/01-client-get-request.php).
+ *
+ * @param string $url URL for the request.
+ * @param array $headers
+ * @return PromiseInterface<ResponseInterface>
+ */
+ public function get($url, array $headers = array())
+ {
+ return $this->requestMayBeStreaming('GET', $url, $headers);
+ }
+
+ /**
+ * Sends an HTTP POST request
+ *
+ * ```php
+ * $browser->post(
+ * $url,
+ * [
+ * 'Content-Type' => 'application/json'
+ * ],
+ * json_encode($data)
+ * )->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * var_dump(json_decode((string)$response->getBody()));
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [POST JSON client example](../examples/04-client-post-json.php).
+ *
+ * This method is also commonly used to submit HTML form data:
+ *
+ * ```php
+ * $data = [
+ * 'user' => 'Alice',
+ * 'password' => 'secret'
+ * ];
+ *
+ * $browser->post(
+ * $url,
+ * [
+ * 'Content-Type' => 'application/x-www-form-urlencoded'
+ * ],
+ * http_build_query($data)
+ * );
+ * ```
+ *
+ * This method will automatically add a matching `Content-Length` request
+ * header if the outgoing request body is a `string`. If you're using a
+ * streaming request body (`ReadableStreamInterface`), it will default to
+ * using `Transfer-Encoding: chunked` or you have to explicitly pass in a
+ * matching `Content-Length` request header like so:
+ *
+ * ```php
+ * $body = new React\Stream\ThroughStream();
+ * Loop::addTimer(1.0, function () use ($body) {
+ * $body->end("hello world");
+ * });
+ *
+ * $browser->post($url, array('Content-Length' => '11'), $body);
+ * ```
+ *
+ * @param string $url URL for the request.
+ * @param array $headers
+ * @param string|ReadableStreamInterface $body
+ * @return PromiseInterface<ResponseInterface>
+ */
+ public function post($url, array $headers = array(), $body = '')
+ {
+ return $this->requestMayBeStreaming('POST', $url, $headers, $body);
+ }
+
+ /**
+ * Sends an HTTP HEAD request
+ *
+ * ```php
+ * $browser->head($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * var_dump($response->getHeaders());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * @param string $url URL for the request.
+ * @param array $headers
+ * @return PromiseInterface<ResponseInterface>
+ */
+ public function head($url, array $headers = array())
+ {
+ return $this->requestMayBeStreaming('HEAD', $url, $headers);
+ }
+
+ /**
+ * Sends an HTTP PATCH request
+ *
+ * ```php
+ * $browser->patch(
+ * $url,
+ * [
+ * 'Content-Type' => 'application/json'
+ * ],
+ * json_encode($data)
+ * )->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * var_dump(json_decode((string)$response->getBody()));
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * This method will automatically add a matching `Content-Length` request
+ * header if the outgoing request body is a `string`. If you're using a
+ * streaming request body (`ReadableStreamInterface`), it will default to
+ * using `Transfer-Encoding: chunked` or you have to explicitly pass in a
+ * matching `Content-Length` request header like so:
+ *
+ * ```php
+ * $body = new React\Stream\ThroughStream();
+ * Loop::addTimer(1.0, function () use ($body) {
+ * $body->end("hello world");
+ * });
+ *
+ * $browser->patch($url, array('Content-Length' => '11'), $body);
+ * ```
+ *
+ * @param string $url URL for the request.
+ * @param array $headers
+ * @param string|ReadableStreamInterface $body
+ * @return PromiseInterface<ResponseInterface>
+ */
+ public function patch($url, array $headers = array(), $body = '')
+ {
+ return $this->requestMayBeStreaming('PATCH', $url , $headers, $body);
+ }
+
+ /**
+ * Sends an HTTP PUT request
+ *
+ * ```php
+ * $browser->put(
+ * $url,
+ * [
+ * 'Content-Type' => 'text/xml'
+ * ],
+ * $xml->asXML()
+ * )->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * var_dump((string)$response->getBody());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [PUT XML client example](../examples/05-client-put-xml.php).
+ *
+ * This method will automatically add a matching `Content-Length` request
+ * header if the outgoing request body is a `string`. If you're using a
+ * streaming request body (`ReadableStreamInterface`), it will default to
+ * using `Transfer-Encoding: chunked` or you have to explicitly pass in a
+ * matching `Content-Length` request header like so:
+ *
+ * ```php
+ * $body = new React\Stream\ThroughStream();
+ * Loop::addTimer(1.0, function () use ($body) {
+ * $body->end("hello world");
+ * });
+ *
+ * $browser->put($url, array('Content-Length' => '11'), $body);
+ * ```
+ *
+ * @param string $url URL for the request.
+ * @param array $headers
+ * @param string|ReadableStreamInterface $body
+ * @return PromiseInterface<ResponseInterface>
+ */
+ public function put($url, array $headers = array(), $body = '')
+ {
+ return $this->requestMayBeStreaming('PUT', $url, $headers, $body);
+ }
+
+ /**
+ * Sends an HTTP DELETE request
+ *
+ * ```php
+ * $browser->delete($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * var_dump((string)$response->getBody());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * @param string $url URL for the request.
+ * @param array $headers
+ * @param string|ReadableStreamInterface $body
+ * @return PromiseInterface<ResponseInterface>
+ */
+ public function delete($url, array $headers = array(), $body = '')
+ {
+ return $this->requestMayBeStreaming('DELETE', $url, $headers, $body);
+ }
+
+ /**
+ * Sends an arbitrary HTTP request.
+ *
+ * The preferred way to send an HTTP request is by using the above
+ * [request methods](#request-methods), for example the [`get()`](#get)
+ * method to send an HTTP `GET` request.
+ *
+ * As an alternative, if you want to use a custom HTTP request method, you
+ * can use this method:
+ *
+ * ```php
+ * $browser->request('OPTIONS', $url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * var_dump((string)$response->getBody());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * This method will automatically add a matching `Content-Length` request
+ * header if the size of the outgoing request body is known and non-empty.
+ * For an empty request body, if will only include a `Content-Length: 0`
+ * request header if the request method usually expects a request body (only
+ * applies to `POST`, `PUT` and `PATCH`).
+ *
+ * If you're using a streaming request body (`ReadableStreamInterface`), it
+ * will default to using `Transfer-Encoding: chunked` or you have to
+ * explicitly pass in a matching `Content-Length` request header like so:
+ *
+ * ```php
+ * $body = new React\Stream\ThroughStream();
+ * Loop::addTimer(1.0, function () use ($body) {
+ * $body->end("hello world");
+ * });
+ *
+ * $browser->request('POST', $url, array('Content-Length' => '11'), $body);
+ * ```
+ *
+ * @param string $method HTTP request method, e.g. GET/HEAD/POST etc.
+ * @param string $url URL for the request
+ * @param array $headers Additional request headers
+ * @param string|ReadableStreamInterface $body HTTP request body contents
+ * @return PromiseInterface<ResponseInterface,\Exception>
+ */
+ public function request($method, $url, array $headers = array(), $body = '')
+ {
+ return $this->withOptions(array('streaming' => false))->requestMayBeStreaming($method, $url, $headers, $body);
+ }
+
+ /**
+ * Sends an arbitrary HTTP request and receives a streaming response without buffering the response body.
+ *
+ * The preferred way to send an HTTP request is by using the above
+ * [request methods](#request-methods), for example the [`get()`](#get)
+ * method to send an HTTP `GET` request. Each of these methods will buffer
+ * the whole response body in memory by default. This is easy to get started
+ * and works reasonably well for smaller responses.
+ *
+ * In some situations, it's a better idea to use a streaming approach, where
+ * only small chunks have to be kept in memory. You can use this method to
+ * send an arbitrary HTTP request and receive a streaming response. It uses
+ * the same HTTP message API, but does not buffer the response body in
+ * memory. It only processes the response body in small chunks as data is
+ * received and forwards this data through [ReactPHP's Stream API](https://github.com/reactphp/stream).
+ * This works for (any number of) responses of arbitrary sizes.
+ *
+ * ```php
+ * $browser->requestStreaming('GET', $url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * $body = $response->getBody();
+ * assert($body instanceof Psr\Http\Message\StreamInterface);
+ * assert($body instanceof React\Stream\ReadableStreamInterface);
+ *
+ * $body->on('data', function ($chunk) {
+ * echo $chunk;
+ * });
+ *
+ * $body->on('error', function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ *
+ * $body->on('close', function () {
+ * echo '[DONE]' . PHP_EOL;
+ * });
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * See also [`ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface)
+ * and the [streaming response](#streaming-response) for more details,
+ * examples and possible use-cases.
+ *
+ * This method will automatically add a matching `Content-Length` request
+ * header if the size of the outgoing request body is known and non-empty.
+ * For an empty request body, if will only include a `Content-Length: 0`
+ * request header if the request method usually expects a request body (only
+ * applies to `POST`, `PUT` and `PATCH`).
+ *
+ * If you're using a streaming request body (`ReadableStreamInterface`), it
+ * will default to using `Transfer-Encoding: chunked` or you have to
+ * explicitly pass in a matching `Content-Length` request header like so:
+ *
+ * ```php
+ * $body = new React\Stream\ThroughStream();
+ * Loop::addTimer(1.0, function () use ($body) {
+ * $body->end("hello world");
+ * });
+ *
+ * $browser->requestStreaming('POST', $url, array('Content-Length' => '11'), $body);
+ * ```
+ *
+ * @param string $method HTTP request method, e.g. GET/HEAD/POST etc.
+ * @param string $url URL for the request
+ * @param array $headers Additional request headers
+ * @param string|ReadableStreamInterface $body HTTP request body contents
+ * @return PromiseInterface<ResponseInterface,\Exception>
+ */
+ public function requestStreaming($method, $url, $headers = array(), $body = '')
+ {
+ return $this->withOptions(array('streaming' => true))->requestMayBeStreaming($method, $url, $headers, $body);
+ }
+
+ /**
+ * Changes the maximum timeout used for waiting for pending requests.
+ *
+ * You can pass in the number of seconds to use as a new timeout value:
+ *
+ * ```php
+ * $browser = $browser->withTimeout(10.0);
+ * ```
+ *
+ * You can pass in a bool `false` to disable any timeouts. In this case,
+ * requests can stay pending forever:
+ *
+ * ```php
+ * $browser = $browser->withTimeout(false);
+ * ```
+ *
+ * You can pass in a bool `true` to re-enable default timeout handling. This
+ * will respects PHP's `default_socket_timeout` setting (default 60s):
+ *
+ * ```php
+ * $browser = $browser->withTimeout(true);
+ * ```
+ *
+ * See also [timeouts](#timeouts) for more details about timeout handling.
+ *
+ * Notice that the [`Browser`](#browser) is an immutable object, i.e. this
+ * method actually returns a *new* [`Browser`](#browser) instance with the
+ * given timeout value applied.
+ *
+ * @param bool|number $timeout
+ * @return self
+ */
+ public function withTimeout($timeout)
+ {
+ if ($timeout === true) {
+ $timeout = null;
+ } elseif ($timeout === false) {
+ $timeout = -1;
+ } elseif ($timeout < 0) {
+ $timeout = 0;
+ }
+
+ return $this->withOptions(array(
+ 'timeout' => $timeout,
+ ));
+ }
+
+ /**
+ * Changes how HTTP redirects will be followed.
+ *
+ * You can pass in the maximum number of redirects to follow:
+ *
+ * ```php
+ * $browser = $browser->withFollowRedirects(5);
+ * ```
+ *
+ * The request will automatically be rejected when the number of redirects
+ * is exceeded. You can pass in a `0` to reject the request for any
+ * redirects encountered:
+ *
+ * ```php
+ * $browser = $browser->withFollowRedirects(0);
+ *
+ * $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * // only non-redirected responses will now end up here
+ * var_dump($response->getHeaders());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * You can pass in a bool `false` to disable following any redirects. In
+ * this case, requests will resolve with the redirection response instead
+ * of following the `Location` response header:
+ *
+ * ```php
+ * $browser = $browser->withFollowRedirects(false);
+ *
+ * $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * // any redirects will now end up here
+ * var_dump($response->getHeaderLine('Location'));
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * You can pass in a bool `true` to re-enable default redirect handling.
+ * This defaults to following a maximum of 10 redirects:
+ *
+ * ```php
+ * $browser = $browser->withFollowRedirects(true);
+ * ```
+ *
+ * See also [redirects](#redirects) for more details about redirect handling.
+ *
+ * Notice that the [`Browser`](#browser) is an immutable object, i.e. this
+ * method actually returns a *new* [`Browser`](#browser) instance with the
+ * given redirect setting applied.
+ *
+ * @param bool|int $followRedirects
+ * @return self
+ */
+ public function withFollowRedirects($followRedirects)
+ {
+ return $this->withOptions(array(
+ 'followRedirects' => $followRedirects !== false,
+ 'maxRedirects' => \is_bool($followRedirects) ? null : $followRedirects
+ ));
+ }
+
+ /**
+ * Changes whether non-successful HTTP response status codes (4xx and 5xx) will be rejected.
+ *
+ * You can pass in a bool `false` to disable rejecting incoming responses
+ * that use a 4xx or 5xx response status code. In this case, requests will
+ * resolve with the response message indicating an error condition:
+ *
+ * ```php
+ * $browser = $browser->withRejectErrorResponse(false);
+ *
+ * $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * // any HTTP response will now end up here
+ * var_dump($response->getStatusCode(), $response->getReasonPhrase());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * You can pass in a bool `true` to re-enable default status code handling.
+ * This defaults to rejecting any response status codes in the 4xx or 5xx
+ * range:
+ *
+ * ```php
+ * $browser = $browser->withRejectErrorResponse(true);
+ *
+ * $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * // any successful HTTP response will now end up here
+ * var_dump($response->getStatusCode(), $response->getReasonPhrase());
+ * }, function (Exception $e) {
+ * if ($e instanceof React\Http\Message\ResponseException) {
+ * // any HTTP response error message will now end up here
+ * $response = $e->getResponse();
+ * var_dump($response->getStatusCode(), $response->getReasonPhrase());
+ * } else {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * }
+ * });
+ * ```
+ *
+ * Notice that the [`Browser`](#browser) is an immutable object, i.e. this
+ * method actually returns a *new* [`Browser`](#browser) instance with the
+ * given setting applied.
+ *
+ * @param bool $obeySuccessCode
+ * @return self
+ */
+ public function withRejectErrorResponse($obeySuccessCode)
+ {
+ return $this->withOptions(array(
+ 'obeySuccessCode' => $obeySuccessCode,
+ ));
+ }
+
+ /**
+ * Changes the base URL used to resolve relative URLs to.
+ *
+ * If you configure a base URL, any requests to relative URLs will be
+ * processed by first resolving this relative to the given absolute base
+ * URL. This supports resolving relative path references (like `../` etc.).
+ * This is particularly useful for (RESTful) API calls where all endpoints
+ * (URLs) are located under a common base URL.
+ *
+ * ```php
+ * $browser = $browser->withBase('http://api.example.com/v3/');
+ *
+ * // will request http://api.example.com/v3/users
+ * $browser->get('users')->then(…);
+ * ```
+ *
+ * You can pass in a `null` base URL to return a new instance that does not
+ * use a base URL:
+ *
+ * ```php
+ * $browser = $browser->withBase(null);
+ * ```
+ *
+ * Accordingly, any requests using relative URLs to a browser that does not
+ * use a base URL can not be completed and will be rejected without sending
+ * a request.
+ *
+ * This method will throw an `InvalidArgumentException` if the given
+ * `$baseUrl` argument is not a valid URL.
+ *
+ * Notice that the [`Browser`](#browser) is an immutable object, i.e. the `withBase()` method
+ * actually returns a *new* [`Browser`](#browser) instance with the given base URL applied.
+ *
+ * @param string|null $baseUrl absolute base URL
+ * @return self
+ * @throws InvalidArgumentException if the given $baseUrl is not a valid absolute URL
+ * @see self::withoutBase()
+ */
+ public function withBase($baseUrl)
+ {
+ $browser = clone $this;
+ if ($baseUrl === null) {
+ $browser->baseUrl = null;
+ return $browser;
+ }
+
+ $browser->baseUrl = new Uri($baseUrl);
+ if (!\in_array($browser->baseUrl->getScheme(), array('http', 'https')) || $browser->baseUrl->getHost() === '') {
+ throw new \InvalidArgumentException('Base URL must be absolute');
+ }
+
+ return $browser;
+ }
+
+ /**
+ * Changes the HTTP protocol version that will be used for all subsequent requests.
+ *
+ * All the above [request methods](#request-methods) default to sending
+ * requests as HTTP/1.1. This is the preferred HTTP protocol version which
+ * also provides decent backwards-compatibility with legacy HTTP/1.0
+ * servers. As such, there should rarely be a need to explicitly change this
+ * protocol version.
+ *
+ * If you want to explicitly use the legacy HTTP/1.0 protocol version, you
+ * can use this method:
+ *
+ * ```php
+ * $browser = $browser->withProtocolVersion('1.0');
+ *
+ * $browser->get($url)->then(…);
+ * ```
+ *
+ * Notice that the [`Browser`](#browser) is an immutable object, i.e. this
+ * method actually returns a *new* [`Browser`](#browser) instance with the
+ * new protocol version applied.
+ *
+ * @param string $protocolVersion HTTP protocol version to use, must be one of "1.1" or "1.0"
+ * @return self
+ * @throws InvalidArgumentException
+ */
+ public function withProtocolVersion($protocolVersion)
+ {
+ if (!\in_array($protocolVersion, array('1.0', '1.1'), true)) {
+ throw new InvalidArgumentException('Invalid HTTP protocol version, must be one of "1.1" or "1.0"');
+ }
+
+ $browser = clone $this;
+ $browser->protocolVersion = (string) $protocolVersion;
+
+ return $browser;
+ }
+
+ /**
+ * Changes the maximum size for buffering a response body.
+ *
+ * The preferred way to send an HTTP request is by using the above
+ * [request methods](#request-methods), for example the [`get()`](#get)
+ * method to send an HTTP `GET` request. Each of these methods will buffer
+ * the whole response body in memory by default. This is easy to get started
+ * and works reasonably well for smaller responses.
+ *
+ * By default, the response body buffer will be limited to 16 MiB. If the
+ * response body exceeds this maximum size, the request will be rejected.
+ *
+ * You can pass in the maximum number of bytes to buffer:
+ *
+ * ```php
+ * $browser = $browser->withResponseBuffer(1024 * 1024);
+ *
+ * $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) {
+ * // response body will not exceed 1 MiB
+ * var_dump($response->getHeaders(), (string) $response->getBody());
+ * }, function (Exception $e) {
+ * echo 'Error: ' . $e->getMessage() . PHP_EOL;
+ * });
+ * ```
+ *
+ * Note that the response body buffer has to be kept in memory for each
+ * pending request until its transfer is completed and it will only be freed
+ * after a pending request is fulfilled. As such, increasing this maximum
+ * buffer size to allow larger response bodies is usually not recommended.
+ * Instead, you can use the [`requestStreaming()` method](#requeststreaming)
+ * to receive responses with arbitrary sizes without buffering. Accordingly,
+ * this maximum buffer size setting has no effect on streaming responses.
+ *
+ * Notice that the [`Browser`](#browser) is an immutable object, i.e. this
+ * method actually returns a *new* [`Browser`](#browser) instance with the
+ * given setting applied.
+ *
+ * @param int $maximumSize
+ * @return self
+ * @see self::requestStreaming()
+ */
+ public function withResponseBuffer($maximumSize)
+ {
+ return $this->withOptions(array(
+ 'maximumSize' => $maximumSize
+ ));
+ }
+
+ /**
+ * Changes the [options](#options) to use:
+ *
+ * The [`Browser`](#browser) class exposes several options for the handling of
+ * HTTP transactions. These options resemble some of PHP's
+ * [HTTP context options](http://php.net/manual/en/context.http.php) and
+ * can be controlled via the following API (and their defaults):
+ *
+ * ```php
+ * // deprecated
+ * $newBrowser = $browser->withOptions(array(
+ * 'timeout' => null, // see withTimeout() instead
+ * 'followRedirects' => true, // see withFollowRedirects() instead
+ * 'maxRedirects' => 10, // see withFollowRedirects() instead
+ * 'obeySuccessCode' => true, // see withRejectErrorResponse() instead
+ * 'streaming' => false, // deprecated, see requestStreaming() instead
+ * ));
+ * ```
+ *
+ * See also [timeouts](#timeouts), [redirects](#redirects) and
+ * [streaming](#streaming) for more details.
+ *
+ * Notice that the [`Browser`](#browser) is an immutable object, i.e. this
+ * method actually returns a *new* [`Browser`](#browser) instance with the
+ * options applied.
+ *
+ * @param array $options
+ * @return self
+ * @see self::withTimeout()
+ * @see self::withFollowRedirects()
+ * @see self::withRejectErrorResponse()
+ */
+ private function withOptions(array $options)
+ {
+ $browser = clone $this;
+ $browser->transaction = $this->transaction->withOptions($options);
+
+ return $browser;
+ }
+
+ /**
+ * @param string $method
+ * @param string $url
+ * @param array $headers
+ * @param string|ReadableStreamInterface $body
+ * @return PromiseInterface<ResponseInterface,\Exception>
+ */
+ private function requestMayBeStreaming($method, $url, array $headers = array(), $body = '')
+ {
+ if ($this->baseUrl !== null) {
+ // ensure we're actually below the base URL
+ $url = Uri::resolve($this->baseUrl, $url);
+ }
+
+ if ($body instanceof ReadableStreamInterface) {
+ $body = new ReadableBodyStream($body);
+ }
+
+ return $this->transaction->send(
+ new Request($method, $url, $headers, $body, $this->protocolVersion)
+ );
+ }
+}
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();
+ }
+}
diff --git a/vendor/react/http/src/HttpServer.php b/vendor/react/http/src/HttpServer.php
new file mode 100644
index 0000000..f233473
--- /dev/null
+++ b/vendor/react/http/src/HttpServer.php
@@ -0,0 +1,351 @@
+<?php
+
+namespace React\Http;
+
+use Evenement\EventEmitter;
+use React\EventLoop\Loop;
+use React\EventLoop\LoopInterface;
+use React\Http\Io\IniUtil;
+use React\Http\Io\MiddlewareRunner;
+use React\Http\Io\StreamingServer;
+use React\Http\Middleware\LimitConcurrentRequestsMiddleware;
+use React\Http\Middleware\StreamingRequestMiddleware;
+use React\Http\Middleware\RequestBodyBufferMiddleware;
+use React\Http\Middleware\RequestBodyParserMiddleware;
+use React\Socket\ServerInterface;
+
+/**
+ * The `React\Http\HttpServer` class is responsible for handling incoming connections and then
+ * processing each incoming HTTP request.
+ *
+ * When a complete HTTP request has been received, it will invoke the given
+ * request handler function. This request handler function needs to be passed to
+ * the constructor and will be invoked with the respective [request](#server-request)
+ * object and expects a [response](#server-response) object in return:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
+ * return new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * array(
+ * 'Content-Type' => 'text/plain'
+ * ),
+ * "Hello World!\n"
+ * );
+ * });
+ * ```
+ *
+ * Each incoming HTTP request message is always represented by the
+ * [PSR-7 `ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface),
+ * see also following [request](#server-request) chapter for more details.
+ *
+ * Each outgoing HTTP response message is always represented by the
+ * [PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface),
+ * see also following [response](#server-response) chapter for more details.
+ *
+ * This class takes an optional `LoopInterface|null $loop` parameter that can be used to
+ * pass the event loop instance to use for this object. You can use a `null` value
+ * here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
+ * This value SHOULD NOT be given unless you're sure you want to explicitly use a
+ * given event loop instance.
+ *
+ * In order to start listening for any incoming connections, the `HttpServer` needs
+ * to be attached to an instance of
+ * [`React\Socket\ServerInterface`](https://github.com/reactphp/socket#serverinterface)
+ * through the [`listen()`](#listen) method as described in the following
+ * chapter. In its most simple form, you can attach this to a
+ * [`React\Socket\SocketServer`](https://github.com/reactphp/socket#socketserver)
+ * in order to start a plaintext HTTP server like this:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer($handler);
+ *
+ * $socket = new React\Socket\SocketServer('0.0.0.0:8080');
+ * $http->listen($socket);
+ * ```
+ *
+ * See also the [`listen()`](#listen) method and
+ * [hello world server example](../examples/51-server-hello-world.php)
+ * for more details.
+ *
+ * By default, the `HttpServer` buffers and parses the complete incoming HTTP
+ * request in memory. It will invoke the given request handler function when the
+ * complete request headers and request body has been received. This means the
+ * [request](#server-request) object passed to your request handler function will be
+ * fully compatible with PSR-7 (http-message). This provides sane defaults for
+ * 80% of the use cases and is the recommended way to use this library unless
+ * you're sure you know what you're doing.
+ *
+ * On the other hand, buffering complete HTTP requests in memory until they can
+ * be processed by your request handler function means that this class has to
+ * employ a number of limits to avoid consuming too much memory. In order to
+ * take the more advanced configuration out your hand, it respects setting from
+ * your [`php.ini`](https://www.php.net/manual/en/ini.core.php) to apply its
+ * default settings. This is a list of PHP settings this class respects with
+ * their respective default values:
+ *
+ * ```
+ * memory_limit 128M
+ * post_max_size 8M // capped at 64K
+ *
+ * enable_post_data_reading 1
+ * max_input_nesting_level 64
+ * max_input_vars 1000
+ *
+ * file_uploads 1
+ * upload_max_filesize 2M
+ * max_file_uploads 20
+ * ```
+ *
+ * In particular, the `post_max_size` setting limits how much memory a single
+ * HTTP request is allowed to consume while buffering its request body. This
+ * needs to be limited because the server can process a large number of requests
+ * concurrently, so the server may potentially consume a large amount of memory
+ * otherwise. To support higher concurrency by default, this value is capped
+ * at `64K`. If you assign a higher value, it will only allow `64K` by default.
+ * If a request exceeds this limit, its request body will be ignored and it will
+ * be processed like a request with no request body at all. See below for
+ * explicit configuration to override this setting.
+ *
+ * By default, this class will try to avoid consuming more than half of your
+ * `memory_limit` for buffering multiple concurrent HTTP requests. As such, with
+ * the above default settings of `128M` max, it will try to consume no more than
+ * `64M` for buffering multiple concurrent HTTP requests. As a consequence, it
+ * will limit the concurrency to `1024` HTTP requests with the above defaults.
+ *
+ * It is imperative that you assign reasonable values to your PHP ini settings.
+ * It is usually recommended to not support buffering incoming HTTP requests
+ * with a large HTTP request body (e.g. large file uploads). If you want to
+ * increase this buffer size, you will have to also increase the total memory
+ * limit to allow for more concurrent requests (set `memory_limit 512M` or more)
+ * or explicitly limit concurrency.
+ *
+ * In order to override the above buffering defaults, you can configure the
+ * `HttpServer` explicitly. You can use the
+ * [`LimitConcurrentRequestsMiddleware`](#limitconcurrentrequestsmiddleware) and
+ * [`RequestBodyBufferMiddleware`](#requestbodybuffermiddleware) (see below)
+ * to explicitly configure the total number of requests that can be handled at
+ * once like this:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(
+ * new React\Http\Middleware\StreamingRequestMiddleware(),
+ * new React\Http\Middleware\LimitConcurrentRequestsMiddleware(100), // 100 concurrent buffering handlers
+ * new React\Http\Middleware\RequestBodyBufferMiddleware(2 * 1024 * 1024), // 2 MiB per request
+ * new React\Http\Middleware\RequestBodyParserMiddleware(),
+ * $handler
+ * ));
+ * ```
+ *
+ * In this example, we allow processing up to 100 concurrent requests at once
+ * and each request can buffer up to `2M`. This means you may have to keep a
+ * maximum of `200M` of memory for incoming request body buffers. Accordingly,
+ * you need to adjust the `memory_limit` ini setting to allow for these buffers
+ * plus your actual application logic memory requirements (think `512M` or more).
+ *
+ * > Internally, this class automatically assigns these middleware handlers
+ * automatically when no [`StreamingRequestMiddleware`](#streamingrequestmiddleware)
+ * is given. Accordingly, you can use this example to override all default
+ * settings to implement custom limits.
+ *
+ * As an alternative to buffering the complete request body in memory, you can
+ * also use a streaming approach where only small chunks of data have to be kept
+ * in memory:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(
+ * new React\Http\Middleware\StreamingRequestMiddleware(),
+ * $handler
+ * );
+ * ```
+ *
+ * In this case, it will invoke the request handler function once the HTTP
+ * request headers have been received, i.e. before receiving the potentially
+ * much larger HTTP request body. This means the [request](#server-request) passed to
+ * your request handler function may not be fully compatible with PSR-7. This is
+ * specifically designed to help with more advanced use cases where you want to
+ * have full control over consuming the incoming HTTP request body and
+ * concurrency settings. See also [streaming incoming request](#streaming-incoming-request)
+ * below for more details.
+ *
+ * > Changelog v1.5.0: This class has been renamed to `HttpServer` from the
+ * previous `Server` class in order to avoid any ambiguities.
+ * The previous name has been deprecated and should not be used anymore.
+ */
+final class HttpServer extends EventEmitter
+{
+ /**
+ * The maximum buffer size used for each request.
+ *
+ * This needs to be limited because the server can process a large number of
+ * requests concurrently, so the server may potentially consume a large
+ * amount of memory otherwise.
+ *
+ * See `RequestBodyBufferMiddleware` to override this setting.
+ *
+ * @internal
+ */
+ const MAXIMUM_BUFFER_SIZE = 65536; // 64 KiB
+
+ /**
+ * @var StreamingServer
+ */
+ private $streamingServer;
+
+ /**
+ * Creates an HTTP server that invokes the given callback for each incoming HTTP request
+ *
+ * In order to process any connections, the server needs to be attached to an
+ * instance of `React\Socket\ServerInterface` which emits underlying streaming
+ * connections in order to then parse incoming data as HTTP.
+ * See also [listen()](#listen) for more details.
+ *
+ * @param callable|LoopInterface $requestHandlerOrLoop
+ * @param callable[] ...$requestHandler
+ * @see self::listen()
+ */
+ public function __construct($requestHandlerOrLoop)
+ {
+ $requestHandlers = \func_get_args();
+ if (reset($requestHandlers) instanceof LoopInterface) {
+ $loop = \array_shift($requestHandlers);
+ } else {
+ $loop = Loop::get();
+ }
+
+ $requestHandlersCount = \count($requestHandlers);
+ if ($requestHandlersCount === 0 || \count(\array_filter($requestHandlers, 'is_callable')) < $requestHandlersCount) {
+ throw new \InvalidArgumentException('Invalid request handler given');
+ }
+
+ $streaming = false;
+ foreach ((array) $requestHandlers as $handler) {
+ if ($handler instanceof StreamingRequestMiddleware) {
+ $streaming = true;
+ break;
+ }
+ }
+
+ $middleware = array();
+ if (!$streaming) {
+ $maxSize = $this->getMaxRequestSize();
+ $concurrency = $this->getConcurrentRequestsLimit(\ini_get('memory_limit'), $maxSize);
+ if ($concurrency !== null) {
+ $middleware[] = new LimitConcurrentRequestsMiddleware($concurrency);
+ }
+ $middleware[] = new RequestBodyBufferMiddleware($maxSize);
+ // Checking for an empty string because that is what a boolean
+ // false is returned as by ini_get depending on the PHP version.
+ // @link http://php.net/manual/en/ini.core.php#ini.enable-post-data-reading
+ // @link http://php.net/manual/en/function.ini-get.php#refsect1-function.ini-get-notes
+ // @link https://3v4l.org/qJtsa
+ $enablePostDataReading = \ini_get('enable_post_data_reading');
+ if ($enablePostDataReading !== '') {
+ $middleware[] = new RequestBodyParserMiddleware();
+ }
+ }
+
+ $middleware = \array_merge($middleware, $requestHandlers);
+
+ /**
+ * Filter out any configuration middleware, no need to run requests through something that isn't
+ * doing anything with the request.
+ */
+ $middleware = \array_filter($middleware, function ($handler) {
+ return !($handler instanceof StreamingRequestMiddleware);
+ });
+
+ $this->streamingServer = new StreamingServer($loop, new MiddlewareRunner($middleware));
+
+ $that = $this;
+ $this->streamingServer->on('error', function ($error) use ($that) {
+ $that->emit('error', array($error));
+ });
+ }
+
+ /**
+ * Starts listening for HTTP requests on the given socket server instance
+ *
+ * The given [`React\Socket\ServerInterface`](https://github.com/reactphp/socket#serverinterface)
+ * is responsible for emitting the underlying streaming connections. This
+ * HTTP server needs to be attached to it in order to process any
+ * connections and pase incoming streaming data as incoming HTTP request
+ * messages. In its most common form, you can attach this to a
+ * [`React\Socket\SocketServer`](https://github.com/reactphp/socket#socketserver)
+ * in order to start a plaintext HTTP server like this:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer($handler);
+ *
+ * $socket = new React\Socket\SocketServer('0.0.0.0:8080');
+ * $http->listen($socket);
+ * ```
+ *
+ * See also [hello world server example](../examples/51-server-hello-world.php)
+ * for more details.
+ *
+ * This example will start listening for HTTP requests on the alternative
+ * HTTP port `8080` on all interfaces (publicly). As an alternative, it is
+ * very common to use a reverse proxy and let this HTTP server listen on the
+ * localhost (loopback) interface only by using the listen address
+ * `127.0.0.1:8080` instead. This way, you host your application(s) on the
+ * default HTTP port `80` and only route specific requests to this HTTP
+ * server.
+ *
+ * Likewise, it's usually recommended to use a reverse proxy setup to accept
+ * secure HTTPS requests on default HTTPS port `443` (TLS termination) and
+ * only route plaintext requests to this HTTP server. As an alternative, you
+ * can also accept secure HTTPS requests with this HTTP server by attaching
+ * this to a [`React\Socket\SocketServer`](https://github.com/reactphp/socket#socketserver)
+ * using a secure TLS listen address, a certificate file and optional
+ * `passphrase` like this:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer($handler);
+ *
+ * $socket = new React\Socket\SocketServer('tls://0.0.0.0:8443', array(
+ * 'tls' => array(
+ * 'local_cert' => __DIR__ . '/localhost.pem'
+ * )
+ * ));
+ * $http->listen($socket);
+ * ```
+ *
+ * See also [hello world HTTPS example](../examples/61-server-hello-world-https.php)
+ * for more details.
+ *
+ * @param ServerInterface $socket
+ */
+ public function listen(ServerInterface $socket)
+ {
+ $this->streamingServer->listen($socket);
+ }
+
+ /**
+ * @param string $memory_limit
+ * @param string $post_max_size
+ * @return ?int
+ */
+ private function getConcurrentRequestsLimit($memory_limit, $post_max_size)
+ {
+ if ($memory_limit == -1) {
+ return null;
+ }
+
+ $availableMemory = IniUtil::iniSizeToBytes($memory_limit) / 2;
+ $concurrentRequests = (int) \ceil($availableMemory / IniUtil::iniSizeToBytes($post_max_size));
+
+ return $concurrentRequests;
+ }
+
+ /**
+ * @param ?string $post_max_size
+ * @return int
+ */
+ private function getMaxRequestSize($post_max_size = null)
+ {
+ $maxSize = IniUtil::iniSizeToBytes($post_max_size === null ? \ini_get('post_max_size') : $post_max_size);
+
+ return ($maxSize === 0 || $maxSize >= self::MAXIMUM_BUFFER_SIZE) ? self::MAXIMUM_BUFFER_SIZE : $maxSize;
+ }
+}
diff --git a/vendor/react/http/src/Io/BufferedBody.php b/vendor/react/http/src/Io/BufferedBody.php
new file mode 100644
index 0000000..4a4d839
--- /dev/null
+++ b/vendor/react/http/src/Io/BufferedBody.php
@@ -0,0 +1,179 @@
+<?php
+
+namespace React\Http\Io;
+
+use Psr\Http\Message\StreamInterface;
+
+/**
+ * [Internal] PSR-7 message body implementation using an in-memory buffer
+ *
+ * @internal
+ */
+class BufferedBody implements StreamInterface
+{
+ private $buffer = '';
+ private $position = 0;
+ private $closed = false;
+
+ /**
+ * @param string $buffer
+ */
+ public function __construct($buffer)
+ {
+ $this->buffer = $buffer;
+ }
+
+ public function __toString()
+ {
+ if ($this->closed) {
+ return '';
+ }
+
+ $this->seek(0);
+
+ return $this->getContents();
+ }
+
+ public function close()
+ {
+ $this->buffer = '';
+ $this->position = 0;
+ $this->closed = true;
+ }
+
+ public function detach()
+ {
+ $this->close();
+
+ return null;
+ }
+
+ public function getSize()
+ {
+ return $this->closed ? null : \strlen($this->buffer);
+ }
+
+ public function tell()
+ {
+ if ($this->closed) {
+ throw new \RuntimeException('Unable to tell position of closed stream');
+ }
+
+ return $this->position;
+ }
+
+ public function eof()
+ {
+ return $this->position >= \strlen($this->buffer);
+ }
+
+ public function isSeekable()
+ {
+ return !$this->closed;
+ }
+
+ public function seek($offset, $whence = \SEEK_SET)
+ {
+ if ($this->closed) {
+ throw new \RuntimeException('Unable to seek on closed stream');
+ }
+
+ $old = $this->position;
+
+ if ($whence === \SEEK_SET) {
+ $this->position = $offset;
+ } elseif ($whence === \SEEK_CUR) {
+ $this->position += $offset;
+ } elseif ($whence === \SEEK_END) {
+ $this->position = \strlen($this->buffer) + $offset;
+ } else {
+ throw new \InvalidArgumentException('Invalid seek mode given');
+ }
+
+ if (!\is_int($this->position) || $this->position < 0) {
+ $this->position = $old;
+ throw new \RuntimeException('Unable to seek to position');
+ }
+ }
+
+ public function rewind()
+ {
+ $this->seek(0);
+ }
+
+ public function isWritable()
+ {
+ return !$this->closed;
+ }
+
+ public function write($string)
+ {
+ if ($this->closed) {
+ throw new \RuntimeException('Unable to write to closed stream');
+ }
+
+ if ($string === '') {
+ return 0;
+ }
+
+ if ($this->position > 0 && !isset($this->buffer[$this->position - 1])) {
+ $this->buffer = \str_pad($this->buffer, $this->position, "\0");
+ }
+
+ $len = \strlen($string);
+ $this->buffer = \substr($this->buffer, 0, $this->position) . $string . \substr($this->buffer, $this->position + $len);
+ $this->position += $len;
+
+ return $len;
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed;
+ }
+
+ public function read($length)
+ {
+ if ($this->closed) {
+ throw new \RuntimeException('Unable to read from closed stream');
+ }
+
+ if ($length < 1) {
+ throw new \InvalidArgumentException('Invalid read length given');
+ }
+
+ if ($this->position + $length > \strlen($this->buffer)) {
+ $length = \strlen($this->buffer) - $this->position;
+ }
+
+ if (!isset($this->buffer[$this->position])) {
+ return '';
+ }
+
+ $pos = $this->position;
+ $this->position += $length;
+
+ return \substr($this->buffer, $pos, $length);
+ }
+
+ public function getContents()
+ {
+ if ($this->closed) {
+ throw new \RuntimeException('Unable to read from closed stream');
+ }
+
+ if (!isset($this->buffer[$this->position])) {
+ return '';
+ }
+
+ $pos = $this->position;
+ $this->position = \strlen($this->buffer);
+
+ return \substr($this->buffer, $pos);
+ }
+
+ public function getMetadata($key = null)
+ {
+ return $key === null ? array() : null;
+ }
+}
diff --git a/vendor/react/http/src/Io/ChunkedDecoder.php b/vendor/react/http/src/Io/ChunkedDecoder.php
new file mode 100644
index 0000000..2f58f42
--- /dev/null
+++ b/vendor/react/http/src/Io/ChunkedDecoder.php
@@ -0,0 +1,175 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+use Exception;
+
+/**
+ * [Internal] Decodes "Transfer-Encoding: chunked" from given stream and returns only payload data.
+ *
+ * This is used internally to decode incoming requests with this encoding.
+ *
+ * @internal
+ */
+class ChunkedDecoder extends EventEmitter implements ReadableStreamInterface
+{
+ const CRLF = "\r\n";
+ const MAX_CHUNK_HEADER_SIZE = 1024;
+
+ private $closed = false;
+ private $input;
+ private $buffer = '';
+ private $chunkSize = 0;
+ private $transferredSize = 0;
+ private $headerCompleted = false;
+
+ public function __construct(ReadableStreamInterface $input)
+ {
+ $this->input = $input;
+
+ $this->input->on('data', array($this, 'handleData'));
+ $this->input->on('end', array($this, 'handleEnd'));
+ $this->input->on('error', array($this, 'handleError'));
+ $this->input->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed && $this->input->isReadable();
+ }
+
+ public function pause()
+ {
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->buffer = '';
+
+ $this->closed = true;
+
+ $this->input->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ if (!$this->closed) {
+ $this->handleError(new Exception('Unexpected end event'));
+ }
+ }
+
+ /** @internal */
+ public function handleError(Exception $e)
+ {
+ $this->emit('error', array($e));
+ $this->close();
+ }
+
+ /** @internal */
+ public function handleData($data)
+ {
+ $this->buffer .= $data;
+
+ while ($this->buffer !== '') {
+ if (!$this->headerCompleted) {
+ $positionCrlf = \strpos($this->buffer, static::CRLF);
+
+ if ($positionCrlf === false) {
+ // Header shouldn't be bigger than 1024 bytes
+ if (isset($this->buffer[static::MAX_CHUNK_HEADER_SIZE])) {
+ $this->handleError(new Exception('Chunk header size inclusive extension bigger than' . static::MAX_CHUNK_HEADER_SIZE. ' bytes'));
+ }
+ return;
+ }
+
+ $header = \strtolower((string)\substr($this->buffer, 0, $positionCrlf));
+ $hexValue = $header;
+
+ if (\strpos($header, ';') !== false) {
+ $array = \explode(';', $header);
+ $hexValue = $array[0];
+ }
+
+ if ($hexValue !== '') {
+ $hexValue = \ltrim(\trim($hexValue), "0");
+ if ($hexValue === '') {
+ $hexValue = "0";
+ }
+ }
+
+ $this->chunkSize = @\hexdec($hexValue);
+ if (!\is_int($this->chunkSize) || \dechex($this->chunkSize) !== $hexValue) {
+ $this->handleError(new Exception($hexValue . ' is not a valid hexadecimal number'));
+ return;
+ }
+
+ $this->buffer = (string)\substr($this->buffer, $positionCrlf + 2);
+ $this->headerCompleted = true;
+ if ($this->buffer === '') {
+ return;
+ }
+ }
+
+ $chunk = (string)\substr($this->buffer, 0, $this->chunkSize - $this->transferredSize);
+
+ if ($chunk !== '') {
+ $this->transferredSize += \strlen($chunk);
+ $this->emit('data', array($chunk));
+ $this->buffer = (string)\substr($this->buffer, \strlen($chunk));
+ }
+
+ $positionCrlf = \strpos($this->buffer, static::CRLF);
+
+ if ($positionCrlf === 0) {
+ if ($this->chunkSize === 0) {
+ $this->emit('end');
+ $this->close();
+ return;
+ }
+ $this->chunkSize = 0;
+ $this->headerCompleted = false;
+ $this->transferredSize = 0;
+ $this->buffer = (string)\substr($this->buffer, 2);
+ } elseif ($this->chunkSize === 0) {
+ // end chunk received, skip all trailer data
+ $this->buffer = (string)\substr($this->buffer, $positionCrlf);
+ }
+
+ if ($positionCrlf !== 0 && $this->chunkSize !== 0 && $this->chunkSize === $this->transferredSize && \strlen($this->buffer) > 2) {
+ // the first 2 characters are not CRLF, send error event
+ $this->handleError(new Exception('Chunk does not end with a CRLF'));
+ return;
+ }
+
+ if ($positionCrlf !== 0 && \strlen($this->buffer) < 2) {
+ // No CRLF found, wait for additional data which could be a CRLF
+ return;
+ }
+ }
+ }
+}
diff --git a/vendor/react/http/src/Io/ChunkedEncoder.php b/vendor/react/http/src/Io/ChunkedEncoder.php
new file mode 100644
index 0000000..c84ef54
--- /dev/null
+++ b/vendor/react/http/src/Io/ChunkedEncoder.php
@@ -0,0 +1,92 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * [Internal] Encodes given payload stream with "Transfer-Encoding: chunked" and emits encoded data
+ *
+ * This is used internally to encode outgoing requests with this encoding.
+ *
+ * @internal
+ */
+class ChunkedEncoder extends EventEmitter implements ReadableStreamInterface
+{
+ private $input;
+ private $closed = false;
+
+ public function __construct(ReadableStreamInterface $input)
+ {
+ $this->input = $input;
+
+ $this->input->on('data', array($this, 'handleData'));
+ $this->input->on('end', array($this, 'handleEnd'));
+ $this->input->on('error', array($this, 'handleError'));
+ $this->input->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed && $this->input->isReadable();
+ }
+
+ public function pause()
+ {
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ return Util::pipe($this, $dest, $options);
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+ $this->input->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleData($data)
+ {
+ if ($data !== '') {
+ $this->emit('data', array(
+ \dechex(\strlen($data)) . "\r\n" . $data . "\r\n"
+ ));
+ }
+ }
+
+ /** @internal */
+ public function handleError(\Exception $e)
+ {
+ $this->emit('error', array($e));
+ $this->close();
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ $this->emit('data', array("0\r\n\r\n"));
+
+ if (!$this->closed) {
+ $this->emit('end');
+ $this->close();
+ }
+ }
+}
diff --git a/vendor/react/http/src/Io/CloseProtectionStream.php b/vendor/react/http/src/Io/CloseProtectionStream.php
new file mode 100644
index 0000000..2e1ed6e
--- /dev/null
+++ b/vendor/react/http/src/Io/CloseProtectionStream.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * [Internal] Protects a given stream from actually closing and only discards its incoming data instead.
+ *
+ * This is used internally to prevent the underlying connection from closing, so
+ * that we can still send back a response over the same stream.
+ *
+ * @internal
+ * */
+class CloseProtectionStream extends EventEmitter implements ReadableStreamInterface
+{
+ private $input;
+ private $closed = false;
+ private $paused = false;
+
+ /**
+ * @param ReadableStreamInterface $input stream that will be discarded instead of closing it on an 'close' event.
+ */
+ public function __construct(ReadableStreamInterface $input)
+ {
+ $this->input = $input;
+
+ $this->input->on('data', array($this, 'handleData'));
+ $this->input->on('end', array($this, 'handleEnd'));
+ $this->input->on('error', array($this, 'handleError'));
+ $this->input->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed && $this->input->isReadable();
+ }
+
+ public function pause()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->paused = true;
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->paused = false;
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+
+ // stop listening for incoming events
+ $this->input->removeListener('data', array($this, 'handleData'));
+ $this->input->removeListener('error', array($this, 'handleError'));
+ $this->input->removeListener('end', array($this, 'handleEnd'));
+ $this->input->removeListener('close', array($this, 'close'));
+
+ // resume the stream to ensure we discard everything from incoming connection
+ if ($this->paused) {
+ $this->paused = false;
+ $this->input->resume();
+ }
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleData($data)
+ {
+ $this->emit('data', array($data));
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ $this->emit('end');
+ $this->close();
+ }
+
+ /** @internal */
+ public function handleError(\Exception $e)
+ {
+ $this->emit('error', array($e));
+ }
+}
diff --git a/vendor/react/http/src/Io/EmptyBodyStream.php b/vendor/react/http/src/Io/EmptyBodyStream.php
new file mode 100644
index 0000000..5056219
--- /dev/null
+++ b/vendor/react/http/src/Io/EmptyBodyStream.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use Psr\Http\Message\StreamInterface;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * [Internal] Bridge between an empty StreamInterface from PSR-7 and ReadableStreamInterface from ReactPHP
+ *
+ * This class is used in the server to represent an empty body stream of an
+ * incoming response from the client. This is similar to the `HttpBodyStream`,
+ * but is specifically designed for the common case of having an empty message
+ * body.
+ *
+ * Note that this is an internal class only and nothing you should usually care
+ * about. See the `StreamInterface` and `ReadableStreamInterface` for more
+ * details.
+ *
+ * @see HttpBodyStream
+ * @see StreamInterface
+ * @see ReadableStreamInterface
+ * @internal
+ */
+class EmptyBodyStream extends EventEmitter implements StreamInterface, ReadableStreamInterface
+{
+ private $closed = false;
+
+ public function isReadable()
+ {
+ return !$this->closed;
+ }
+
+ public function pause()
+ {
+ // NOOP
+ }
+
+ public function resume()
+ {
+ // NOOP
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ public function getSize()
+ {
+ return 0;
+ }
+
+ /** @ignore */
+ public function __toString()
+ {
+ return '';
+ }
+
+ /** @ignore */
+ public function detach()
+ {
+ return null;
+ }
+
+ /** @ignore */
+ public function tell()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function eof()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ /** @ignore */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function rewind()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function isWritable()
+ {
+ return false;
+ }
+
+ /** @ignore */
+ public function write($string)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function read($length)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function getContents()
+ {
+ return '';
+ }
+
+ /** @ignore */
+ public function getMetadata($key = null)
+ {
+ return ($key === null) ? array() : null;
+ }
+}
diff --git a/vendor/react/http/src/Io/HttpBodyStream.php b/vendor/react/http/src/Io/HttpBodyStream.php
new file mode 100644
index 0000000..25d15a1
--- /dev/null
+++ b/vendor/react/http/src/Io/HttpBodyStream.php
@@ -0,0 +1,182 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use Psr\Http\Message\StreamInterface;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * [Internal] Bridge between StreamInterface from PSR-7 and ReadableStreamInterface from ReactPHP
+ *
+ * This class is used in the server to stream the body of an incoming response
+ * from the client. This allows us to stream big amounts of data without having
+ * to buffer this data. Similarly, this used to stream the body of an outgoing
+ * request body to the client. The data will be sent directly to the client.
+ *
+ * Note that this is an internal class only and nothing you should usually care
+ * about. See the `StreamInterface` and `ReadableStreamInterface` for more
+ * details.
+ *
+ * @see StreamInterface
+ * @see ReadableStreamInterface
+ * @internal
+ */
+class HttpBodyStream extends EventEmitter implements StreamInterface, ReadableStreamInterface
+{
+ public $input;
+ private $closed = false;
+ private $size;
+
+ /**
+ * @param ReadableStreamInterface $input Stream data from $stream as a body of a PSR-7 object4
+ * @param int|null $size size of the data body
+ */
+ public function __construct(ReadableStreamInterface $input, $size)
+ {
+ $this->input = $input;
+ $this->size = $size;
+
+ $this->input->on('data', array($this, 'handleData'));
+ $this->input->on('end', array($this, 'handleEnd'));
+ $this->input->on('error', array($this, 'handleError'));
+ $this->input->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed && $this->input->isReadable();
+ }
+
+ public function pause()
+ {
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+
+ $this->input->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ /** @ignore */
+ public function __toString()
+ {
+ return '';
+ }
+
+ /** @ignore */
+ public function detach()
+ {
+ return null;
+ }
+
+ /** @ignore */
+ public function tell()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function eof()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ /** @ignore */
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function rewind()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function isWritable()
+ {
+ return false;
+ }
+
+ /** @ignore */
+ public function write($string)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function read($length)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ /** @ignore */
+ public function getContents()
+ {
+ return '';
+ }
+
+ /** @ignore */
+ public function getMetadata($key = null)
+ {
+ return null;
+ }
+
+ /** @internal */
+ public function handleData($data)
+ {
+ $this->emit('data', array($data));
+ }
+
+ /** @internal */
+ public function handleError(\Exception $e)
+ {
+ $this->emit('error', array($e));
+ $this->close();
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ if (!$this->closed) {
+ $this->emit('end');
+ $this->close();
+ }
+ }
+}
diff --git a/vendor/react/http/src/Io/IniUtil.php b/vendor/react/http/src/Io/IniUtil.php
new file mode 100644
index 0000000..612aae2
--- /dev/null
+++ b/vendor/react/http/src/Io/IniUtil.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace React\Http\Io;
+
+/**
+ * @internal
+ */
+final class IniUtil
+{
+ /**
+ * Convert a ini like size to a numeric size in bytes.
+ *
+ * @param string $size
+ * @return int
+ */
+ public static function iniSizeToBytes($size)
+ {
+ if (\is_numeric($size)) {
+ return (int)$size;
+ }
+
+ $suffix = \strtoupper(\substr($size, -1));
+ $strippedSize = \substr($size, 0, -1);
+
+ if (!\is_numeric($strippedSize)) {
+ throw new \InvalidArgumentException("$size is not a valid ini size");
+ }
+
+ if ($strippedSize <= 0) {
+ throw new \InvalidArgumentException("Expect $size to be higher isn't zero or lower");
+ }
+
+ if ($suffix === 'K') {
+ return $strippedSize * 1024;
+ }
+ if ($suffix === 'M') {
+ return $strippedSize * 1024 * 1024;
+ }
+ if ($suffix === 'G') {
+ return $strippedSize * 1024 * 1024 * 1024;
+ }
+ if ($suffix === 'T') {
+ return $strippedSize * 1024 * 1024 * 1024 * 1024;
+ }
+
+ return (int)$size;
+ }
+}
diff --git a/vendor/react/http/src/Io/LengthLimitedStream.php b/vendor/react/http/src/Io/LengthLimitedStream.php
new file mode 100644
index 0000000..bc64c54
--- /dev/null
+++ b/vendor/react/http/src/Io/LengthLimitedStream.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * [Internal] Limits the amount of data the given stream can emit
+ *
+ * This is used internally to limit the size of the underlying connection stream
+ * to the size defined by the "Content-Length" header of the incoming request.
+ *
+ * @internal
+ */
+class LengthLimitedStream extends EventEmitter implements ReadableStreamInterface
+{
+ private $stream;
+ private $closed = false;
+ private $transferredLength = 0;
+ private $maxLength;
+
+ public function __construct(ReadableStreamInterface $stream, $maxLength)
+ {
+ $this->stream = $stream;
+ $this->maxLength = $maxLength;
+
+ $this->stream->on('data', array($this, 'handleData'));
+ $this->stream->on('end', array($this, 'handleEnd'));
+ $this->stream->on('error', array($this, 'handleError'));
+ $this->stream->on('close', array($this, 'close'));
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed && $this->stream->isReadable();
+ }
+
+ public function pause()
+ {
+ $this->stream->pause();
+ }
+
+ public function resume()
+ {
+ $this->stream->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+
+ $this->stream->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleData($data)
+ {
+ if (($this->transferredLength + \strlen($data)) > $this->maxLength) {
+ // Only emit data until the value of 'Content-Length' is reached, the rest will be ignored
+ $data = (string)\substr($data, 0, $this->maxLength - $this->transferredLength);
+ }
+
+ if ($data !== '') {
+ $this->transferredLength += \strlen($data);
+ $this->emit('data', array($data));
+ }
+
+ if ($this->transferredLength === $this->maxLength) {
+ // 'Content-Length' reached, stream will end
+ $this->emit('end');
+ $this->close();
+ $this->stream->removeListener('data', array($this, 'handleData'));
+ }
+ }
+
+ /** @internal */
+ public function handleError(\Exception $e)
+ {
+ $this->emit('error', array($e));
+ $this->close();
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ if (!$this->closed) {
+ $this->handleError(new \Exception('Unexpected end event'));
+ }
+ }
+
+}
diff --git a/vendor/react/http/src/Io/MiddlewareRunner.php b/vendor/react/http/src/Io/MiddlewareRunner.php
new file mode 100644
index 0000000..dedf6ff
--- /dev/null
+++ b/vendor/react/http/src/Io/MiddlewareRunner.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace React\Http\Io;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use React\Promise\PromiseInterface;
+
+/**
+ * [Internal] Middleware runner to expose an array of middleware request handlers as a single request handler callable
+ *
+ * @internal
+ */
+final class MiddlewareRunner
+{
+ /**
+ * @var callable[]
+ */
+ private $middleware;
+
+ /**
+ * @param callable[] $middleware
+ */
+ public function __construct(array $middleware)
+ {
+ $this->middleware = \array_values($middleware);
+ }
+
+ /**
+ * @param ServerRequestInterface $request
+ * @return ResponseInterface|PromiseInterface<ResponseInterface>
+ * @throws \Exception
+ */
+ public function __invoke(ServerRequestInterface $request)
+ {
+ if (empty($this->middleware)) {
+ throw new \RuntimeException('No middleware to run');
+ }
+
+ return $this->call($request, 0);
+ }
+
+ /** @internal */
+ public function call(ServerRequestInterface $request, $position)
+ {
+ // final request handler will be invoked without a next handler
+ if (!isset($this->middleware[$position + 1])) {
+ $handler = $this->middleware[$position];
+ return $handler($request);
+ }
+
+ $that = $this;
+ $next = function (ServerRequestInterface $request) use ($that, $position) {
+ return $that->call($request, $position + 1);
+ };
+
+ // invoke middleware request handler with next handler
+ $handler = $this->middleware[$position];
+ return $handler($request, $next);
+ }
+}
diff --git a/vendor/react/http/src/Io/MultipartParser.php b/vendor/react/http/src/Io/MultipartParser.php
new file mode 100644
index 0000000..536694f
--- /dev/null
+++ b/vendor/react/http/src/Io/MultipartParser.php
@@ -0,0 +1,328 @@
+<?php
+
+namespace React\Http\Io;
+
+use Psr\Http\Message\ServerRequestInterface;
+
+/**
+ * [Internal] Parses a string body with "Content-Type: multipart/form-data" into structured data
+ *
+ * This is use internally to parse incoming request bodies into structured data
+ * that resembles PHP's `$_POST` and `$_FILES` superglobals.
+ *
+ * @internal
+ * @link https://tools.ietf.org/html/rfc7578
+ * @link https://tools.ietf.org/html/rfc2046#section-5.1.1
+ */
+final class MultipartParser
+{
+ /**
+ * @var ServerRequestInterface|null
+ */
+ private $request;
+
+ /**
+ * @var int|null
+ */
+ private $maxFileSize;
+
+ /**
+ * ini setting "max_input_vars"
+ *
+ * Does not exist in PHP < 5.3.9 or HHVM, so assume PHP's default 1000 here.
+ *
+ * @var int
+ * @link http://php.net/manual/en/info.configuration.php#ini.max-input-vars
+ */
+ private $maxInputVars = 1000;
+
+ /**
+ * ini setting "max_input_nesting_level"
+ *
+ * Does not exist in HHVM, but assumes hard coded to 64 (PHP's default).
+ *
+ * @var int
+ * @link http://php.net/manual/en/info.configuration.php#ini.max-input-nesting-level
+ */
+ private $maxInputNestingLevel = 64;
+
+ /**
+ * ini setting "upload_max_filesize"
+ *
+ * @var int
+ */
+ private $uploadMaxFilesize;
+
+ /**
+ * ini setting "max_file_uploads"
+ *
+ * Additionally, setting "file_uploads = off" effectively sets this to zero.
+ *
+ * @var int
+ */
+ private $maxFileUploads;
+
+ private $postCount = 0;
+ private $filesCount = 0;
+ private $emptyCount = 0;
+
+ /**
+ * @param int|string|null $uploadMaxFilesize
+ * @param int|null $maxFileUploads
+ */
+ public function __construct($uploadMaxFilesize = null, $maxFileUploads = null)
+ {
+ $var = \ini_get('max_input_vars');
+ if ($var !== false) {
+ $this->maxInputVars = (int)$var;
+ }
+ $var = \ini_get('max_input_nesting_level');
+ if ($var !== false) {
+ $this->maxInputNestingLevel = (int)$var;
+ }
+
+ if ($uploadMaxFilesize === null) {
+ $uploadMaxFilesize = \ini_get('upload_max_filesize');
+ }
+
+ $this->uploadMaxFilesize = IniUtil::iniSizeToBytes($uploadMaxFilesize);
+ $this->maxFileUploads = $maxFileUploads === null ? (\ini_get('file_uploads') === '' ? 0 : (int)\ini_get('max_file_uploads')) : (int)$maxFileUploads;
+ }
+
+ public function parse(ServerRequestInterface $request)
+ {
+ $contentType = $request->getHeaderLine('content-type');
+ if(!\preg_match('/boundary="?(.*?)"?$/', $contentType, $matches)) {
+ return $request;
+ }
+
+ $this->request = $request;
+ $this->parseBody('--' . $matches[1], (string)$request->getBody());
+
+ $request = $this->request;
+ $this->request = null;
+ $this->postCount = 0;
+ $this->filesCount = 0;
+ $this->emptyCount = 0;
+ $this->maxFileSize = null;
+
+ return $request;
+ }
+
+ private function parseBody($boundary, $buffer)
+ {
+ $len = \strlen($boundary);
+
+ // ignore everything before initial boundary (SHOULD be empty)
+ $start = \strpos($buffer, $boundary . "\r\n");
+
+ while ($start !== false) {
+ // search following boundary (preceded by newline)
+ // ignore last if not followed by boundary (SHOULD end with "--")
+ $start += $len + 2;
+ $end = \strpos($buffer, "\r\n" . $boundary, $start);
+ if ($end === false) {
+ break;
+ }
+
+ // parse one part and continue searching for next
+ $this->parsePart(\substr($buffer, $start, $end - $start));
+ $start = $end;
+ }
+ }
+
+ private function parsePart($chunk)
+ {
+ $pos = \strpos($chunk, "\r\n\r\n");
+ if ($pos === false) {
+ return;
+ }
+
+ $headers = $this->parseHeaders((string)substr($chunk, 0, $pos));
+ $body = (string)\substr($chunk, $pos + 4);
+
+ if (!isset($headers['content-disposition'])) {
+ return;
+ }
+
+ $name = $this->getParameterFromHeader($headers['content-disposition'], 'name');
+ if ($name === null) {
+ return;
+ }
+
+ $filename = $this->getParameterFromHeader($headers['content-disposition'], 'filename');
+ if ($filename !== null) {
+ $this->parseFile(
+ $name,
+ $filename,
+ isset($headers['content-type'][0]) ? $headers['content-type'][0] : null,
+ $body
+ );
+ } else {
+ $this->parsePost($name, $body);
+ }
+ }
+
+ private function parseFile($name, $filename, $contentType, $contents)
+ {
+ $file = $this->parseUploadedFile($filename, $contentType, $contents);
+ if ($file === null) {
+ return;
+ }
+
+ $this->request = $this->request->withUploadedFiles($this->extractPost(
+ $this->request->getUploadedFiles(),
+ $name,
+ $file
+ ));
+ }
+
+ private function parseUploadedFile($filename, $contentType, $contents)
+ {
+ $size = \strlen($contents);
+
+ // no file selected (zero size and empty filename)
+ if ($size === 0 && $filename === '') {
+ // ignore excessive number of empty file uploads
+ if (++$this->emptyCount + $this->filesCount > $this->maxInputVars) {
+ return;
+ }
+
+ return new UploadedFile(
+ new BufferedBody(''),
+ $size,
+ \UPLOAD_ERR_NO_FILE,
+ $filename,
+ $contentType
+ );
+ }
+
+ // ignore excessive number of file uploads
+ if (++$this->filesCount > $this->maxFileUploads) {
+ return;
+ }
+
+ // file exceeds "upload_max_filesize" ini setting
+ if ($size > $this->uploadMaxFilesize) {
+ return new UploadedFile(
+ new BufferedBody(''),
+ $size,
+ \UPLOAD_ERR_INI_SIZE,
+ $filename,
+ $contentType
+ );
+ }
+
+ // file exceeds MAX_FILE_SIZE value
+ if ($this->maxFileSize !== null && $size > $this->maxFileSize) {
+ return new UploadedFile(
+ new BufferedBody(''),
+ $size,
+ \UPLOAD_ERR_FORM_SIZE,
+ $filename,
+ $contentType
+ );
+ }
+
+ return new UploadedFile(
+ new BufferedBody($contents),
+ $size,
+ \UPLOAD_ERR_OK,
+ $filename,
+ $contentType
+ );
+ }
+
+ private function parsePost($name, $value)
+ {
+ // ignore excessive number of post fields
+ if (++$this->postCount > $this->maxInputVars) {
+ return;
+ }
+
+ $this->request = $this->request->withParsedBody($this->extractPost(
+ $this->request->getParsedBody(),
+ $name,
+ $value
+ ));
+
+ if (\strtoupper($name) === 'MAX_FILE_SIZE') {
+ $this->maxFileSize = (int)$value;
+
+ if ($this->maxFileSize === 0) {
+ $this->maxFileSize = null;
+ }
+ }
+ }
+
+ private function parseHeaders($header)
+ {
+ $headers = array();
+
+ foreach (\explode("\r\n", \trim($header)) as $line) {
+ $parts = \explode(':', $line, 2);
+ if (!isset($parts[1])) {
+ continue;
+ }
+
+ $key = \strtolower(trim($parts[0]));
+ $values = \explode(';', $parts[1]);
+ $values = \array_map('trim', $values);
+ $headers[$key] = $values;
+ }
+
+ return $headers;
+ }
+
+ private function getParameterFromHeader(array $header, $parameter)
+ {
+ foreach ($header as $part) {
+ if (\preg_match('/' . $parameter . '="?(.*?)"?$/', $part, $matches)) {
+ return $matches[1];
+ }
+ }
+
+ return null;
+ }
+
+ private function extractPost($postFields, $key, $value)
+ {
+ $chunks = \explode('[', $key);
+ if (\count($chunks) == 1) {
+ $postFields[$key] = $value;
+ return $postFields;
+ }
+
+ // ignore this key if maximum nesting level is exceeded
+ if (isset($chunks[$this->maxInputNestingLevel])) {
+ return $postFields;
+ }
+
+ $chunkKey = \rtrim($chunks[0], ']');
+ $parent = &$postFields;
+ for ($i = 1; isset($chunks[$i]); $i++) {
+ $previousChunkKey = $chunkKey;
+
+ if ($previousChunkKey === '') {
+ $parent[] = array();
+ \end($parent);
+ $parent = &$parent[\key($parent)];
+ } else {
+ if (!isset($parent[$previousChunkKey]) || !\is_array($parent[$previousChunkKey])) {
+ $parent[$previousChunkKey] = array();
+ }
+ $parent = &$parent[$previousChunkKey];
+ }
+
+ $chunkKey = \rtrim($chunks[$i], ']');
+ }
+
+ if ($chunkKey === '') {
+ $parent[] = $value;
+ } else {
+ $parent[$chunkKey] = $value;
+ }
+
+ return $postFields;
+ }
+}
diff --git a/vendor/react/http/src/Io/PauseBufferStream.php b/vendor/react/http/src/Io/PauseBufferStream.php
new file mode 100644
index 0000000..fb5ed45
--- /dev/null
+++ b/vendor/react/http/src/Io/PauseBufferStream.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * [Internal] Pauses a given stream and buffers all events while paused
+ *
+ * This class is used to buffer all events that happen on a given stream while
+ * it is paused. This allows you to pause a stream and no longer watch for any
+ * of its events. Once the stream is resumed, all buffered events will be
+ * emitted. Explicitly closing the resulting stream clears all buffers.
+ *
+ * Note that this is an internal class only and nothing you should usually care
+ * about.
+ *
+ * @see ReadableStreamInterface
+ * @internal
+ */
+class PauseBufferStream extends EventEmitter implements ReadableStreamInterface
+{
+ private $input;
+ private $closed = false;
+ private $paused = false;
+ private $dataPaused = '';
+ private $endPaused = false;
+ private $closePaused = false;
+ private $errorPaused;
+ private $implicit = false;
+
+ public function __construct(ReadableStreamInterface $input)
+ {
+ $this->input = $input;
+
+ $this->input->on('data', array($this, 'handleData'));
+ $this->input->on('end', array($this, 'handleEnd'));
+ $this->input->on('error', array($this, 'handleError'));
+ $this->input->on('close', array($this, 'handleClose'));
+ }
+
+ /**
+ * pause and remember this was not explicitly from user control
+ *
+ * @internal
+ */
+ public function pauseImplicit()
+ {
+ $this->pause();
+ $this->implicit = true;
+ }
+
+ /**
+ * resume only if this was previously paused implicitly and not explicitly from user control
+ *
+ * @internal
+ */
+ public function resumeImplicit()
+ {
+ if ($this->implicit) {
+ $this->resume();
+ }
+ }
+
+ public function isReadable()
+ {
+ return !$this->closed;
+ }
+
+ public function pause()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->input->pause();
+ $this->paused = true;
+ $this->implicit = false;
+ }
+
+ public function resume()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->paused = false;
+ $this->implicit = false;
+
+ if ($this->dataPaused !== '') {
+ $this->emit('data', array($this->dataPaused));
+ $this->dataPaused = '';
+ }
+
+ if ($this->errorPaused) {
+ $this->emit('error', array($this->errorPaused));
+ return $this->close();
+ }
+
+ if ($this->endPaused) {
+ $this->endPaused = false;
+ $this->emit('end');
+ return $this->close();
+ }
+
+ if ($this->closePaused) {
+ $this->closePaused = false;
+ return $this->close();
+ }
+
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function close()
+ {
+ if ($this->closed) {
+ return;
+ }
+
+ $this->closed = true;
+ $this->dataPaused = '';
+ $this->endPaused = $this->closePaused = false;
+ $this->errorPaused = null;
+
+ $this->input->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+
+ /** @internal */
+ public function handleData($data)
+ {
+ if ($this->paused) {
+ $this->dataPaused .= $data;
+ return;
+ }
+
+ $this->emit('data', array($data));
+ }
+
+ /** @internal */
+ public function handleError(\Exception $e)
+ {
+ if ($this->paused) {
+ $this->errorPaused = $e;
+ return;
+ }
+
+ $this->emit('error', array($e));
+ $this->close();
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ if ($this->paused) {
+ $this->endPaused = true;
+ return;
+ }
+
+ if (!$this->closed) {
+ $this->emit('end');
+ $this->close();
+ }
+ }
+
+ /** @internal */
+ public function handleClose()
+ {
+ if ($this->paused) {
+ $this->closePaused = true;
+ return;
+ }
+
+ $this->close();
+ }
+}
diff --git a/vendor/react/http/src/Io/ReadableBodyStream.php b/vendor/react/http/src/Io/ReadableBodyStream.php
new file mode 100644
index 0000000..daef45f
--- /dev/null
+++ b/vendor/react/http/src/Io/ReadableBodyStream.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use Psr\Http\Message\StreamInterface;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\Util;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * @internal
+ */
+class ReadableBodyStream extends EventEmitter implements ReadableStreamInterface, StreamInterface
+{
+ private $input;
+ private $position = 0;
+ private $size;
+ private $closed = false;
+
+ public function __construct(ReadableStreamInterface $input, $size = null)
+ {
+ $this->input = $input;
+ $this->size = $size;
+
+ $that = $this;
+ $pos =& $this->position;
+ $input->on('data', function ($data) use ($that, &$pos, $size) {
+ $that->emit('data', array($data));
+
+ $pos += \strlen($data);
+ if ($size !== null && $pos >= $size) {
+ $that->handleEnd();
+ }
+ });
+ $input->on('error', function ($error) use ($that) {
+ $that->emit('error', array($error));
+ $that->close();
+ });
+ $input->on('end', array($that, 'handleEnd'));
+ $input->on('close', array($that, 'close'));
+ }
+
+ public function close()
+ {
+ if (!$this->closed) {
+ $this->closed = true;
+ $this->input->close();
+
+ $this->emit('close');
+ $this->removeAllListeners();
+ }
+ }
+
+ public function isReadable()
+ {
+ return $this->input->isReadable();
+ }
+
+ public function pause()
+ {
+ $this->input->pause();
+ }
+
+ public function resume()
+ {
+ $this->input->resume();
+ }
+
+ public function pipe(WritableStreamInterface $dest, array $options = array())
+ {
+ Util::pipe($this, $dest, $options);
+
+ return $dest;
+ }
+
+ public function eof()
+ {
+ return !$this->isReadable();
+ }
+
+ public function __toString()
+ {
+ return '';
+ }
+
+ public function detach()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ public function tell()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ public function isSeekable()
+ {
+ return false;
+ }
+
+ public function seek($offset, $whence = SEEK_SET)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ public function rewind()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ public function isWritable()
+ {
+ return false;
+ }
+
+ public function write($string)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ public function read($length)
+ {
+ throw new \BadMethodCallException();
+ }
+
+ public function getContents()
+ {
+ throw new \BadMethodCallException();
+ }
+
+ public function getMetadata($key = null)
+ {
+ return ($key === null) ? array() : null;
+ }
+
+ /** @internal */
+ public function handleEnd()
+ {
+ if ($this->position !== $this->size && $this->size !== null) {
+ $this->emit('error', array(new \UnderflowException('Unexpected end of response body after ' . $this->position . '/' . $this->size . ' bytes')));
+ } else {
+ $this->emit('end');
+ }
+
+ $this->close();
+ }
+}
diff --git a/vendor/react/http/src/Io/RequestHeaderParser.php b/vendor/react/http/src/Io/RequestHeaderParser.php
new file mode 100644
index 0000000..e5554c4
--- /dev/null
+++ b/vendor/react/http/src/Io/RequestHeaderParser.php
@@ -0,0 +1,281 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use Psr\Http\Message\ServerRequestInterface;
+use React\Http\Message\Response;
+use React\Http\Message\ServerRequest;
+use React\Socket\ConnectionInterface;
+use Exception;
+
+/**
+ * [Internal] Parses an incoming request header from an input stream
+ *
+ * This is used internally to parse the request header from the connection and
+ * then process the remaining connection as the request body.
+ *
+ * @event headers
+ * @event error
+ *
+ * @internal
+ */
+class RequestHeaderParser extends EventEmitter
+{
+ private $maxSize = 8192;
+
+ public function handle(ConnectionInterface $conn)
+ {
+ $buffer = '';
+ $maxSize = $this->maxSize;
+ $that = $this;
+ $conn->on('data', $fn = function ($data) use (&$buffer, &$fn, $conn, $maxSize, $that) {
+ // append chunk of data to buffer and look for end of request headers
+ $buffer .= $data;
+ $endOfHeader = \strpos($buffer, "\r\n\r\n");
+
+ // reject request if buffer size is exceeded
+ if ($endOfHeader > $maxSize || ($endOfHeader === false && isset($buffer[$maxSize]))) {
+ $conn->removeListener('data', $fn);
+ $fn = null;
+
+ $that->emit('error', array(
+ new \OverflowException("Maximum header size of {$maxSize} exceeded.", Response::STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE),
+ $conn
+ ));
+ return;
+ }
+
+ // ignore incomplete requests
+ if ($endOfHeader === false) {
+ return;
+ }
+
+ // request headers received => try to parse request
+ $conn->removeListener('data', $fn);
+ $fn = null;
+
+ try {
+ $request = $that->parseRequest(
+ (string)\substr($buffer, 0, $endOfHeader + 2),
+ $conn->getRemoteAddress(),
+ $conn->getLocalAddress()
+ );
+ } catch (Exception $exception) {
+ $buffer = '';
+ $that->emit('error', array(
+ $exception,
+ $conn
+ ));
+ return;
+ }
+
+ $contentLength = 0;
+ if ($request->hasHeader('Transfer-Encoding')) {
+ $contentLength = null;
+ } elseif ($request->hasHeader('Content-Length')) {
+ $contentLength = (int)$request->getHeaderLine('Content-Length');
+ }
+
+ if ($contentLength === 0) {
+ // happy path: request body is known to be empty
+ $stream = new EmptyBodyStream();
+ $request = $request->withBody($stream);
+ } else {
+ // otherwise body is present => delimit using Content-Length or ChunkedDecoder
+ $stream = new CloseProtectionStream($conn);
+ if ($contentLength !== null) {
+ $stream = new LengthLimitedStream($stream, $contentLength);
+ } else {
+ $stream = new ChunkedDecoder($stream);
+ }
+
+ $request = $request->withBody(new HttpBodyStream($stream, $contentLength));
+ }
+
+ $bodyBuffer = isset($buffer[$endOfHeader + 4]) ? \substr($buffer, $endOfHeader + 4) : '';
+ $buffer = '';
+ $that->emit('headers', array($request, $conn));
+
+ if ($bodyBuffer !== '') {
+ $conn->emit('data', array($bodyBuffer));
+ }
+
+ // happy path: request body is known to be empty => immediately end stream
+ if ($contentLength === 0) {
+ $stream->emit('end');
+ $stream->close();
+ }
+ });
+ }
+
+ /**
+ * @param string $headers buffer string containing request headers only
+ * @param ?string $remoteSocketUri
+ * @param ?string $localSocketUri
+ * @return ServerRequestInterface
+ * @throws \InvalidArgumentException
+ * @internal
+ */
+ public function parseRequest($headers, $remoteSocketUri, $localSocketUri)
+ {
+ // additional, stricter safe-guard for request line
+ // because request parser doesn't properly cope with invalid ones
+ $start = array();
+ if (!\preg_match('#^(?<method>[^ ]+) (?<target>[^ ]+) HTTP/(?<version>\d\.\d)#m', $headers, $start)) {
+ throw new \InvalidArgumentException('Unable to parse invalid request-line');
+ }
+
+ // only support HTTP/1.1 and HTTP/1.0 requests
+ if ($start['version'] !== '1.1' && $start['version'] !== '1.0') {
+ throw new \InvalidArgumentException('Received request with invalid protocol version', Response::STATUS_VERSION_NOT_SUPPORTED);
+ }
+
+ // match all request header fields into array, thanks to @kelunik for checking the HTTP specs and coming up with this regex
+ $matches = array();
+ $n = \preg_match_all('/^([^()<>@,;:\\\"\/\[\]?={}\x01-\x20\x7F]++):[\x20\x09]*+((?:[\x20\x09]*+[\x21-\x7E\x80-\xFF]++)*+)[\x20\x09]*+[\r]?+\n/m', $headers, $matches, \PREG_SET_ORDER);
+
+ // check number of valid header fields matches number of lines + request line
+ if (\substr_count($headers, "\n") !== $n + 1) {
+ throw new \InvalidArgumentException('Unable to parse invalid request header fields');
+ }
+
+ // format all header fields into associative array
+ $host = null;
+ $fields = array();
+ foreach ($matches as $match) {
+ $fields[$match[1]][] = $match[2];
+
+ // match `Host` request header
+ if ($host === null && \strtolower($match[1]) === 'host') {
+ $host = $match[2];
+ }
+ }
+
+ // create new obj implementing ServerRequestInterface by preserving all
+ // previous properties and restoring original request-target
+ $serverParams = array(
+ 'REQUEST_TIME' => \time(),
+ 'REQUEST_TIME_FLOAT' => \microtime(true)
+ );
+
+ // scheme is `http` unless TLS is used
+ $localParts = $localSocketUri === null ? array() : \parse_url($localSocketUri);
+ if (isset($localParts['scheme']) && $localParts['scheme'] === 'tls') {
+ $scheme = 'https://';
+ $serverParams['HTTPS'] = 'on';
+ } else {
+ $scheme = 'http://';
+ }
+
+ // default host if unset comes from local socket address or defaults to localhost
+ $hasHost = $host !== null;
+ if ($host === null) {
+ $host = isset($localParts['host'], $localParts['port']) ? $localParts['host'] . ':' . $localParts['port'] : '127.0.0.1';
+ }
+
+ if ($start['method'] === 'OPTIONS' && $start['target'] === '*') {
+ // support asterisk-form for `OPTIONS *` request line only
+ $uri = $scheme . $host;
+ } elseif ($start['method'] === 'CONNECT') {
+ $parts = \parse_url('tcp://' . $start['target']);
+
+ // check this is a valid authority-form request-target (host:port)
+ if (!isset($parts['scheme'], $parts['host'], $parts['port']) || \count($parts) !== 3) {
+ throw new \InvalidArgumentException('CONNECT method MUST use authority-form request target');
+ }
+ $uri = $scheme . $start['target'];
+ } else {
+ // support absolute-form or origin-form for proxy requests
+ if ($start['target'][0] === '/') {
+ $uri = $scheme . $host . $start['target'];
+ } else {
+ // ensure absolute-form request-target contains a valid URI
+ $parts = \parse_url($start['target']);
+
+ // make sure value contains valid host component (IP or hostname), but no fragment
+ if (!isset($parts['scheme'], $parts['host']) || $parts['scheme'] !== 'http' || isset($parts['fragment'])) {
+ throw new \InvalidArgumentException('Invalid absolute-form request-target');
+ }
+
+ $uri = $start['target'];
+ }
+ }
+
+ // apply REMOTE_ADDR and REMOTE_PORT if source address is known
+ // address should always be known, unless this is over Unix domain sockets (UDS)
+ if ($remoteSocketUri !== null) {
+ $remoteAddress = \parse_url($remoteSocketUri);
+ $serverParams['REMOTE_ADDR'] = $remoteAddress['host'];
+ $serverParams['REMOTE_PORT'] = $remoteAddress['port'];
+ }
+
+ // apply SERVER_ADDR and SERVER_PORT if server address is known
+ // address should always be known, even for Unix domain sockets (UDS)
+ // but skip UDS as it doesn't have a concept of host/port.
+ if ($localSocketUri !== null && isset($localParts['host'], $localParts['port'])) {
+ $serverParams['SERVER_ADDR'] = $localParts['host'];
+ $serverParams['SERVER_PORT'] = $localParts['port'];
+ }
+
+ $request = new ServerRequest(
+ $start['method'],
+ $uri,
+ $fields,
+ '',
+ $start['version'],
+ $serverParams
+ );
+
+ // only assign request target if it is not in origin-form (happy path for most normal requests)
+ if ($start['target'][0] !== '/') {
+ $request = $request->withRequestTarget($start['target']);
+ }
+
+ if ($hasHost) {
+ // Optional Host request header value MUST be valid (host and optional port)
+ $parts = \parse_url('http://' . $request->getHeaderLine('Host'));
+
+ // make sure value contains valid host component (IP or hostname)
+ if (!$parts || !isset($parts['scheme'], $parts['host'])) {
+ $parts = false;
+ }
+
+ // make sure value does not contain any other URI component
+ if (\is_array($parts)) {
+ unset($parts['scheme'], $parts['host'], $parts['port']);
+ }
+ if ($parts === false || $parts) {
+ throw new \InvalidArgumentException('Invalid Host header value');
+ }
+ } elseif (!$hasHost && $start['version'] === '1.1' && $start['method'] !== 'CONNECT') {
+ // require Host request header for HTTP/1.1 (except for CONNECT method)
+ throw new \InvalidArgumentException('Missing required Host request header');
+ } elseif (!$hasHost) {
+ // remove default Host request header for HTTP/1.0 when not explicitly given
+ $request = $request->withoutHeader('Host');
+ }
+
+ // ensure message boundaries are valid according to Content-Length and Transfer-Encoding request headers
+ if ($request->hasHeader('Transfer-Encoding')) {
+ if (\strtolower($request->getHeaderLine('Transfer-Encoding')) !== 'chunked') {
+ throw new \InvalidArgumentException('Only chunked-encoding is allowed for Transfer-Encoding', Response::STATUS_NOT_IMPLEMENTED);
+ }
+
+ // Transfer-Encoding: chunked and Content-Length header MUST NOT be used at the same time
+ // as per https://tools.ietf.org/html/rfc7230#section-3.3.3
+ if ($request->hasHeader('Content-Length')) {
+ throw new \InvalidArgumentException('Using both `Transfer-Encoding: chunked` and `Content-Length` is not allowed', Response::STATUS_BAD_REQUEST);
+ }
+ } elseif ($request->hasHeader('Content-Length')) {
+ $string = $request->getHeaderLine('Content-Length');
+
+ if ((string)(int)$string !== $string) {
+ // Content-Length value is not an integer or not a single integer
+ throw new \InvalidArgumentException('The value of `Content-Length` is not valid', Response::STATUS_BAD_REQUEST);
+ }
+ }
+
+ return $request;
+ }
+}
diff --git a/vendor/react/http/src/Io/Sender.php b/vendor/react/http/src/Io/Sender.php
new file mode 100644
index 0000000..2f04c79
--- /dev/null
+++ b/vendor/react/http/src/Io/Sender.php
@@ -0,0 +1,160 @@
+<?php
+
+namespace React\Http\Io;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use React\EventLoop\LoopInterface;
+use React\Http\Client\Client as HttpClient;
+use React\Http\Message\Response;
+use React\Promise\PromiseInterface;
+use React\Promise\Deferred;
+use React\Socket\ConnectorInterface;
+use React\Stream\ReadableStreamInterface;
+
+/**
+ * [Internal] Sends requests and receives responses
+ *
+ * The `Sender` is responsible for passing the [`RequestInterface`](#requestinterface) objects to
+ * the underlying [`HttpClient`](https://github.com/reactphp/http-client) library
+ * and keeps track of its transmission and converts its reponses back to [`ResponseInterface`](#responseinterface) objects.
+ *
+ * It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage)
+ * and the default [`Connector`](https://github.com/reactphp/socket-client) and [DNS `Resolver`](https://github.com/reactphp/dns).
+ *
+ * The `Sender` class mostly exists in order to abstract changes on the underlying
+ * components away from this package in order to provide backwards and forwards
+ * compatibility.
+ *
+ * @internal You SHOULD NOT rely on this API, it is subject to change without prior notice!
+ * @see Browser
+ */
+class Sender
+{
+ /**
+ * create a new default sender attached to the given event loop
+ *
+ * This method is used internally to create the "default sender".
+ *
+ * You may also use this method if you need custom DNS or connector
+ * settings. You can use this method manually like this:
+ *
+ * ```php
+ * $connector = new \React\Socket\Connector(array(), $loop);
+ * $sender = \React\Http\Io\Sender::createFromLoop($loop, $connector);
+ * ```
+ *
+ * @param LoopInterface $loop
+ * @param ConnectorInterface|null $connector
+ * @return self
+ */
+ public static function createFromLoop(LoopInterface $loop, ConnectorInterface $connector = null)
+ {
+ return new self(new HttpClient($loop, $connector));
+ }
+
+ private $http;
+
+ /**
+ * [internal] Instantiate Sender
+ *
+ * @param HttpClient $http
+ * @internal
+ */
+ public function __construct(HttpClient $http)
+ {
+ $this->http = $http;
+ }
+
+ /**
+ *
+ * @internal
+ * @param RequestInterface $request
+ * @return PromiseInterface Promise<ResponseInterface, Exception>
+ */
+ public function send(RequestInterface $request)
+ {
+ $body = $request->getBody();
+ $size = $body->getSize();
+
+ if ($size !== null && $size !== 0) {
+ // automatically assign a "Content-Length" request header if the body size is known and non-empty
+ $request = $request->withHeader('Content-Length', (string)$size);
+ } elseif ($size === 0 && \in_array($request->getMethod(), array('POST', 'PUT', 'PATCH'))) {
+ // only assign a "Content-Length: 0" request header if the body is expected for certain methods
+ $request = $request->withHeader('Content-Length', '0');
+ } elseif ($body instanceof ReadableStreamInterface && $body->isReadable() && !$request->hasHeader('Content-Length')) {
+ // use "Transfer-Encoding: chunked" when this is a streaming body and body size is unknown
+ $request = $request->withHeader('Transfer-Encoding', 'chunked');
+ } else {
+ // do not use chunked encoding if size is known or if this is an empty request body
+ $size = 0;
+ }
+
+ $headers = array();
+ foreach ($request->getHeaders() as $name => $values) {
+ $headers[$name] = implode(', ', $values);
+ }
+
+ $requestStream = $this->http->request($request->getMethod(), (string)$request->getUri(), $headers, $request->getProtocolVersion());
+
+ $deferred = new Deferred(function ($_, $reject) use ($requestStream) {
+ // close request stream if request is cancelled
+ $reject(new \RuntimeException('Request cancelled'));
+ $requestStream->close();
+ });
+
+ $requestStream->on('error', function($error) use ($deferred) {
+ $deferred->reject($error);
+ });
+
+ $requestStream->on('response', function (ResponseInterface $response, ReadableStreamInterface $body) use ($deferred, $request) {
+ $length = null;
+ $code = $response->getStatusCode();
+ if ($request->getMethod() === 'HEAD' || ($code >= 100 && $code < 200) || $code == Response::STATUS_NO_CONTENT || $code == Response::STATUS_NOT_MODIFIED) {
+ $length = 0;
+ } elseif (\strtolower($response->getHeaderLine('Transfer-Encoding')) === 'chunked') {
+ $body = new ChunkedDecoder($body);
+ } elseif ($response->hasHeader('Content-Length')) {
+ $length = (int) $response->getHeaderLine('Content-Length');
+ }
+
+ $deferred->resolve($response->withBody(new ReadableBodyStream($body, $length)));
+ });
+
+ if ($body instanceof ReadableStreamInterface) {
+ if ($body->isReadable()) {
+ // length unknown => apply chunked transfer-encoding
+ if ($size === null) {
+ $body = new ChunkedEncoder($body);
+ }
+
+ // pipe body into request stream
+ // add dummy write to immediately start request even if body does not emit any data yet
+ $body->pipe($requestStream);
+ $requestStream->write('');
+
+ $body->on('close', $close = function () use ($deferred, $requestStream) {
+ $deferred->reject(new \RuntimeException('Request failed because request body closed unexpectedly'));
+ $requestStream->close();
+ });
+ $body->on('error', function ($e) use ($deferred, $requestStream, $close, $body) {
+ $body->removeListener('close', $close);
+ $deferred->reject(new \RuntimeException('Request failed because request body reported an error', 0, $e));
+ $requestStream->close();
+ });
+ $body->on('end', function () use ($close, $body) {
+ $body->removeListener('close', $close);
+ });
+ } else {
+ // stream is not readable => end request without body
+ $requestStream->end();
+ }
+ } else {
+ // body is fully buffered => write as one chunk
+ $requestStream->end((string)$body);
+ }
+
+ return $deferred->promise();
+ }
+}
diff --git a/vendor/react/http/src/Io/StreamingServer.php b/vendor/react/http/src/Io/StreamingServer.php
new file mode 100644
index 0000000..7818f0b
--- /dev/null
+++ b/vendor/react/http/src/Io/StreamingServer.php
@@ -0,0 +1,383 @@
+<?php
+
+namespace React\Http\Io;
+
+use Evenement\EventEmitter;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use React\EventLoop\LoopInterface;
+use React\Http\Message\Response;
+use React\Http\Message\ServerRequest;
+use React\Promise;
+use React\Promise\CancellablePromiseInterface;
+use React\Promise\PromiseInterface;
+use React\Socket\ConnectionInterface;
+use React\Socket\ServerInterface;
+use React\Stream\ReadableStreamInterface;
+use React\Stream\WritableStreamInterface;
+
+/**
+ * The internal `StreamingServer` class is responsible for handling incoming connections and then
+ * processing each incoming HTTP request.
+ *
+ * Unlike the [`HttpServer`](#httpserver) class, it does not buffer and parse the incoming
+ * HTTP request body by default. This means that the request handler will be
+ * invoked with a streaming request body. Once the request headers have been
+ * received, it will invoke the request handler function. This request handler
+ * function needs to be passed to the constructor and will be invoked with the
+ * respective [request](#request) object and expects a [response](#response)
+ * object in return:
+ *
+ * ```php
+ * $server = new StreamingServer($loop, function (ServerRequestInterface $request) {
+ * return new Response(
+ * Response::STATUS_OK,
+ * array(
+ * 'Content-Type' => 'text/plain'
+ * ),
+ * "Hello World!\n"
+ * );
+ * });
+ * ```
+ *
+ * Each incoming HTTP request message is always represented by the
+ * [PSR-7 `ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface),
+ * see also following [request](#request) chapter for more details.
+ * Each outgoing HTTP response message is always represented by the
+ * [PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface),
+ * see also following [response](#response) chapter for more details.
+ *
+ * In order to process any connections, the server needs to be attached to an
+ * instance of `React\Socket\ServerInterface` through the [`listen()`](#listen) method
+ * as described in the following chapter. In its most simple form, you can attach
+ * this to a [`React\Socket\SocketServer`](https://github.com/reactphp/socket#socketserver)
+ * in order to start a plaintext HTTP server like this:
+ *
+ * ```php
+ * $server = new StreamingServer($loop, $handler);
+ *
+ * $socket = new React\Socket\SocketServer('0.0.0.0:8080', array(), $loop);
+ * $server->listen($socket);
+ * ```
+ *
+ * See also the [`listen()`](#listen) method and the [first example](examples) for more details.
+ *
+ * The `StreamingServer` class is considered advanced usage and unless you know
+ * what you're doing, you're recommended to use the [`HttpServer`](#httpserver) class
+ * instead. The `StreamingServer` class is specifically designed to help with
+ * more advanced use cases where you want to have full control over consuming
+ * the incoming HTTP request body and concurrency settings.
+ *
+ * In particular, this class does not buffer and parse the incoming HTTP request
+ * in memory. It will invoke the request handler function once the HTTP request
+ * headers have been received, i.e. before receiving the potentially much larger
+ * HTTP request body. This means the [request](#request) passed to your request
+ * handler function may not be fully compatible with PSR-7. See also
+ * [streaming request](#streaming-request) below for more details.
+ *
+ * @see \React\Http\HttpServer
+ * @see \React\Http\Message\Response
+ * @see self::listen()
+ * @internal
+ */
+final class StreamingServer extends EventEmitter
+{
+ private $callback;
+ private $parser;
+ private $loop;
+
+ /**
+ * Creates an HTTP server that invokes the given callback for each incoming HTTP request
+ *
+ * In order to process any connections, the server needs to be attached to an
+ * instance of `React\Socket\ServerInterface` which emits underlying streaming
+ * connections in order to then parse incoming data as HTTP.
+ * See also [listen()](#listen) for more details.
+ *
+ * @param LoopInterface $loop
+ * @param callable $requestHandler
+ * @see self::listen()
+ */
+ public function __construct(LoopInterface $loop, $requestHandler)
+ {
+ if (!\is_callable($requestHandler)) {
+ throw new \InvalidArgumentException('Invalid request handler given');
+ }
+
+ $this->loop = $loop;
+
+ $this->callback = $requestHandler;
+ $this->parser = new RequestHeaderParser();
+
+ $that = $this;
+ $this->parser->on('headers', function (ServerRequestInterface $request, ConnectionInterface $conn) use ($that) {
+ $that->handleRequest($conn, $request);
+ });
+
+ $this->parser->on('error', function(\Exception $e, ConnectionInterface $conn) use ($that) {
+ $that->emit('error', array($e));
+
+ // parsing failed => assume dummy request and send appropriate error
+ $that->writeError(
+ $conn,
+ $e->getCode() !== 0 ? $e->getCode() : Response::STATUS_BAD_REQUEST,
+ new ServerRequest('GET', '/')
+ );
+ });
+ }
+
+ /**
+ * Starts listening for HTTP requests on the given socket server instance
+ *
+ * @param ServerInterface $socket
+ * @see \React\Http\HttpServer::listen()
+ */
+ public function listen(ServerInterface $socket)
+ {
+ $socket->on('connection', array($this->parser, 'handle'));
+ }
+
+ /** @internal */
+ public function handleRequest(ConnectionInterface $conn, ServerRequestInterface $request)
+ {
+ if ($request->getProtocolVersion() !== '1.0' && '100-continue' === \strtolower($request->getHeaderLine('Expect'))) {
+ $conn->write("HTTP/1.1 100 Continue\r\n\r\n");
+ }
+
+ // execute request handler callback
+ $callback = $this->callback;
+ try {
+ $response = $callback($request);
+ } catch (\Exception $error) {
+ // request handler callback throws an Exception
+ $response = Promise\reject($error);
+ } catch (\Throwable $error) { // @codeCoverageIgnoreStart
+ // request handler callback throws a PHP7+ Error
+ $response = Promise\reject($error); // @codeCoverageIgnoreEnd
+ }
+
+ // cancel pending promise once connection closes
+ if ($response instanceof CancellablePromiseInterface) {
+ $conn->on('close', function () use ($response) {
+ $response->cancel();
+ });
+ }
+
+ // happy path: response returned, handle and return immediately
+ if ($response instanceof ResponseInterface) {
+ return $this->handleResponse($conn, $request, $response);
+ }
+
+ // did not return a promise? this is an error, convert into one for rejection below.
+ if (!$response instanceof PromiseInterface) {
+ $response = Promise\resolve($response);
+ }
+
+ $that = $this;
+ $response->then(
+ function ($response) use ($that, $conn, $request) {
+ if (!$response instanceof ResponseInterface) {
+ $message = 'The response callback is expected to resolve with an object implementing Psr\Http\Message\ResponseInterface, but resolved with "%s" instead.';
+ $message = \sprintf($message, \is_object($response) ? \get_class($response) : \gettype($response));
+ $exception = new \RuntimeException($message);
+
+ $that->emit('error', array($exception));
+ return $that->writeError($conn, Response::STATUS_INTERNAL_SERVER_ERROR, $request);
+ }
+ $that->handleResponse($conn, $request, $response);
+ },
+ function ($error) use ($that, $conn, $request) {
+ $message = 'The response callback is expected to resolve with an object implementing Psr\Http\Message\ResponseInterface, but rejected with "%s" instead.';
+ $message = \sprintf($message, \is_object($error) ? \get_class($error) : \gettype($error));
+
+ $previous = null;
+
+ if ($error instanceof \Throwable || $error instanceof \Exception) {
+ $previous = $error;
+ }
+
+ $exception = new \RuntimeException($message, 0, $previous);
+
+ $that->emit('error', array($exception));
+ return $that->writeError($conn, Response::STATUS_INTERNAL_SERVER_ERROR, $request);
+ }
+ );
+ }
+
+ /** @internal */
+ public function writeError(ConnectionInterface $conn, $code, ServerRequestInterface $request)
+ {
+ $response = new Response(
+ $code,
+ array(
+ 'Content-Type' => 'text/plain',
+ 'Connection' => 'close' // we do not want to keep the connection open after an error
+ ),
+ 'Error ' . $code
+ );
+
+ // append reason phrase to response body if known
+ $reason = $response->getReasonPhrase();
+ if ($reason !== '') {
+ $body = $response->getBody();
+ $body->seek(0, SEEK_END);
+ $body->write(': ' . $reason);
+ }
+
+ $this->handleResponse($conn, $request, $response);
+ }
+
+
+ /** @internal */
+ public function handleResponse(ConnectionInterface $connection, ServerRequestInterface $request, ResponseInterface $response)
+ {
+ // return early and close response body if connection is already closed
+ $body = $response->getBody();
+ if (!$connection->isWritable()) {
+ $body->close();
+ return;
+ }
+
+ $code = $response->getStatusCode();
+ $method = $request->getMethod();
+
+ // assign HTTP protocol version from request automatically
+ $version = $request->getProtocolVersion();
+ $response = $response->withProtocolVersion($version);
+
+ // assign default "Server" header automatically
+ if (!$response->hasHeader('Server')) {
+ $response = $response->withHeader('Server', 'ReactPHP/1');
+ } elseif ($response->getHeaderLine('Server') === ''){
+ $response = $response->withoutHeader('Server');
+ }
+
+ // assign default "Date" header from current time automatically
+ if (!$response->hasHeader('Date')) {
+ // IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT
+ $response = $response->withHeader('Date', gmdate('D, d M Y H:i:s') . ' GMT');
+ } elseif ($response->getHeaderLine('Date') === ''){
+ $response = $response->withoutHeader('Date');
+ }
+
+ // assign "Content-Length" header automatically
+ $chunked = false;
+ if (($method === 'CONNECT' && $code >= 200 && $code < 300) || ($code >= 100 && $code < 200) || $code === Response::STATUS_NO_CONTENT) {
+ // 2xx response to CONNECT and 1xx and 204 MUST NOT include Content-Length or Transfer-Encoding header
+ $response = $response->withoutHeader('Content-Length');
+ } elseif ($code === Response::STATUS_NOT_MODIFIED && ($response->hasHeader('Content-Length') || $body->getSize() === 0)) {
+ // 304 Not Modified: preserve explicit Content-Length and preserve missing header if body is empty
+ } elseif ($body->getSize() !== null) {
+ // assign Content-Length header when using a "normal" buffered body string
+ $response = $response->withHeader('Content-Length', (string)$body->getSize());
+ } elseif (!$response->hasHeader('Content-Length') && $version === '1.1') {
+ // assign chunked transfer-encoding if no 'content-length' is given for HTTP/1.1 responses
+ $chunked = true;
+ }
+
+ // assign "Transfer-Encoding" header automatically
+ if ($chunked) {
+ $response = $response->withHeader('Transfer-Encoding', 'chunked');
+ } else {
+ // remove any Transfer-Encoding headers unless automatically enabled above
+ $response = $response->withoutHeader('Transfer-Encoding');
+ }
+
+ // assign "Connection" header automatically
+ $persist = false;
+ if ($code === Response::STATUS_SWITCHING_PROTOCOLS) {
+ // 101 (Switching Protocols) response uses Connection: upgrade header
+ // This implies that this stream now uses another protocol and we
+ // may not persist this connection for additional requests.
+ $response = $response->withHeader('Connection', 'upgrade');
+ } elseif (\strtolower($request->getHeaderLine('Connection')) === 'close' || \strtolower($response->getHeaderLine('Connection')) === 'close') {
+ // obey explicit "Connection: close" request header or response header if present
+ $response = $response->withHeader('Connection', 'close');
+ } elseif ($version === '1.1') {
+ // HTTP/1.1 assumes persistent connection support by default, so we don't need to inform client
+ $persist = true;
+ } elseif (strtolower($request->getHeaderLine('Connection')) === 'keep-alive') {
+ // obey explicit "Connection: keep-alive" request header and inform client
+ $persist = true;
+ $response = $response->withHeader('Connection', 'keep-alive');
+ } else {
+ // remove any Connection headers unless automatically enabled above
+ $response = $response->withoutHeader('Connection');
+ }
+
+ // 101 (Switching Protocols) response (for Upgrade request) forwards upgraded data through duplex stream
+ // 2xx (Successful) response to CONNECT forwards tunneled application data through duplex stream
+ if (($code === Response::STATUS_SWITCHING_PROTOCOLS || ($method === 'CONNECT' && $code >= 200 && $code < 300)) && $body instanceof HttpBodyStream && $body->input instanceof WritableStreamInterface) {
+ if ($request->getBody()->isReadable()) {
+ // request is still streaming => wait for request close before forwarding following data from connection
+ $request->getBody()->on('close', function () use ($connection, $body) {
+ if ($body->input->isWritable()) {
+ $connection->pipe($body->input);
+ $connection->resume();
+ }
+ });
+ } elseif ($body->input->isWritable()) {
+ // request already closed => forward following data from connection
+ $connection->pipe($body->input);
+ $connection->resume();
+ }
+ }
+
+ // build HTTP response header by appending status line and header fields
+ $headers = "HTTP/" . $version . " " . $code . " " . $response->getReasonPhrase() . "\r\n";
+ foreach ($response->getHeaders() as $name => $values) {
+ foreach ($values as $value) {
+ $headers .= $name . ": " . $value . "\r\n";
+ }
+ }
+
+ // response to HEAD and 1xx, 204 and 304 responses MUST NOT include a body
+ // exclude status 101 (Switching Protocols) here for Upgrade request handling above
+ if ($method === 'HEAD' || ($code >= 100 && $code < 200 && $code !== Response::STATUS_SWITCHING_PROTOCOLS) || $code === Response::STATUS_NO_CONTENT || $code === Response::STATUS_NOT_MODIFIED) {
+ $body->close();
+ $body = '';
+ }
+
+ // this is a non-streaming response body or the body stream already closed?
+ if (!$body instanceof ReadableStreamInterface || !$body->isReadable()) {
+ // add final chunk if a streaming body is already closed and uses `Transfer-Encoding: chunked`
+ if ($body instanceof ReadableStreamInterface && $chunked) {
+ $body = "0\r\n\r\n";
+ }
+
+ // write response headers and body
+ $connection->write($headers . "\r\n" . $body);
+
+ // either wait for next request over persistent connection or end connection
+ if ($persist) {
+ $this->parser->handle($connection);
+ } else {
+ $connection->end();
+ }
+ return;
+ }
+
+ $connection->write($headers . "\r\n");
+
+ if ($chunked) {
+ $body = new ChunkedEncoder($body);
+ }
+
+ // Close response stream once connection closes.
+ // Note that this TCP/IP close detection may take some time,
+ // in particular this may only fire on a later read/write attempt.
+ $connection->on('close', array($body, 'close'));
+
+ // write streaming body and then wait for next request over persistent connection
+ if ($persist) {
+ $body->pipe($connection, array('end' => false));
+ $parser = $this->parser;
+ $body->on('end', function () use ($connection, $parser, $body) {
+ $connection->removeListener('close', array($body, 'close'));
+ $parser->handle($connection);
+ });
+ } else {
+ $body->pipe($connection);
+ }
+ }
+}
diff --git a/vendor/react/http/src/Io/Transaction.php b/vendor/react/http/src/Io/Transaction.php
new file mode 100644
index 0000000..7bf7008
--- /dev/null
+++ b/vendor/react/http/src/Io/Transaction.php
@@ -0,0 +1,303 @@
+<?php
+
+namespace React\Http\Io;
+
+use Psr\Http\Message\RequestInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\UriInterface;
+use RingCentral\Psr7\Request;
+use RingCentral\Psr7\Uri;
+use React\EventLoop\LoopInterface;
+use React\Http\Message\ResponseException;
+use React\Promise\Deferred;
+use React\Promise\PromiseInterface;
+use React\Stream\ReadableStreamInterface;
+
+/**
+ * @internal
+ */
+class Transaction
+{
+ private $sender;
+ private $loop;
+
+ // context: http.timeout (ini_get('default_socket_timeout'): 60)
+ private $timeout;
+
+ // context: http.follow_location (true)
+ private $followRedirects = true;
+
+ // context: http.max_redirects (10)
+ private $maxRedirects = 10;
+
+ // context: http.ignore_errors (false)
+ private $obeySuccessCode = true;
+
+ private $streaming = false;
+
+ private $maximumSize = 16777216; // 16 MiB = 2^24 bytes
+
+ public function __construct(Sender $sender, LoopInterface $loop)
+ {
+ $this->sender = $sender;
+ $this->loop = $loop;
+ }
+
+ /**
+ * @param array $options
+ * @return self returns new instance, without modifying existing instance
+ */
+ public function withOptions(array $options)
+ {
+ $transaction = clone $this;
+ foreach ($options as $name => $value) {
+ if (property_exists($transaction, $name)) {
+ // restore default value if null is given
+ if ($value === null) {
+ $default = new self($this->sender, $this->loop);
+ $value = $default->$name;
+ }
+
+ $transaction->$name = $value;
+ }
+ }
+
+ return $transaction;
+ }
+
+ public function send(RequestInterface $request)
+ {
+ $deferred = new Deferred(function () use (&$deferred) {
+ if (isset($deferred->pending)) {
+ $deferred->pending->cancel();
+ unset($deferred->pending);
+ }
+ });
+
+ $deferred->numRequests = 0;
+
+ // use timeout from options or default to PHP's default_socket_timeout (60)
+ $timeout = (float)($this->timeout !== null ? $this->timeout : ini_get("default_socket_timeout"));
+
+ $loop = $this->loop;
+ $this->next($request, $deferred)->then(
+ function (ResponseInterface $response) use ($deferred, $loop, &$timeout) {
+ if (isset($deferred->timeout)) {
+ $loop->cancelTimer($deferred->timeout);
+ unset($deferred->timeout);
+ }
+ $timeout = -1;
+ $deferred->resolve($response);
+ },
+ function ($e) use ($deferred, $loop, &$timeout) {
+ if (isset($deferred->timeout)) {
+ $loop->cancelTimer($deferred->timeout);
+ unset($deferred->timeout);
+ }
+ $timeout = -1;
+ $deferred->reject($e);
+ }
+ );
+
+ if ($timeout < 0) {
+ return $deferred->promise();
+ }
+
+ $body = $request->getBody();
+ if ($body instanceof ReadableStreamInterface && $body->isReadable()) {
+ $that = $this;
+ $body->on('close', function () use ($that, $deferred, &$timeout) {
+ if ($timeout >= 0) {
+ $that->applyTimeout($deferred, $timeout);
+ }
+ });
+ } else {
+ $this->applyTimeout($deferred, $timeout);
+ }
+
+ return $deferred->promise();
+ }
+
+ /**
+ * @internal
+ * @param Deferred $deferred
+ * @param number $timeout
+ * @return void
+ */
+ public function applyTimeout(Deferred $deferred, $timeout)
+ {
+ $deferred->timeout = $this->loop->addTimer($timeout, function () use ($timeout, $deferred) {
+ $deferred->reject(new \RuntimeException(
+ 'Request timed out after ' . $timeout . ' seconds'
+ ));
+ if (isset($deferred->pending)) {
+ $deferred->pending->cancel();
+ unset($deferred->pending);
+ }
+ });
+ }
+
+ private function next(RequestInterface $request, Deferred $deferred)
+ {
+ $this->progress('request', array($request));
+
+ $that = $this;
+ ++$deferred->numRequests;
+
+ $promise = $this->sender->send($request);
+
+ if (!$this->streaming) {
+ $promise = $promise->then(function ($response) use ($deferred, $that) {
+ return $that->bufferResponse($response, $deferred);
+ });
+ }
+
+ $deferred->pending = $promise;
+
+ return $promise->then(
+ function (ResponseInterface $response) use ($request, $that, $deferred) {
+ return $that->onResponse($response, $request, $deferred);
+ }
+ );
+ }
+
+ /**
+ * @internal
+ * @param ResponseInterface $response
+ * @return PromiseInterface Promise<ResponseInterface, Exception>
+ */
+ public function bufferResponse(ResponseInterface $response, $deferred)
+ {
+ $stream = $response->getBody();
+
+ $size = $stream->getSize();
+ if ($size !== null && $size > $this->maximumSize) {
+ $stream->close();
+ return \React\Promise\reject(new \OverflowException(
+ 'Response body size of ' . $size . ' bytes exceeds maximum of ' . $this->maximumSize . ' bytes',
+ \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 0
+ ));
+ }
+
+ // body is not streaming => already buffered
+ if (!$stream instanceof ReadableStreamInterface) {
+ return \React\Promise\resolve($response);
+ }
+
+ // buffer stream and resolve with buffered body
+ $maximumSize = $this->maximumSize;
+ $promise = \React\Promise\Stream\buffer($stream, $maximumSize)->then(
+ function ($body) use ($response) {
+ return $response->withBody(new BufferedBody($body));
+ },
+ function ($e) use ($stream, $maximumSize) {
+ // try to close stream if buffering fails (or is cancelled)
+ $stream->close();
+
+ if ($e instanceof \OverflowException) {
+ $e = new \OverflowException(
+ 'Response body size exceeds maximum of ' . $maximumSize . ' bytes',
+ \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 0
+ );
+ }
+
+ throw $e;
+ }
+ );
+
+ $deferred->pending = $promise;
+
+ return $promise;
+ }
+
+ /**
+ * @internal
+ * @param ResponseInterface $response
+ * @param RequestInterface $request
+ * @throws ResponseException
+ * @return ResponseInterface|PromiseInterface
+ */
+ public function onResponse(ResponseInterface $response, RequestInterface $request, $deferred)
+ {
+ $this->progress('response', array($response, $request));
+
+ // follow 3xx (Redirection) response status codes if Location header is present and not explicitly disabled
+ // @link https://tools.ietf.org/html/rfc7231#section-6.4
+ if ($this->followRedirects && ($response->getStatusCode() >= 300 && $response->getStatusCode() < 400) && $response->hasHeader('Location')) {
+ return $this->onResponseRedirect($response, $request, $deferred);
+ }
+
+ // only status codes 200-399 are considered to be valid, reject otherwise
+ if ($this->obeySuccessCode && ($response->getStatusCode() < 200 || $response->getStatusCode() >= 400)) {
+ throw new ResponseException($response);
+ }
+
+ // resolve our initial promise
+ return $response;
+ }
+
+ /**
+ * @param ResponseInterface $response
+ * @param RequestInterface $request
+ * @return PromiseInterface
+ * @throws \RuntimeException
+ */
+ private function onResponseRedirect(ResponseInterface $response, RequestInterface $request, Deferred $deferred)
+ {
+ // resolve location relative to last request URI
+ $location = Uri::resolve($request->getUri(), $response->getHeaderLine('Location'));
+
+ $request = $this->makeRedirectRequest($request, $location);
+ $this->progress('redirect', array($request));
+
+ if ($deferred->numRequests >= $this->maxRedirects) {
+ throw new \RuntimeException('Maximum number of redirects (' . $this->maxRedirects . ') exceeded');
+ }
+
+ return $this->next($request, $deferred);
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @param UriInterface $location
+ * @return RequestInterface
+ */
+ private function makeRedirectRequest(RequestInterface $request, UriInterface $location)
+ {
+ $originalHost = $request->getUri()->getHost();
+ $request = $request
+ ->withoutHeader('Host')
+ ->withoutHeader('Content-Type')
+ ->withoutHeader('Content-Length');
+
+ // Remove authorization if changing hostnames (but not if just changing ports or protocols).
+ if ($location->getHost() !== $originalHost) {
+ $request = $request->withoutHeader('Authorization');
+ }
+
+ // naïve approach..
+ $method = ($request->getMethod() === 'HEAD') ? 'HEAD' : 'GET';
+
+ return new Request($method, $location, $request->getHeaders());
+ }
+
+ private function progress($name, array $args = array())
+ {
+ return;
+
+ echo $name;
+
+ foreach ($args as $arg) {
+ echo ' ';
+ if ($arg instanceof ResponseInterface) {
+ echo 'HTTP/' . $arg->getProtocolVersion() . ' ' . $arg->getStatusCode() . ' ' . $arg->getReasonPhrase();
+ } elseif ($arg instanceof RequestInterface) {
+ echo $arg->getMethod() . ' ' . $arg->getRequestTarget() . ' HTTP/' . $arg->getProtocolVersion();
+ } else {
+ echo $arg;
+ }
+ }
+
+ echo PHP_EOL;
+ }
+}
diff --git a/vendor/react/http/src/Io/UploadedFile.php b/vendor/react/http/src/Io/UploadedFile.php
new file mode 100644
index 0000000..f2a6c9e
--- /dev/null
+++ b/vendor/react/http/src/Io/UploadedFile.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace React\Http\Io;
+
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use InvalidArgumentException;
+use RuntimeException;
+
+/**
+ * [Internal] Implementation of the PSR-7 `UploadedFileInterface`
+ *
+ * This is used internally to represent each incoming file upload.
+ *
+ * Note that this is an internal class only and nothing you should usually care
+ * about. See the `UploadedFileInterface` for more details.
+ *
+ * @see UploadedFileInterface
+ * @internal
+ */
+final class UploadedFile implements UploadedFileInterface
+{
+ /**
+ * @var StreamInterface
+ */
+ private $stream;
+
+ /**
+ * @var int
+ */
+ private $size;
+
+ /**
+ * @var int
+ */
+ private $error;
+
+ /**
+ * @var string
+ */
+ private $filename;
+
+ /**
+ * @var string
+ */
+ private $mediaType;
+
+ /**
+ * @param StreamInterface $stream
+ * @param int $size
+ * @param int $error
+ * @param string $filename
+ * @param string $mediaType
+ */
+ public function __construct(StreamInterface $stream, $size, $error, $filename, $mediaType)
+ {
+ $this->stream = $stream;
+ $this->size = $size;
+
+ if (!\is_int($error) || !\in_array($error, array(
+ \UPLOAD_ERR_OK,
+ \UPLOAD_ERR_INI_SIZE,
+ \UPLOAD_ERR_FORM_SIZE,
+ \UPLOAD_ERR_PARTIAL,
+ \UPLOAD_ERR_NO_FILE,
+ \UPLOAD_ERR_NO_TMP_DIR,
+ \UPLOAD_ERR_CANT_WRITE,
+ \UPLOAD_ERR_EXTENSION,
+ ))) {
+ throw new InvalidArgumentException(
+ 'Invalid error code, must be an UPLOAD_ERR_* constant'
+ );
+ }
+ $this->error = $error;
+ $this->filename = $filename;
+ $this->mediaType = $mediaType;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStream()
+ {
+ if ($this->error !== \UPLOAD_ERR_OK) {
+ throw new RuntimeException('Cannot retrieve stream due to upload error');
+ }
+
+ return $this->stream;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function moveTo($targetPath)
+ {
+ throw new RuntimeException('Not implemented');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ return $this->size;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getClientFilename()
+ {
+ return $this->filename;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getClientMediaType()
+ {
+ return $this->mediaType;
+ }
+}
diff --git a/vendor/react/http/src/Message/Response.php b/vendor/react/http/src/Message/Response.php
new file mode 100644
index 0000000..edd6245
--- /dev/null
+++ b/vendor/react/http/src/Message/Response.php
@@ -0,0 +1,291 @@
+<?php
+
+namespace React\Http\Message;
+
+use Fig\Http\Message\StatusCodeInterface;
+use Psr\Http\Message\StreamInterface;
+use React\Http\Io\BufferedBody;
+use React\Http\Io\HttpBodyStream;
+use React\Stream\ReadableStreamInterface;
+use RingCentral\Psr7\Response as Psr7Response;
+
+/**
+ * Represents an outgoing server response message.
+ *
+ * ```php
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * array(
+ * 'Content-Type' => 'text/html'
+ * ),
+ * "<html>Hello world!</html>\n"
+ * );
+ * ```
+ *
+ * This class implements the
+ * [PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface)
+ * which in turn extends the
+ * [PSR-7 `MessageInterface`](https://www.php-fig.org/psr/psr-7/#31-psrhttpmessagemessageinterface).
+ *
+ * On top of this, this class implements the
+ * [PSR-7 Message Util `StatusCodeInterface`](https://github.com/php-fig/http-message-util/blob/master/src/StatusCodeInterface.php)
+ * which means that most common HTTP status codes are available as class
+ * constants with the `STATUS_*` prefix. For instance, the `200 OK` and
+ * `404 Not Found` status codes can used as `Response::STATUS_OK` and
+ * `Response::STATUS_NOT_FOUND` respectively.
+ *
+ * > Internally, this implementation builds on top of an existing incoming
+ * response message and only adds required streaming support. This base class is
+ * considered an implementation detail that may change in the future.
+ *
+ * @see \Psr\Http\Message\ResponseInterface
+ */
+final class Response extends Psr7Response implements StatusCodeInterface
+{
+ /**
+ * Create an HTML response
+ *
+ * ```php
+ * $html = <<<HTML
+ * <!doctype html>
+ * <html>
+ * <body>Hello wörld!</body>
+ * </html>
+ *
+ * HTML;
+ *
+ * $response = React\Http\Message\Response::html($html);
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'text/html; charset=utf-8'
+ * ],
+ * $html
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given HTTP source
+ * string encoded in UTF-8 (Unicode). It's generally recommended to end the
+ * given plaintext string with a trailing newline.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::html(
+ * "<h1>Error</h1>\n<p>Invalid user name given.</p>\n"
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param string $html
+ * @return self
+ */
+ public static function html($html)
+ {
+ return new self(self::STATUS_OK, array('Content-Type' => 'text/html; charset=utf-8'), $html);
+ }
+
+ /**
+ * Create a JSON response
+ *
+ * ```php
+ * $response = React\Http\Message\Response::json(['name' => 'Alice']);
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'application/json'
+ * ],
+ * json_encode(
+ * ['name' => 'Alice'],
+ * JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION
+ * ) . "\n"
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given structured
+ * data encoded as a JSON text.
+ *
+ * The given structured data will be encoded as a JSON text. Any `string`
+ * values in the data must be encoded in UTF-8 (Unicode). If the encoding
+ * fails, this method will throw an `InvalidArgumentException`.
+ *
+ * By default, the given structured data will be encoded with the flags as
+ * shown above. This includes pretty printing (PHP 5.4+) and preserving
+ * zero fractions for `float` values (PHP 5.6.6+) to ease debugging. It is
+ * assumed any additional data overhead is usually compensated by using HTTP
+ * response compression.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::json(
+ * ['error' => 'Invalid user name given']
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param mixed $data
+ * @return self
+ * @throws \InvalidArgumentException when encoding fails
+ */
+ public static function json($data)
+ {
+ $json = @\json_encode(
+ $data,
+ (\defined('JSON_PRETTY_PRINT') ? \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE : 0) | (\defined('JSON_PRESERVE_ZERO_FRACTION') ? \JSON_PRESERVE_ZERO_FRACTION : 0)
+ );
+
+ // throw on error, now `false` but used to be `(string) "null"` before PHP 5.5
+ if ($json === false || (\PHP_VERSION_ID < 50500 && \json_last_error() !== \JSON_ERROR_NONE)) {
+ throw new \InvalidArgumentException(
+ 'Unable to encode given data as JSON' . (\function_exists('json_last_error_msg') ? ': ' . \json_last_error_msg() : ''),
+ \json_last_error()
+ );
+ }
+
+ return new self(self::STATUS_OK, array('Content-Type' => 'application/json'), $json . "\n");
+ }
+
+ /**
+ * Create a plaintext response
+ *
+ * ```php
+ * $response = React\Http\Message\Response::plaintext("Hello wörld!\n");
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'text/plain; charset=utf-8'
+ * ],
+ * "Hello wörld!\n"
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given plaintext
+ * string encoded in UTF-8 (Unicode). It's generally recommended to end the
+ * given plaintext string with a trailing newline.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::plaintext(
+ * "Error: Invalid user name given.\n"
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param string $text
+ * @return self
+ */
+ public static function plaintext($text)
+ {
+ return new self(self::STATUS_OK, array('Content-Type' => 'text/plain; charset=utf-8'), $text);
+ }
+
+ /**
+ * Create an XML response
+ *
+ * ```php
+ * $xml = <<<XML
+ * <?xml version="1.0" encoding="utf-8"?>
+ * <body>
+ * <greeting>Hello wörld!</greeting>
+ * </body>
+ *
+ * XML;
+ *
+ * $response = React\Http\Message\Response::xml($xml);
+ * ```
+ *
+ * This is a convenient shortcut method that returns the equivalent of this:
+ *
+ * ```
+ * $response = new React\Http\Message\Response(
+ * React\Http\Message\Response::STATUS_OK,
+ * [
+ * 'Content-Type' => 'application/xml'
+ * ],
+ * $xml
+ * );
+ * ```
+ *
+ * This method always returns a response with a `200 OK` status code and
+ * the appropriate `Content-Type` response header for the given XML source
+ * string. It's generally recommended to use UTF-8 (Unicode) and specify
+ * this as part of the leading XML declaration and to end the given XML
+ * source string with a trailing newline.
+ *
+ * If you want to use a different status code or custom HTTP response
+ * headers, you can manipulate the returned response object using the
+ * provided PSR-7 methods or directly instantiate a custom HTTP response
+ * object using the `Response` constructor:
+ *
+ * ```php
+ * $response = React\Http\Message\Response::xml(
+ * "<error><message>Invalid user name given.</message></error>\n"
+ * )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST);
+ * ```
+ *
+ * @param string $xml
+ * @return self
+ */
+ public static function xml($xml)
+ {
+ return new self(self::STATUS_OK, array('Content-Type' => 'application/xml'), $xml);
+ }
+
+ /**
+ * @param int $status HTTP status code (e.g. 200/404), see `self::STATUS_*` constants
+ * @param array<string,string|string[]> $headers additional response headers
+ * @param string|ReadableStreamInterface|StreamInterface $body response body
+ * @param string $version HTTP protocol version (e.g. 1.1/1.0)
+ * @param ?string $reason custom HTTP response phrase
+ * @throws \InvalidArgumentException for an invalid body
+ */
+ public function __construct(
+ $status = self::STATUS_OK,
+ array $headers = array(),
+ $body = '',
+ $version = '1.1',
+ $reason = null
+ ) {
+ if (\is_string($body)) {
+ $body = new BufferedBody($body);
+ } elseif ($body instanceof ReadableStreamInterface && !$body instanceof StreamInterface) {
+ $body = new HttpBodyStream($body, null);
+ } elseif (!$body instanceof StreamInterface) {
+ throw new \InvalidArgumentException('Invalid response body given');
+ }
+
+ parent::__construct(
+ $status,
+ $headers,
+ $body,
+ $version,
+ $reason
+ );
+ }
+}
diff --git a/vendor/react/http/src/Message/ResponseException.php b/vendor/react/http/src/Message/ResponseException.php
new file mode 100644
index 0000000..f4912c9
--- /dev/null
+++ b/vendor/react/http/src/Message/ResponseException.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace React\Http\Message;
+
+use RuntimeException;
+use Psr\Http\Message\ResponseInterface;
+
+/**
+ * The `React\Http\Message\ResponseException` is an `Exception` sub-class that will be used to reject
+ * a request promise if the remote server returns a non-success status code
+ * (anything but 2xx or 3xx).
+ * You can control this behavior via the [`withRejectErrorResponse()` method](#withrejecterrorresponse).
+ *
+ * The `getCode(): int` method can be used to
+ * return the HTTP response status code.
+ */
+final class ResponseException extends RuntimeException
+{
+ private $response;
+
+ public function __construct(ResponseInterface $response, $message = null, $code = null, $previous = null)
+ {
+ if ($message === null) {
+ $message = 'HTTP status code ' . $response->getStatusCode() . ' (' . $response->getReasonPhrase() . ')';
+ }
+ if ($code === null) {
+ $code = $response->getStatusCode();
+ }
+ parent::__construct($message, $code, $previous);
+
+ $this->response = $response;
+ }
+
+ /**
+ * Access its underlying response object.
+ *
+ * @return ResponseInterface
+ */
+ public function getResponse()
+ {
+ return $this->response;
+ }
+}
diff --git a/vendor/react/http/src/Message/ServerRequest.php b/vendor/react/http/src/Message/ServerRequest.php
new file mode 100644
index 0000000..f446f24
--- /dev/null
+++ b/vendor/react/http/src/Message/ServerRequest.php
@@ -0,0 +1,197 @@
+<?php
+
+namespace React\Http\Message;
+
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UriInterface;
+use React\Http\Io\BufferedBody;
+use React\Http\Io\HttpBodyStream;
+use React\Stream\ReadableStreamInterface;
+use RingCentral\Psr7\Request;
+
+/**
+ * Respresents an incoming server request message.
+ *
+ * This class implements the
+ * [PSR-7 `ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface)
+ * which extends the
+ * [PSR-7 `RequestInterface`](https://www.php-fig.org/psr/psr-7/#32-psrhttpmessagerequestinterface)
+ * which in turn extends the
+ * [PSR-7 `MessageInterface`](https://www.php-fig.org/psr/psr-7/#31-psrhttpmessagemessageinterface).
+ *
+ * This is mostly used internally to represent each incoming request message.
+ * Likewise, you can also use this class in test cases to test how your web
+ * application reacts to certain HTTP requests.
+ *
+ * > Internally, this implementation builds on top of an existing outgoing
+ * request message and only adds required server methods. This base class is
+ * considered an implementation detail that may change in the future.
+ *
+ * @see ServerRequestInterface
+ */
+final class ServerRequest extends Request implements ServerRequestInterface
+{
+ private $attributes = array();
+
+ private $serverParams;
+ private $fileParams = array();
+ private $cookies = array();
+ private $queryParams = array();
+ private $parsedBody;
+
+ /**
+ * @param string $method HTTP method for the request.
+ * @param string|UriInterface $url URL for the request.
+ * @param array<string,string|string[]> $headers Headers for the message.
+ * @param string|ReadableStreamInterface|StreamInterface $body Message body.
+ * @param string $version HTTP protocol version.
+ * @param array<string,string> $serverParams server-side parameters
+ * @throws \InvalidArgumentException for an invalid URL or body
+ */
+ public function __construct(
+ $method,
+ $url,
+ array $headers = array(),
+ $body = '',
+ $version = '1.1',
+ $serverParams = array()
+ ) {
+ $stream = null;
+ if (\is_string($body)) {
+ $body = new BufferedBody($body);
+ } elseif ($body instanceof ReadableStreamInterface && !$body instanceof StreamInterface) {
+ $stream = $body;
+ $body = null;
+ } elseif (!$body instanceof StreamInterface) {
+ throw new \InvalidArgumentException('Invalid server request body given');
+ }
+
+ $this->serverParams = $serverParams;
+ parent::__construct($method, $url, $headers, $body, $version);
+
+ if ($stream !== null) {
+ $size = (int) $this->getHeaderLine('Content-Length');
+ if (\strtolower($this->getHeaderLine('Transfer-Encoding')) === 'chunked') {
+ $size = null;
+ }
+ $this->stream = new HttpBodyStream($stream, $size);
+ }
+
+ $query = $this->getUri()->getQuery();
+ if ($query !== '') {
+ \parse_str($query, $this->queryParams);
+ }
+
+ // Multiple cookie headers are not allowed according
+ // to https://tools.ietf.org/html/rfc6265#section-5.4
+ $cookieHeaders = $this->getHeader("Cookie");
+
+ if (count($cookieHeaders) === 1) {
+ $this->cookies = $this->parseCookie($cookieHeaders[0]);
+ }
+ }
+
+ public function getServerParams()
+ {
+ return $this->serverParams;
+ }
+
+ public function getCookieParams()
+ {
+ return $this->cookies;
+ }
+
+ public function withCookieParams(array $cookies)
+ {
+ $new = clone $this;
+ $new->cookies = $cookies;
+ return $new;
+ }
+
+ public function getQueryParams()
+ {
+ return $this->queryParams;
+ }
+
+ public function withQueryParams(array $query)
+ {
+ $new = clone $this;
+ $new->queryParams = $query;
+ return $new;
+ }
+
+ public function getUploadedFiles()
+ {
+ return $this->fileParams;
+ }
+
+ public function withUploadedFiles(array $uploadedFiles)
+ {
+ $new = clone $this;
+ $new->fileParams = $uploadedFiles;
+ return $new;
+ }
+
+ public function getParsedBody()
+ {
+ return $this->parsedBody;
+ }
+
+ public function withParsedBody($data)
+ {
+ $new = clone $this;
+ $new->parsedBody = $data;
+ return $new;
+ }
+
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ public function getAttribute($name, $default = null)
+ {
+ if (!\array_key_exists($name, $this->attributes)) {
+ return $default;
+ }
+ return $this->attributes[$name];
+ }
+
+ public function withAttribute($name, $value)
+ {
+ $new = clone $this;
+ $new->attributes[$name] = $value;
+ return $new;
+ }
+
+ public function withoutAttribute($name)
+ {
+ $new = clone $this;
+ unset($new->attributes[$name]);
+ return $new;
+ }
+
+ /**
+ * @param string $cookie
+ * @return array
+ */
+ private function parseCookie($cookie)
+ {
+ $cookieArray = \explode(';', $cookie);
+ $result = array();
+
+ foreach ($cookieArray as $pair) {
+ $pair = \trim($pair);
+ $nameValuePair = \explode('=', $pair, 2);
+
+ if (\count($nameValuePair) === 2) {
+ $key = \urldecode($nameValuePair[0]);
+ $value = \urldecode($nameValuePair[1]);
+ $result[$key] = $value;
+ }
+ }
+
+ return $result;
+ }
+}
diff --git a/vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php b/vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php
new file mode 100644
index 0000000..5333810
--- /dev/null
+++ b/vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php
@@ -0,0 +1,211 @@
+<?php
+
+namespace React\Http\Middleware;
+
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use React\Http\Io\HttpBodyStream;
+use React\Http\Io\PauseBufferStream;
+use React\Promise;
+use React\Promise\PromiseInterface;
+use React\Promise\Deferred;
+use React\Stream\ReadableStreamInterface;
+
+/**
+ * Limits how many next handlers can be executed concurrently.
+ *
+ * If this middleware is invoked, it will check if the number of pending
+ * handlers is below the allowed limit and then simply invoke the next handler
+ * and it will return whatever the next handler returns (or throws).
+ *
+ * If the number of pending handlers exceeds the allowed limit, the request will
+ * be queued (and its streaming body will be paused) and it will return a pending
+ * promise.
+ * Once a pending handler returns (or throws), it will pick the oldest request
+ * from this queue and invokes the next handler (and its streaming body will be
+ * resumed).
+ *
+ * The following example shows how this middleware can be used to ensure no more
+ * than 10 handlers will be invoked at once:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(
+ * new React\Http\Middleware\StreamingRequestMiddleware(),
+ * new React\Http\Middleware\LimitConcurrentRequestsMiddleware(10),
+ * $handler
+ * );
+ * ```
+ *
+ * Similarly, this middleware is often used in combination with the
+ * [`RequestBodyBufferMiddleware`](#requestbodybuffermiddleware) (see below)
+ * to limit the total number of requests that can be buffered at once:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(
+ * new React\Http\Middleware\StreamingRequestMiddleware(),
+ * new React\Http\Middleware\LimitConcurrentRequestsMiddleware(100), // 100 concurrent buffering handlers
+ * new React\Http\Middleware\RequestBodyBufferMiddleware(2 * 1024 * 1024), // 2 MiB per request
+ * new React\Http\Middleware\RequestBodyParserMiddleware(),
+ * $handler
+ * );
+ * ```
+ *
+ * More sophisticated examples include limiting the total number of requests
+ * that can be buffered at once and then ensure the actual request handler only
+ * processes one request after another without any concurrency:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(
+ * new React\Http\Middleware\StreamingRequestMiddleware(),
+ * new React\Http\Middleware\LimitConcurrentRequestsMiddleware(100), // 100 concurrent buffering handlers
+ * new React\Http\Middleware\RequestBodyBufferMiddleware(2 * 1024 * 1024), // 2 MiB per request
+ * new React\Http\Middleware\RequestBodyParserMiddleware(),
+ * new React\Http\Middleware\LimitConcurrentRequestsMiddleware(1), // only execute 1 handler (no concurrency)
+ * $handler
+ * );
+ * ```
+ *
+ * @see RequestBodyBufferMiddleware
+ */
+final class LimitConcurrentRequestsMiddleware
+{
+ private $limit;
+ private $pending = 0;
+ private $queue = array();
+
+ /**
+ * @param int $limit Maximum amount of concurrent requests handled.
+ *
+ * For example when $limit is set to 10, 10 requests will flow to $next
+ * while more incoming requests have to wait until one is done.
+ */
+ public function __construct($limit)
+ {
+ $this->limit = $limit;
+ }
+
+ public function __invoke(ServerRequestInterface $request, $next)
+ {
+ // happy path: simply invoke next request handler if we're below limit
+ if ($this->pending < $this->limit) {
+ ++$this->pending;
+
+ try {
+ $response = $next($request);
+ } catch (\Exception $e) {
+ $this->processQueue();
+ throw $e;
+ } catch (\Throwable $e) { // @codeCoverageIgnoreStart
+ // handle Errors just like Exceptions (PHP 7+ only)
+ $this->processQueue();
+ throw $e; // @codeCoverageIgnoreEnd
+ }
+
+ // happy path: if next request handler returned immediately,
+ // we can simply try to invoke the next queued request
+ if ($response instanceof ResponseInterface) {
+ $this->processQueue();
+ return $response;
+ }
+
+ // if the next handler returns a pending promise, we have to
+ // await its resolution before invoking next queued request
+ return $this->await(Promise\resolve($response));
+ }
+
+ // if we reach this point, then this request will need to be queued
+ // check if the body is streaming, in which case we need to buffer everything
+ $body = $request->getBody();
+ if ($body instanceof ReadableStreamInterface) {
+ // pause actual body to stop emitting data until the handler is called
+ $size = $body->getSize();
+ $body = new PauseBufferStream($body);
+ $body->pauseImplicit();
+
+ // replace with buffering body to ensure any readable events will be buffered
+ $request = $request->withBody(new HttpBodyStream(
+ $body,
+ $size
+ ));
+ }
+
+ // get next queue position
+ $queue =& $this->queue;
+ $queue[] = null;
+ \end($queue);
+ $id = \key($queue);
+
+ $deferred = new Deferred(function ($_, $reject) use (&$queue, $id) {
+ // queued promise cancelled before its next handler is invoked
+ // remove from queue and reject explicitly
+ unset($queue[$id]);
+ $reject(new \RuntimeException('Cancelled queued next handler'));
+ });
+
+ // queue request and process queue if pending does not exceed limit
+ $queue[$id] = $deferred;
+
+ $pending = &$this->pending;
+ $that = $this;
+ return $deferred->promise()->then(function () use ($request, $next, $body, &$pending, $that) {
+ // invoke next request handler
+ ++$pending;
+
+ try {
+ $response = $next($request);
+ } catch (\Exception $e) {
+ $that->processQueue();
+ throw $e;
+ } catch (\Throwable $e) { // @codeCoverageIgnoreStart
+ // handle Errors just like Exceptions (PHP 7+ only)
+ $that->processQueue();
+ throw $e; // @codeCoverageIgnoreEnd
+ }
+
+ // resume readable stream and replay buffered events
+ if ($body instanceof PauseBufferStream) {
+ $body->resumeImplicit();
+ }
+
+ // if the next handler returns a pending promise, we have to
+ // await its resolution before invoking next queued request
+ return $that->await(Promise\resolve($response));
+ });
+ }
+
+ /**
+ * @internal
+ * @param PromiseInterface $promise
+ * @return PromiseInterface
+ */
+ public function await(PromiseInterface $promise)
+ {
+ $that = $this;
+
+ return $promise->then(function ($response) use ($that) {
+ $that->processQueue();
+
+ return $response;
+ }, function ($error) use ($that) {
+ $that->processQueue();
+
+ return Promise\reject($error);
+ });
+ }
+
+ /**
+ * @internal
+ */
+ public function processQueue()
+ {
+ // skip if we're still above concurrency limit or there's no queued request waiting
+ if (--$this->pending >= $this->limit || !$this->queue) {
+ return;
+ }
+
+ $first = \reset($this->queue);
+ unset($this->queue[key($this->queue)]);
+
+ $first->resolve();
+ }
+}
diff --git a/vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php b/vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php
new file mode 100644
index 0000000..c13a5de
--- /dev/null
+++ b/vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace React\Http\Middleware;
+
+use OverflowException;
+use Psr\Http\Message\ServerRequestInterface;
+use React\Http\Io\BufferedBody;
+use React\Http\Io\IniUtil;
+use React\Promise\Stream;
+use React\Stream\ReadableStreamInterface;
+
+final class RequestBodyBufferMiddleware
+{
+ private $sizeLimit;
+
+ /**
+ * @param int|string|null $sizeLimit Either an int with the max request body size
+ * in bytes or an ini like size string
+ * or null to use post_max_size from PHP's
+ * configuration. (Note that the value from
+ * the CLI configuration will be used.)
+ */
+ public function __construct($sizeLimit = null)
+ {
+ if ($sizeLimit === null) {
+ $sizeLimit = \ini_get('post_max_size');
+ }
+
+ $this->sizeLimit = IniUtil::iniSizeToBytes($sizeLimit);
+ }
+
+ public function __invoke(ServerRequestInterface $request, $stack)
+ {
+ $body = $request->getBody();
+ $size = $body->getSize();
+
+ // happy path: skip if body is known to be empty (or is already buffered)
+ if ($size === 0 || !$body instanceof ReadableStreamInterface) {
+ // replace with empty body if body is streaming (or buffered size exceeds limit)
+ if ($body instanceof ReadableStreamInterface || $size > $this->sizeLimit) {
+ $request = $request->withBody(new BufferedBody(''));
+ }
+
+ return $stack($request);
+ }
+
+ // request body of known size exceeding limit
+ $sizeLimit = $this->sizeLimit;
+ if ($size > $this->sizeLimit) {
+ $sizeLimit = 0;
+ }
+
+ return Stream\buffer($body, $sizeLimit)->then(function ($buffer) use ($request, $stack) {
+ $request = $request->withBody(new BufferedBody($buffer));
+
+ return $stack($request);
+ }, function ($error) use ($stack, $request, $body) {
+ // On buffer overflow keep the request body stream in,
+ // but ignore the contents and wait for the close event
+ // before passing the request on to the next middleware.
+ if ($error instanceof OverflowException) {
+ return Stream\first($body, 'close')->then(function () use ($stack, $request) {
+ return $stack($request);
+ });
+ }
+
+ throw $error;
+ });
+ }
+}
diff --git a/vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php b/vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php
new file mode 100644
index 0000000..be5ba16
--- /dev/null
+++ b/vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace React\Http\Middleware;
+
+use Psr\Http\Message\ServerRequestInterface;
+use React\Http\Io\MultipartParser;
+
+final class RequestBodyParserMiddleware
+{
+ private $multipart;
+
+ /**
+ * @param int|string|null $uploadMaxFilesize
+ * @param int|null $maxFileUploads
+ */
+ public function __construct($uploadMaxFilesize = null, $maxFileUploads = null)
+ {
+ $this->multipart = new MultipartParser($uploadMaxFilesize, $maxFileUploads);
+ }
+
+ public function __invoke(ServerRequestInterface $request, $next)
+ {
+ $type = \strtolower($request->getHeaderLine('Content-Type'));
+ list ($type) = \explode(';', $type);
+
+ if ($type === 'application/x-www-form-urlencoded') {
+ return $next($this->parseFormUrlencoded($request));
+ }
+
+ if ($type === 'multipart/form-data') {
+ return $next($this->multipart->parse($request));
+ }
+
+ return $next($request);
+ }
+
+ private function parseFormUrlencoded(ServerRequestInterface $request)
+ {
+ // parse string into array structure
+ // ignore warnings due to excessive data structures (max_input_vars and max_input_nesting_level)
+ $ret = array();
+ @\parse_str((string)$request->getBody(), $ret);
+
+ return $request->withParsedBody($ret);
+ }
+}
diff --git a/vendor/react/http/src/Middleware/StreamingRequestMiddleware.php b/vendor/react/http/src/Middleware/StreamingRequestMiddleware.php
new file mode 100644
index 0000000..6ab74b7
--- /dev/null
+++ b/vendor/react/http/src/Middleware/StreamingRequestMiddleware.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace React\Http\Middleware;
+
+use Psr\Http\Message\ServerRequestInterface;
+
+/**
+ * Process incoming requests with a streaming request body (without buffering).
+ *
+ * This allows you to process requests of any size without buffering the request
+ * body in memory. Instead, it will represent the request body as a
+ * [`ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface)
+ * that emit chunks of incoming data as it is received:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(
+ * new React\Http\Middleware\StreamingRequestMiddleware(),
+ * function (Psr\Http\Message\ServerRequestInterface $request) {
+ * $body = $request->getBody();
+ * assert($body instanceof Psr\Http\Message\StreamInterface);
+ * assert($body instanceof React\Stream\ReadableStreamInterface);
+ *
+ * return new React\Promise\Promise(function ($resolve) use ($body) {
+ * $bytes = 0;
+ * $body->on('data', function ($chunk) use (&$bytes) {
+ * $bytes += \count($chunk);
+ * });
+ * $body->on('close', function () use (&$bytes, $resolve) {
+ * $resolve(new React\Http\Response(
+ * 200,
+ * [],
+ * "Received $bytes bytes\n"
+ * ));
+ * });
+ * });
+ * }
+ * );
+ * ```
+ *
+ * See also [streaming incoming request](../../README.md#streaming-incoming-request)
+ * for more details.
+ *
+ * Additionally, this middleware can be used in combination with the
+ * [`LimitConcurrentRequestsMiddleware`](#limitconcurrentrequestsmiddleware) and
+ * [`RequestBodyBufferMiddleware`](#requestbodybuffermiddleware) (see below)
+ * to explicitly configure the total number of requests that can be handled at
+ * once:
+ *
+ * ```php
+ * $http = new React\Http\HttpServer(
+ * new React\Http\Middleware\StreamingRequestMiddleware(),
+ * new React\Http\Middleware\LimitConcurrentRequestsMiddleware(100), // 100 concurrent buffering handlers
+ * new React\Http\Middleware\RequestBodyBufferMiddleware(2 * 1024 * 1024), // 2 MiB per request
+ * new React\Http\Middleware\RequestBodyParserMiddleware(),
+ * $handler
+ * );
+ * ```
+ *
+ * > Internally, this class is used as a "marker" to not trigger the default
+ * request buffering behavior in the `HttpServer`. It does not implement any logic
+ * on its own.
+ */
+final class StreamingRequestMiddleware
+{
+ public function __invoke(ServerRequestInterface $request, $next)
+ {
+ return $next($request);
+ }
+}
diff --git a/vendor/react/http/src/Server.php b/vendor/react/http/src/Server.php
new file mode 100644
index 0000000..9bb9cf7
--- /dev/null
+++ b/vendor/react/http/src/Server.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace React\Http;
+
+// Deprecated `Server` is an alias for new `HttpServer` to ensure existing code continues to work as-is.
+\class_alias(__NAMESPACE__ . '\\HttpServer', __NAMESPACE__ . '\\Server', true);
+
+// Aid static analysis and IDE autocompletion about this deprecation,
+// but don't actually execute during runtime because `HttpServer` is final.
+if (!\class_exists(__NAMESPACE__ . '\\Server', false)) {
+ /**
+ * @deprecated 1.5.0 See HttpServer instead
+ * @see HttpServer
+ */
+ final class Server extends HttpServer
+ {
+ }
+}