From d405d58f8d3741e3a669f8ab9178750ff0163a20 Mon Sep 17 00:00:00 2001 From: panariga Date: Wed, 27 Nov 2024 21:41:18 +0200 Subject: [PATCH] + --- vendor/clue/framework-x/.github/FUNDING.yml | 2 - vendor/clue/framework-x/CHANGELOG.md | 373 -- vendor/clue/framework-x/LICENSE | 21 - vendor/clue/framework-x/README.md | 147 - vendor/clue/framework-x/composer.json | 40 - .../clue/framework-x/src/AccessLogHandler.php | 140 - vendor/clue/framework-x/src/App.php | 354 -- vendor/clue/framework-x/src/Container.php | 382 --- vendor/clue/framework-x/src/ErrorHandler.php | 211 -- .../framework-x/src/FilesystemHandler.php | 130 - .../clue/framework-x/src/Io/FiberHandler.php | 67 - .../clue/framework-x/src/Io/HtmlHandler.php | 65 - .../framework-x/src/Io/LogStreamHandler.php | 95 - .../framework-x/src/Io/MiddlewareHandler.php | 40 - .../framework-x/src/Io/ReactiveHandler.php | 91 - .../framework-x/src/Io/RedirectHandler.php | 48 - .../clue/framework-x/src/Io/RouteHandler.php | 116 - .../clue/framework-x/src/Io/SapiHandler.php | 164 - vendor/fig/http-message-util/.gitignore | 1 - vendor/fig/http-message-util/CHANGELOG.md | 147 - vendor/fig/http-message-util/LICENSE | 19 - vendor/fig/http-message-util/README.md | 17 - vendor/fig/http-message-util/composer.json | 28 - .../src/RequestMethodInterface.php | 34 - .../src/StatusCodeInterface.php | 107 - vendor/nikic/fast-route/.gitignore | 5 - vendor/nikic/fast-route/.hhconfig | 1 - vendor/nikic/fast-route/.travis.yml | 20 - vendor/nikic/fast-route/FastRoute.hhi | 126 - vendor/nikic/fast-route/LICENSE | 31 - vendor/nikic/fast-route/README.md | 313 -- vendor/nikic/fast-route/composer.json | 24 - vendor/nikic/fast-route/phpunit.xml | 24 - vendor/nikic/fast-route/psalm.xml | 28 - .../fast-route/src/BadRouteException.php | 7 - vendor/nikic/fast-route/src/DataGenerator.php | 26 - .../src/DataGenerator/CharCountBased.php | 31 - .../src/DataGenerator/GroupCountBased.php | 30 - .../src/DataGenerator/GroupPosBased.php | 27 - .../src/DataGenerator/MarkBased.php | 27 - .../src/DataGenerator/RegexBasedAbstract.php | 186 - vendor/nikic/fast-route/src/Dispatcher.php | 26 - .../src/Dispatcher/CharCountBased.php | 31 - .../src/Dispatcher/GroupCountBased.php | 31 - .../src/Dispatcher/GroupPosBased.php | 33 - .../fast-route/src/Dispatcher/MarkBased.php | 31 - .../src/Dispatcher/RegexBasedAbstract.php | 88 - vendor/nikic/fast-route/src/Route.php | 47 - .../nikic/fast-route/src/RouteCollector.php | 152 - vendor/nikic/fast-route/src/RouteParser.php | 37 - .../nikic/fast-route/src/RouteParser/Std.php | 87 - vendor/nikic/fast-route/src/bootstrap.php | 12 - vendor/nikic/fast-route/src/functions.php | 74 - .../test/Dispatcher/CharCountBasedTest.php | 16 - .../test/Dispatcher/DispatcherTest.php | 581 ---- .../test/Dispatcher/GroupCountBasedTest.php | 16 - .../test/Dispatcher/GroupPosBasedTest.php | 16 - .../test/Dispatcher/MarkBasedTest.php | 24 - .../HackTypechecker/HackTypecheckerTest.php | 44 - .../HackTypechecker/fixtures/all_options.php | 29 - .../fixtures/empty_options.php | 11 - .../HackTypechecker/fixtures/no_options.php | 11 - .../fast-route/test/RouteCollectorTest.php | 108 - .../fast-route/test/RouteParser/StdTest.php | 154 - vendor/nikic/fast-route/test/bootstrap.php | 11 - vendor/psr/http-message/CHANGELOG.md | 36 - vendor/psr/http-message/LICENSE | 19 - vendor/psr/http-message/README.md | 16 - vendor/psr/http-message/composer.json | 26 - .../psr/http-message/docs/PSR7-Interfaces.md | 130 - vendor/psr/http-message/docs/PSR7-Usage.md | 159 - .../psr/http-message/src/MessageInterface.php | 189 -- .../psr/http-message/src/RequestInterface.php | 131 - .../http-message/src/ResponseInterface.php | 70 - .../src/ServerRequestInterface.php | 263 -- .../psr/http-message/src/StreamInterface.php | 160 - .../src/UploadedFileInterface.php | 125 - vendor/psr/http-message/src/UriInterface.php | 326 -- vendor/react/async/CHANGELOG.md | 112 - vendor/react/async/LICENSE | 19 - vendor/react/async/README.md | 672 ---- vendor/react/async/composer.json | 50 - vendor/react/async/src/FiberFactory.php | 33 - vendor/react/async/src/FiberInterface.php | 23 - vendor/react/async/src/FiberMap.php | 42 - vendor/react/async/src/SimpleFiber.php | 79 - vendor/react/async/src/functions.php | 846 ----- vendor/react/async/src/functions_include.php | 9 - vendor/react/cache/CHANGELOG.md | 96 - vendor/react/cache/LICENSE | 21 - vendor/react/cache/README.md | 367 -- vendor/react/cache/composer.json | 45 - vendor/react/cache/src/ArrayCache.php | 181 - vendor/react/cache/src/CacheInterface.php | 194 -- vendor/react/dns/CHANGELOG.md | 452 --- vendor/react/dns/LICENSE | 21 - vendor/react/dns/README.md | 453 --- vendor/react/dns/composer.json | 49 - vendor/react/dns/src/BadServerException.php | 7 - vendor/react/dns/src/Config/Config.php | 137 - vendor/react/dns/src/Config/HostsFile.php | 153 - vendor/react/dns/src/Model/Message.php | 230 -- vendor/react/dns/src/Model/Record.php | 153 - .../react/dns/src/Protocol/BinaryDumper.php | 199 -- vendor/react/dns/src/Protocol/Parser.php | 356 -- .../react/dns/src/Query/CachingExecutor.php | 88 - .../dns/src/Query/CancellationException.php | 7 - vendor/react/dns/src/Query/CoopExecutor.php | 91 - .../react/dns/src/Query/ExecutorInterface.php | 43 - .../react/dns/src/Query/FallbackExecutor.php | 49 - .../react/dns/src/Query/HostsFileExecutor.php | 89 - vendor/react/dns/src/Query/Query.php | 69 - vendor/react/dns/src/Query/RetryExecutor.php | 85 - .../src/Query/SelectiveTransportExecutor.php | 85 - .../dns/src/Query/TcpTransportExecutor.php | 382 --- .../react/dns/src/Query/TimeoutException.php | 7 - .../react/dns/src/Query/TimeoutExecutor.php | 78 - .../dns/src/Query/UdpTransportExecutor.php | 221 -- .../react/dns/src/RecordNotFoundException.php | 7 - vendor/react/dns/src/Resolver/Factory.php | 226 -- vendor/react/dns/src/Resolver/Resolver.php | 147 - .../dns/src/Resolver/ResolverInterface.php | 94 - vendor/react/http/CHANGELOG.md | 920 ----- vendor/react/http/LICENSE | 21 - vendor/react/http/README.md | 3024 ----------------- vendor/react/http/composer.json | 57 - vendor/react/http/src/Browser.php | 858 ----- vendor/react/http/src/Client/Client.php | 27 - vendor/react/http/src/HttpServer.php | 351 -- vendor/react/http/src/Io/AbstractMessage.php | 172 - vendor/react/http/src/Io/AbstractRequest.php | 156 - vendor/react/http/src/Io/BufferedBody.php | 179 - vendor/react/http/src/Io/ChunkedDecoder.php | 175 - vendor/react/http/src/Io/ChunkedEncoder.php | 92 - .../http/src/Io/ClientConnectionManager.php | 137 - .../react/http/src/Io/ClientRequestState.php | 16 - .../react/http/src/Io/ClientRequestStream.php | 307 -- vendor/react/http/src/Io/Clock.php | 54 - .../http/src/Io/CloseProtectionStream.php | 111 - vendor/react/http/src/Io/EmptyBodyStream.php | 142 - vendor/react/http/src/Io/HttpBodyStream.php | 182 - vendor/react/http/src/Io/IniUtil.php | 48 - .../react/http/src/Io/LengthLimitedStream.php | 108 - vendor/react/http/src/Io/MiddlewareRunner.php | 61 - vendor/react/http/src/Io/MultipartParser.php | 345 -- .../react/http/src/Io/PauseBufferStream.php | 188 - .../react/http/src/Io/ReadableBodyStream.php | 153 - .../react/http/src/Io/RequestHeaderParser.php | 179 - vendor/react/http/src/Io/Sender.php | 152 - vendor/react/http/src/Io/StreamingServer.php | 405 --- vendor/react/http/src/Io/Transaction.php | 330 -- vendor/react/http/src/Io/UploadedFile.php | 130 - vendor/react/http/src/Message/Request.php | 57 - vendor/react/http/src/Message/Response.php | 414 --- .../http/src/Message/ResponseException.php | 43 - .../react/http/src/Message/ServerRequest.php | 331 -- vendor/react/http/src/Message/Uri.php | 356 -- .../LimitConcurrentRequestsMiddleware.php | 211 -- .../RequestBodyBufferMiddleware.php | 109 - .../RequestBodyParserMiddleware.php | 46 - .../Middleware/StreamingRequestMiddleware.php | 69 - vendor/react/http/src/Server.php | 18 - vendor/react/socket/CHANGELOG.md | 785 ----- vendor/react/socket/LICENSE | 21 - vendor/react/socket/README.md | 1564 --------- vendor/react/socket/composer.json | 52 - vendor/react/socket/src/Connection.php | 183 - .../react/socket/src/ConnectionInterface.php | 119 - vendor/react/socket/src/Connector.php | 236 -- .../react/socket/src/ConnectorInterface.php | 59 - vendor/react/socket/src/DnsConnector.php | 117 - vendor/react/socket/src/FdServer.php | 222 -- vendor/react/socket/src/FixedUriConnector.php | 41 - .../src/HappyEyeBallsConnectionBuilder.php | 334 -- .../socket/src/HappyEyeBallsConnector.php | 80 - vendor/react/socket/src/LimitingServer.php | 203 -- vendor/react/socket/src/SecureConnector.php | 132 - vendor/react/socket/src/SecureServer.php | 210 -- vendor/react/socket/src/Server.php | 118 - vendor/react/socket/src/ServerInterface.php | 151 - vendor/react/socket/src/SocketServer.php | 215 -- vendor/react/socket/src/StreamEncryption.php | 158 - vendor/react/socket/src/TcpConnector.php | 173 - vendor/react/socket/src/TcpServer.php | 262 -- vendor/react/socket/src/TimeoutConnector.php | 79 - vendor/react/socket/src/UnixConnector.php | 58 - vendor/react/socket/src/UnixServer.php | 162 - 187 files changed, 30300 deletions(-) delete mode 100644 vendor/clue/framework-x/.github/FUNDING.yml delete mode 100644 vendor/clue/framework-x/CHANGELOG.md delete mode 100644 vendor/clue/framework-x/LICENSE delete mode 100644 vendor/clue/framework-x/README.md delete mode 100644 vendor/clue/framework-x/composer.json delete mode 100644 vendor/clue/framework-x/src/AccessLogHandler.php delete mode 100644 vendor/clue/framework-x/src/App.php delete mode 100644 vendor/clue/framework-x/src/Container.php delete mode 100644 vendor/clue/framework-x/src/ErrorHandler.php delete mode 100644 vendor/clue/framework-x/src/FilesystemHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/FiberHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/HtmlHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/LogStreamHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/MiddlewareHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/ReactiveHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/RedirectHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/RouteHandler.php delete mode 100644 vendor/clue/framework-x/src/Io/SapiHandler.php delete mode 100644 vendor/fig/http-message-util/.gitignore delete mode 100644 vendor/fig/http-message-util/CHANGELOG.md delete mode 100644 vendor/fig/http-message-util/LICENSE delete mode 100644 vendor/fig/http-message-util/README.md delete mode 100644 vendor/fig/http-message-util/composer.json delete mode 100644 vendor/fig/http-message-util/src/RequestMethodInterface.php delete mode 100644 vendor/fig/http-message-util/src/StatusCodeInterface.php delete mode 100644 vendor/nikic/fast-route/.gitignore delete mode 100644 vendor/nikic/fast-route/.hhconfig delete mode 100644 vendor/nikic/fast-route/.travis.yml delete mode 100644 vendor/nikic/fast-route/FastRoute.hhi delete mode 100644 vendor/nikic/fast-route/LICENSE delete mode 100644 vendor/nikic/fast-route/README.md delete mode 100644 vendor/nikic/fast-route/composer.json delete mode 100644 vendor/nikic/fast-route/phpunit.xml delete mode 100644 vendor/nikic/fast-route/psalm.xml delete mode 100644 vendor/nikic/fast-route/src/BadRouteException.php delete mode 100644 vendor/nikic/fast-route/src/DataGenerator.php delete mode 100644 vendor/nikic/fast-route/src/DataGenerator/CharCountBased.php delete mode 100644 vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php delete mode 100644 vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php delete mode 100644 vendor/nikic/fast-route/src/DataGenerator/MarkBased.php delete mode 100644 vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php delete mode 100644 vendor/nikic/fast-route/src/Dispatcher.php delete mode 100644 vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php delete mode 100644 vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php delete mode 100644 vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php delete mode 100644 vendor/nikic/fast-route/src/Dispatcher/MarkBased.php delete mode 100644 vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php delete mode 100644 vendor/nikic/fast-route/src/Route.php delete mode 100644 vendor/nikic/fast-route/src/RouteCollector.php delete mode 100644 vendor/nikic/fast-route/src/RouteParser.php delete mode 100644 vendor/nikic/fast-route/src/RouteParser/Std.php delete mode 100644 vendor/nikic/fast-route/src/bootstrap.php delete mode 100644 vendor/nikic/fast-route/src/functions.php delete mode 100644 vendor/nikic/fast-route/test/Dispatcher/CharCountBasedTest.php delete mode 100644 vendor/nikic/fast-route/test/Dispatcher/DispatcherTest.php delete mode 100644 vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php delete mode 100644 vendor/nikic/fast-route/test/Dispatcher/GroupPosBasedTest.php delete mode 100644 vendor/nikic/fast-route/test/Dispatcher/MarkBasedTest.php delete mode 100644 vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php delete mode 100644 vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php delete mode 100644 vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php delete mode 100644 vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php delete mode 100644 vendor/nikic/fast-route/test/RouteCollectorTest.php delete mode 100644 vendor/nikic/fast-route/test/RouteParser/StdTest.php delete mode 100644 vendor/nikic/fast-route/test/bootstrap.php delete mode 100644 vendor/psr/http-message/CHANGELOG.md delete mode 100644 vendor/psr/http-message/LICENSE delete mode 100644 vendor/psr/http-message/README.md delete mode 100644 vendor/psr/http-message/composer.json delete mode 100644 vendor/psr/http-message/docs/PSR7-Interfaces.md delete mode 100644 vendor/psr/http-message/docs/PSR7-Usage.md delete mode 100644 vendor/psr/http-message/src/MessageInterface.php delete mode 100644 vendor/psr/http-message/src/RequestInterface.php delete mode 100644 vendor/psr/http-message/src/ResponseInterface.php delete mode 100644 vendor/psr/http-message/src/ServerRequestInterface.php delete mode 100644 vendor/psr/http-message/src/StreamInterface.php delete mode 100644 vendor/psr/http-message/src/UploadedFileInterface.php delete mode 100644 vendor/psr/http-message/src/UriInterface.php delete mode 100644 vendor/react/async/CHANGELOG.md delete mode 100644 vendor/react/async/LICENSE delete mode 100644 vendor/react/async/README.md delete mode 100644 vendor/react/async/composer.json delete mode 100644 vendor/react/async/src/FiberFactory.php delete mode 100644 vendor/react/async/src/FiberInterface.php delete mode 100644 vendor/react/async/src/FiberMap.php delete mode 100644 vendor/react/async/src/SimpleFiber.php delete mode 100644 vendor/react/async/src/functions.php delete mode 100644 vendor/react/async/src/functions_include.php delete mode 100644 vendor/react/cache/CHANGELOG.md delete mode 100644 vendor/react/cache/LICENSE delete mode 100644 vendor/react/cache/README.md delete mode 100644 vendor/react/cache/composer.json delete mode 100644 vendor/react/cache/src/ArrayCache.php delete mode 100644 vendor/react/cache/src/CacheInterface.php delete mode 100644 vendor/react/dns/CHANGELOG.md delete mode 100644 vendor/react/dns/LICENSE delete mode 100644 vendor/react/dns/README.md delete mode 100644 vendor/react/dns/composer.json delete mode 100644 vendor/react/dns/src/BadServerException.php delete mode 100644 vendor/react/dns/src/Config/Config.php delete mode 100644 vendor/react/dns/src/Config/HostsFile.php delete mode 100644 vendor/react/dns/src/Model/Message.php delete mode 100644 vendor/react/dns/src/Model/Record.php delete mode 100644 vendor/react/dns/src/Protocol/BinaryDumper.php delete mode 100644 vendor/react/dns/src/Protocol/Parser.php delete mode 100644 vendor/react/dns/src/Query/CachingExecutor.php delete mode 100644 vendor/react/dns/src/Query/CancellationException.php delete mode 100644 vendor/react/dns/src/Query/CoopExecutor.php delete mode 100644 vendor/react/dns/src/Query/ExecutorInterface.php delete mode 100644 vendor/react/dns/src/Query/FallbackExecutor.php delete mode 100644 vendor/react/dns/src/Query/HostsFileExecutor.php delete mode 100644 vendor/react/dns/src/Query/Query.php delete mode 100644 vendor/react/dns/src/Query/RetryExecutor.php delete mode 100644 vendor/react/dns/src/Query/SelectiveTransportExecutor.php delete mode 100644 vendor/react/dns/src/Query/TcpTransportExecutor.php delete mode 100644 vendor/react/dns/src/Query/TimeoutException.php delete mode 100644 vendor/react/dns/src/Query/TimeoutExecutor.php delete mode 100644 vendor/react/dns/src/Query/UdpTransportExecutor.php delete mode 100644 vendor/react/dns/src/RecordNotFoundException.php delete mode 100644 vendor/react/dns/src/Resolver/Factory.php delete mode 100644 vendor/react/dns/src/Resolver/Resolver.php delete mode 100644 vendor/react/dns/src/Resolver/ResolverInterface.php delete mode 100644 vendor/react/http/CHANGELOG.md delete mode 100644 vendor/react/http/LICENSE delete mode 100644 vendor/react/http/README.md delete mode 100644 vendor/react/http/composer.json delete mode 100644 vendor/react/http/src/Browser.php delete mode 100644 vendor/react/http/src/Client/Client.php delete mode 100644 vendor/react/http/src/HttpServer.php delete mode 100644 vendor/react/http/src/Io/AbstractMessage.php delete mode 100644 vendor/react/http/src/Io/AbstractRequest.php delete mode 100644 vendor/react/http/src/Io/BufferedBody.php delete mode 100644 vendor/react/http/src/Io/ChunkedDecoder.php delete mode 100644 vendor/react/http/src/Io/ChunkedEncoder.php delete mode 100644 vendor/react/http/src/Io/ClientConnectionManager.php delete mode 100644 vendor/react/http/src/Io/ClientRequestState.php delete mode 100644 vendor/react/http/src/Io/ClientRequestStream.php delete mode 100644 vendor/react/http/src/Io/Clock.php delete mode 100644 vendor/react/http/src/Io/CloseProtectionStream.php delete mode 100644 vendor/react/http/src/Io/EmptyBodyStream.php delete mode 100644 vendor/react/http/src/Io/HttpBodyStream.php delete mode 100644 vendor/react/http/src/Io/IniUtil.php delete mode 100644 vendor/react/http/src/Io/LengthLimitedStream.php delete mode 100644 vendor/react/http/src/Io/MiddlewareRunner.php delete mode 100644 vendor/react/http/src/Io/MultipartParser.php delete mode 100644 vendor/react/http/src/Io/PauseBufferStream.php delete mode 100644 vendor/react/http/src/Io/ReadableBodyStream.php delete mode 100644 vendor/react/http/src/Io/RequestHeaderParser.php delete mode 100644 vendor/react/http/src/Io/Sender.php delete mode 100644 vendor/react/http/src/Io/StreamingServer.php delete mode 100644 vendor/react/http/src/Io/Transaction.php delete mode 100644 vendor/react/http/src/Io/UploadedFile.php delete mode 100644 vendor/react/http/src/Message/Request.php delete mode 100644 vendor/react/http/src/Message/Response.php delete mode 100644 vendor/react/http/src/Message/ResponseException.php delete mode 100644 vendor/react/http/src/Message/ServerRequest.php delete mode 100644 vendor/react/http/src/Message/Uri.php delete mode 100644 vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php delete mode 100644 vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php delete mode 100644 vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php delete mode 100644 vendor/react/http/src/Middleware/StreamingRequestMiddleware.php delete mode 100644 vendor/react/http/src/Server.php delete mode 100644 vendor/react/socket/CHANGELOG.md delete mode 100644 vendor/react/socket/LICENSE delete mode 100644 vendor/react/socket/README.md delete mode 100644 vendor/react/socket/composer.json delete mode 100644 vendor/react/socket/src/Connection.php delete mode 100644 vendor/react/socket/src/ConnectionInterface.php delete mode 100644 vendor/react/socket/src/Connector.php delete mode 100644 vendor/react/socket/src/ConnectorInterface.php delete mode 100644 vendor/react/socket/src/DnsConnector.php delete mode 100644 vendor/react/socket/src/FdServer.php delete mode 100644 vendor/react/socket/src/FixedUriConnector.php delete mode 100644 vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php delete mode 100644 vendor/react/socket/src/HappyEyeBallsConnector.php delete mode 100644 vendor/react/socket/src/LimitingServer.php delete mode 100644 vendor/react/socket/src/SecureConnector.php delete mode 100644 vendor/react/socket/src/SecureServer.php delete mode 100644 vendor/react/socket/src/Server.php delete mode 100644 vendor/react/socket/src/ServerInterface.php delete mode 100644 vendor/react/socket/src/SocketServer.php delete mode 100644 vendor/react/socket/src/StreamEncryption.php delete mode 100644 vendor/react/socket/src/TcpConnector.php delete mode 100644 vendor/react/socket/src/TcpServer.php delete mode 100644 vendor/react/socket/src/TimeoutConnector.php delete mode 100644 vendor/react/socket/src/UnixConnector.php delete mode 100644 vendor/react/socket/src/UnixServer.php diff --git a/vendor/clue/framework-x/.github/FUNDING.yml b/vendor/clue/framework-x/.github/FUNDING.yml deleted file mode 100644 index 9c09fb8..0000000 --- a/vendor/clue/framework-x/.github/FUNDING.yml +++ /dev/null @@ -1,2 +0,0 @@ -github: clue -custom: https://clue.engineering/support diff --git a/vendor/clue/framework-x/CHANGELOG.md b/vendor/clue/framework-x/CHANGELOG.md deleted file mode 100644 index 157d446..0000000 --- a/vendor/clue/framework-x/CHANGELOG.md +++ /dev/null @@ -1,373 +0,0 @@ -# Changelog - -## 0.16.0 (2024-03-05) - -We are thrilled to announce the official release of `v0.16.0` to the public! 🎉🚀 -Additionally, we are making all previous tagged versions available to simplify the upgrade process. -In addition to the release of `v0.16.0`, this update includes all prior tagged releases. - -This release includes exciting new features such as improved performance, additional options -for access logging, updates to our documentation and nginx + Apache configurations, -as well as many more internal improvements to our test suite and integration tests. - -* Feature: Improve performance by skipping `AccessLogHandler` if it writes to `/dev/null`. - (#248 by @clue) - -* Feature: Add optional `$path` argument for `AccessLogHandler`. - (#247 by @clue) - -* Minor documentation improvements and update nginx + Apache configuration. - (#245 and #251 by @clue) - -* Improve test suite with improved directory structure for integration tests. - (#250 by @clue) - -## 0.15.0 (2023-12-07) - -* Feature: Full PHP 8.3 compatibility. - (#244 by @clue) - -* Feature: Add `App::__invoke()` method to enable custom integrations. - (#236 by @clue) - -* Feature: Improve performance by only using `FiberHandler` for `ReactiveHandler`. - (#237 by @clue) - -* Minor documentation improvements. - (#242 by @yadaiio) - -## 0.14.0 (2023-07-31) - -* Feature: Improve Promise v3 support and use Promise v3 template types. - (#233 and #235 by @clue) - -* Feature: Improve handling `OPTIONS *` requests. - (#226 by @clue) - -* Refactor logging into new `LogStreamHandler` and reactive server logic into new `ReactiveHandler`. - (#222 and #224 by @clue) - -* Improve test suite and ensure 100% code coverage. - (#217, #221, #225 and #228 by @clue) - -## 0.13.0 (2023-02-22) - -* Feature: Forward compatibility with upcoming Promise v3. - (#188 by @clue) - -* Feature: Full PHP 8.2 compatibility. - (#194 and #207 by @clue) - -* Feature: Load environment variables from `$_ENV`, `$_SERVER` and `getenv()`. - (#205 by @clue) - -* Feature: Update to support `Content-Length` response header on `HEAD` requests. - (#186 by @clue) - -* Feature / Fix: Consistent handling for HTTP responses with multiple header values (PHP SAPI). - (#214 by @pfk84) - -* Fix: Respect explicit response status code when Location response header is given (PHP SAPI). - (#191 by @jkrzefski) - -* Minor documentation improvements. - (#189 by @clue) - -* Add PHPStan to test environment on level `max` and improve type definitions. - (#200, #201 and #204 by @clue) - -* Improve test suite and report failed assertions. - (#199 by @clue and #208 by @SimonFrings) - -## 0.12.0 (2022-08-03) - -* Feature: Support loading environment variables from DI container configuration. - (#184 by @clue) - -* Feature: Support typed container variables for container factory functions. - (#178, #179 and #180 by @clue) - -* Feature: Support nullable and `null` arguments and default values for DI container configuration. - (#181 and #183 by @clue) - -* Feature: Support untyped and `mixed` arguments for container factory. - (#182 by @clue) - -## 0.11.0 (2022-07-26) - -* Feature: Make `AccessLogHandler` and `ErrorHandler` part of public API. - (#173 and #174 by @clue) - - ```php - run(); - ``` - -* Feature: Support loading `AccessLogHandler` and `ErrorHandler` from `Container`. - (#175 by @clue) - -* Feature: Read `$remote_addr` attribute for `AccessLogHandler` (trusted proxies). - (#177 by @clue) - -* Internal refactoring to move all handlers to `Io` namespace. - (#176 by @clue) - -* Update test suite to remove deprecated `utf8_decode()` (PHP 8.2 preparation). - (#171 by SimonFrings) - -## 0.10.0 (2022-07-14) - -* Feature: Built-in support for fibers on PHP 8.1+ with stable reactphp/async. - (#168 by @clue) - - ```php - $app->get('/book/{isbn}', function (Psr\Http\Message\ServerRequestInterface $request) use ($db) { - $isbn = $request->getAttribute('isbn'); - $result = await($db->query( - 'SELECT title FROM book WHERE isbn = ?', - [$isbn] - )); - - assert($result instanceof React\MySQL\QueryResult); - $data = $result->resultRows[0]['title']; - - return React\Http\Message\Response::plaintext( - $data - ); - }); - ``` - -* Feature: Support PSR-11 container interface by using DI container as adapter. - (#163 by @clue) - -* Minor documentation improvements. - (#158 by @clue and #160 by @SimonFrings) - -## 0.9.0 (2022-05-13) - -* Feature: Add signal handling support for `SIGINT` and `SIGTERM`. - (#150 by @clue) - -* Feature: Improve error output for exception messages with special characters. - (#131 by @clue) - -* Add new documentation chapters for Docker containers and HTTP redirecting. - (#138 by SimonFrings and #136, #151 and #156 by @clue) - -* Minor documentation improvements. - (#143 by @zf2timo, #153 by @mattschlosser and #129 and #154 by @clue) - -* Improve test suite and add tests for `Dockerfile` instructions. - (#148 and #149 by @clue) - -## 0.8.0 (2022-03-07) - -* Feature: Automatically start new fiber for each request on PHP 8.1+. - (#117 by @clue) - -* Feature: Add fiber compatibility mode for PHP < 8.1. - (#128 by @clue) - -* Improve documentation and update installation instructions for react/async. - (#116 and #126 by @clue and #124, #125 and #127 by @SimonFrings) - -* Improve fiber tests to avoid now unneeded `await()` calls. - (#118 by @clue) - -## 0.7.0 (2022-02-05) - -* Feature: Update to use HTTP status code constants and JSON/HTML response helpers. - (#114 by @clue) - - ```php - $app->get('/users/{name}', function (Psr\Http\Message\ServerRequestInterface $request) { - return React\Http\Message\Response::plaintext( - "Hello " . $request->getAttribute('name') . "!\n" - ); - }); - ``` - -* Feature / Fix: Update to improve protocol handling for HTTP responses with no body. - (#113 by @clue) - -* Minor documentation improvements. - (#112 by @SimonFrings and #115 by @netcarver) - -## 0.6.0 (2021-12-20) - -* Feature: Support automatic dependency injection by using class names (DI container). - (#89, #92 and #94 by @clue) - - ```php - get('/', Acme\Todo\HelloController::class); - $app->get('/users/{name}', Acme\Todo\UserController::class); - - $app->run(); - ``` - -* Feature: Add support for explicit DI container configuration. - (#95, #96 and #97 by @clue) - - ```php - fn() => new Acme\Todo\HelloController(); - Acme\Todo\UserController::class => function (React\Http\Browser $browser) { - // example UserController class requires two arguments: - // - first argument will be autowired based on class reference - // - second argument expects some manual value - return new Acme\Todo\UserController($browser, 42); - } - ]); - - // … - ``` - -* Feature: Refactor to use `$_SERVER` instead of `getenv()`. - (#91 by @bpolaszek) - -* Minor documentation improvements. - (#100 by @clue) - -* Update test suite to use stable PHP 8.1 Docker image. - (#90 by @clue) - -## 0.5.0 (2021-11-30) - -* Feature / BC break: Simplify `App` by always using default loop, drop optional loop instance. - (#88 by @clue) - - ```php - // old - $loop = React\EventLoop\Loop::get(); - $app = new FrameworkX\App($loop); - - // new (already supported before) - $app = new FrameworkX\App(); - ``` - -* Add documentation for manual restart of systemd service and chapter for Caddy deployment. - (#87 by @SimonFrings and #82 by @francislavoie) - -* Improve documentation, remove leftover `$loop` references and fix typos. - (#72 by @shuvroroy, #80 by @Ivanshamir, #81 by @clue and #83 by @rattuscz) - -## 0.4.0 (2021-11-23) - -We are excited to announce the official release of Framework X to the public! 🎉🚀 -This release includes exciting new features such as full compatibility with PHP 8.1, -improvements to response handling, and enhanced documentation covering nginx, -Apache, and async database usage. - -* Feature: Announce Framework X public beta. - (#64 by @clue) - -* Feature: Full PHP 8.1 compatibility. - (#58 by @clue) - -* Feature: Improve `AccessLogHandler` and fix response size for streaming response body. - (#47, #48, #49 and #50 by @clue) - -* Feature / Fix: Skip sending body and `Content-Length` for responses with no body. - (#51 by @clue) - -* Feature / Fix: Consistently reject proxy requests and handle `OPTIONS *` requests. - (#46 by @clue) - -* Add new documentation chapters for nginx, Apache and async database. - (#57, #59 and #60 by @clue) - -* Improve documentation, examples and describe HTTP caching and output buffering. - (#52, #53, #55, #56, #61, #62 and #63 by @clue) - -## 0.3.0 (2021-09-23) - -* Feature: Add support for global middleware. - (#23 by @clue) - -* Feature: Improve error output and refactor internal error handler. - (#37, #39 and #41 by @clue) - -* Feature: Support changing listening address via new `X_LISTEN` environment variable. - (#38 by @clue) - -* Feature: Update to new ReactPHP HTTP and Socket API. - (#26 and #29 by @HLeithner and #34 by @clue) - -* Feature: Refactor to use new `AccessLogHandler`, `RouteHandler`, `RedirectHandler` and `SapiHandler`. - (#42, #43, #44 and #45 by @clue) - -* Fix: Fix path filter regex. - (#27 by @HLeithner) - -* Add documentation for async middleware and systemd service unit configuration. - (#24 by @Degra1991 and #32, #35, #36 and #40 by @clue) - -* Improve test suite and run tests on Windows with PHPUnit. - (#31 by @SimonFrings and #28 and #33 by @clue) - -## 0.2.0 (2021-06-18) - -* Feature: Simplify `App` usage by making `LoopInterface` argument optional. - (#22 by @clue) - - ```php - // old (still supported) - $loop = React\EventLoop\Factory::create(); - $app = new FrameworkX\App($loop); - - // new (using default loop) - $app = new FrameworkX\App(); - ``` - -* Feature: Add middleware support. - (#18 by @clue) - -* Feature: Refactor and simplify route dispatcher. - (#21 by @clue) - -* Feature: Add Generator-based coroutine implementation. - (#17 by @clue) - -* Minor documentation improvements. - (#15, #16 and #19 by @clue) - -## 0.1.0 (2021-04-30) - -We're excited to announce the release of the first version of Framework X in -private beta! This version marks the starting point of our project and is the -first of many milestones for making async PHP easier than ever before. - -* Release Framework X, major documentation overhaul and improve examples. - (#14, #13 and #2 by @clue) - -* Feature: Support running behind nginx and Apache (PHP-FPM and mod_php). - (#3, #11 and #12 by @clue) - -* Feature / Fix: Consistently parse request URI and improve URL handling. - (#4, #5, #6 and #7 by @clue) - -* Feature: Rewrite `FilesystemHandler`, improve file access and directory listing. - (#8 and #9 by @clue) - -* Feature: Add `any()` router method to match any request method. - (#10 by @clue) diff --git a/vendor/clue/framework-x/LICENSE b/vendor/clue/framework-x/LICENSE deleted file mode 100644 index d8337e7..0000000 --- a/vendor/clue/framework-x/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2019 Christian Lück - -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/clue/framework-x/README.md b/vendor/clue/framework-x/README.md deleted file mode 100644 index 78a2686..0000000 --- a/vendor/clue/framework-x/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# Framework X - -[![CI status](https://github.com/clue-access/framework-x/workflows/CI/badge.svg)](https://github.com/clue-access/framework-x/actions) -[![code coverage](https://img.shields.io/badge/code%20coverage-100%25-success)](#tests) - -Framework X – the simple and fast micro framework for building reactive web applications that run anywhere. - -* [Support us](#support-us) -* [Quickstart](#quickstart) -* [Documentation](#documentation) -* [Contribute](#contribute) -* [Tests](#tests) -* [License](#license) - -## Support us - -We invest a lot of time developing, maintaining and updating our awesome -open-source projects. You can help us sustain this high-quality of our work by -[becoming a sponsor on GitHub](https://github.com/sponsors/clue). Sponsors get -numerous benefits in return, see our [sponsoring page](https://github.com/sponsors/clue) -for details. - -Let's take these projects to the next level together! 🚀 - -## Quickstart - -Start by creating an empty project directory. -Next, we can start by taking a look at a simple example application. -You can use this example to get started by creating a new `public/` directory with -an `index.php` file inside: - -```php -get('/', function () { - return React\Http\Message\Response::plaintext( - "Hello wörld!\n" - ); -}); - -$app->get('/users/{name}', function (Psr\Http\Message\ServerRequestInterface $request) { - return React\Http\Message\Response::plaintext( - "Hello " . $request->getAttribute('name') . "!\n" - ); -}); - -$app->run(); -``` - -Next, we need to install X and its dependencies to actually run this project. -In your project directory, simply run the following command: - -```bash -$ composer require clue/framework-x:^0.16 -``` - -> See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. - -That's it already! The next step is now to serve this web application. -One of the nice properties of this project is that is works both behind -traditional web server setups as well as in a stand-alone environment. - -For example, you can run the above example using the built-in web server like -this: - -```bash -$ php public/index.php -``` - -You can now use your favorite web browser or command line tool to check your web -application responds as expected: - -```bash -$ curl http://localhost:8080/ -Hello wörld! -``` - -## Documentation - -Hooked? -See [website](https://framework-x.org/) for full documentation. - -Found a typo or want to contribute? -The website documentation is built from the source documentation files in -the [docs/](docs/) folder. - -## Contribute - -You want to contribute to the Framework X source code or documentation? You've -come to the right place! - -To contribute to the source code just locate the [src/](src/) folder and you'll find all -content in there. Additionally, our [tests/](tests/) folder contains all our unit -tests and acceptance tests to assure our code works as expected. For more -information on how to run the test suite check out our [testing chapter](#tests). - -If you want to contribute to the [documentation](#documentation) of Framework X -found on the website, take a look inside the [docs/](docs/) folder. You'll find further -instructions inside the `README.md` in there. - -Found a typo on our [website](https://framework-x.org/)? Simply go to our -[website repository](https://github.com/clue/framework-x-website) -and follow the instructions found in the `README`. - -## Tests - -To run the test suite, you first need to clone this repo and then install all -dependencies [through Composer](https://getcomposer.org/): - -```bash -$ composer install -``` - -To run the test suite, go to the project root and run: - -```bash -$ vendor/bin/phpunit -``` - -The test suite is set up to always ensure 100% code coverage across all -supported environments. If you have the Xdebug extension installed, you can also -generate a code coverage report locally like this: - -```bash -$ XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text -``` - -Additionally, you can run our sophisticated integration tests to verify the -framework examples work as expected behind your web server. Use your web server -of choice (see deployment documentation) and execute the tests with the URL to -your installation like this: - -```bash -$ php tests/integration/public/index.php -$ tests/integration.bash http://localhost:8080 -``` - -## License - -This project is released under the permissive [MIT license](LICENSE). - -> Did you know that I offer custom development services and issuing invoices for - sponsorships of releases and for contributions? Contact me (@clue) for details. diff --git a/vendor/clue/framework-x/composer.json b/vendor/clue/framework-x/composer.json deleted file mode 100644 index e41dcb5..0000000 --- a/vendor/clue/framework-x/composer.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "clue/framework-x", - "description": "Framework X – the simple and fast micro framework for building reactive web applications that run anywhere.", - "keywords": ["microframework", "micro", "framework", "web", "http", "event-driven", "async", "ReactPHP"], - "homepage": "https://framework-x.org/", - "license": "MIT", - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "require": { - "php": ">=7.1", - "nikic/fast-route": "^1.3", - "react/async": "^4 || ^3", - "react/http": "^1.9", - "react/promise": "^3 || ^2.10", - "react/socket": "^1.13" - }, - "require-dev": { - "phpstan/phpstan": "1.10.47 || 1.4.10", - "phpunit/phpunit": "^9.6 || ^7.5", - "psr/container": "^2 || ^1", - "react/promise-timer": "^1.10" - }, - "autoload": { - "psr-4": { - "FrameworkX\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "FrameworkX\\Tests\\": "tests/" - }, - "files": [ - "tests/FiberStub.php" - ] - } -} diff --git a/vendor/clue/framework-x/src/AccessLogHandler.php b/vendor/clue/framework-x/src/AccessLogHandler.php deleted file mode 100644 index a5d87f4..0000000 --- a/vendor/clue/framework-x/src/AccessLogHandler.php +++ /dev/null @@ -1,140 +0,0 @@ -isDevNull()) { - // only assign logger if we're not logging to /dev/null (which would discard any logs) - $this->logger = $logger; - } - - $this->hasHighResolution = \function_exists('hrtime'); // PHP 7.3+ - } - - /** - * [Internal] Returns whether we're writing to /dev/null (which will discard any logs) - * - * @internal - * @return bool - */ - public function isDevNull(): bool - { - return $this->logger === null; - } - - /** - * @return ResponseInterface|PromiseInterface|\Generator - */ - public function __invoke(ServerRequestInterface $request, callable $next) - { - if ($this->logger === null) { - // Skip if we're logging to /dev/null (which will discard any logs). - // As an additional optimization, the `App` will automatically - // detect we no longer need to invoke this instance at all. - return $next($request); // @codeCoverageIgnore - } - - $now = $this->now(); - $response = $next($request); - - if ($response instanceof PromiseInterface) { - /** @var PromiseInterface $response */ - return $response->then(function (ResponseInterface $response) use ($request, $now) { - $this->logWhenClosed($request, $response, $now); - return $response; - }); - } elseif ($response instanceof \Generator) { - return (function (\Generator $generator) use ($request, $now) { - $response = yield from $generator; - $this->logWhenClosed($request, $response, $now); - return $response; - })($response); - } else { - $this->logWhenClosed($request, $response, $now); - return $response; - } - } - - /** - * checks if response body is closed (not streaming) before writing log message for response - */ - private function logWhenClosed(ServerRequestInterface $request, ResponseInterface $response, float $start): void - { - $body = $response->getBody(); - - if ($body instanceof ReadableStreamInterface && $body->isReadable()) { - $size = 0; - $body->on('data', function (string $chunk) use (&$size) { - $size += strlen($chunk); - }); - - $body->on('close', function () use (&$size, $request, $response, $start) { - $this->log($request, $response, $size, $this->now() - $start); - }); - } else { - $this->log($request, $response, $body->getSize() ?? strlen((string) $body), $this->now() - $start); - } - } - - /** - * writes log message for response after response body is closed (not streaming anymore) - */ - private function log(ServerRequestInterface $request, ResponseInterface $response, int $responseSize, float $time): void - { - $method = $request->getMethod(); - $status = $response->getStatusCode(); - - // HEAD requests and `204 No Content` and `304 Not Modified` always use an empty response body - if ($method === 'HEAD' || $status === Response::STATUS_NO_CONTENT || $status === Response::STATUS_NOT_MODIFIED) { - $responseSize = 0; - } - - \assert($this->logger instanceof LogStreamHandler); - $this->logger->log( - ($request->getAttribute('remote_addr') ?? $request->getServerParams()['REMOTE_ADDR'] ?? '-') . ' ' . - '"' . $this->escape($method) . ' ' . $this->escape($request->getRequestTarget()) . ' HTTP/' . $request->getProtocolVersion() . '" ' . - $status . ' ' . $responseSize . ' ' . sprintf('%.3F', $time < 0 ? 0 : $time) - ); - } - - private function escape(string $s): string - { - return (string) preg_replace_callback('/[\x00-\x1F\x7F-\xFF"\\\\]+/', function (array $m) { - return str_replace('%', '\x', rawurlencode($m[0])); - }, $s); - } - - private function now(): float - { - return $this->hasHighResolution ? hrtime(true) * 1e-9 : microtime(true); - } -} diff --git a/vendor/clue/framework-x/src/App.php b/vendor/clue/framework-x/src/App.php deleted file mode 100644 index 2e000b2..0000000 --- a/vendor/clue/framework-x/src/App.php +++ /dev/null @@ -1,354 +0,0 @@ -getAccessLogHandler(); - } elseif ($handler === ErrorHandler::class) { - $handler = $container->getErrorHandler(); - } - - // ensure AccessLogHandler is always followed by ErrorHandler - if ($needsErrorHandlerNext && !$handler instanceof ErrorHandler) { - break; - } - $needsErrorHandlerNext = false; - - if ($handler instanceof Container) { - // remember last Container to load any following class names - $container = $handler; - - // add default ErrorHandler from last Container before adding any other handlers, may be followed by other Container instances (unlikely) - if (!$handlers) { - $needsErrorHandler = $needsAccessLog = $container; - } - } elseif (!\is_callable($handler)) { - $handlers[] = $container->callable($handler); - } else { - // don't need a default ErrorHandler if we're adding one as first handler or AccessLogHandler as first followed by one - if ($needsErrorHandler && ($handler instanceof ErrorHandler || $handler instanceof AccessLogHandler) && !$handlers) { - $needsErrorHandler = null; - } - - // only add to list of handlers if this is not a NOOP - if (!$handler instanceof AccessLogHandler || !$handler->isDevNull()) { - $handlers[] = $handler; - } - - if ($handler instanceof AccessLogHandler) { - $needsAccessLog = null; - $needsErrorHandlerNext = true; - } - } - } - if ($needsErrorHandlerNext) { - throw new \TypeError('AccessLogHandler must be followed by ErrorHandler'); - } - } - - // add default ErrorHandler as first handler unless it is already added explicitly - if ($needsErrorHandler instanceof Container) { - \array_unshift($handlers, $needsErrorHandler->getErrorHandler()); - } - - // only log for built-in webserver and PHP development webserver by default, others have their own access log - if ($needsAccessLog instanceof Container) { - $handler = $needsAccessLog->getAccessLogHandler(); - if (!$handler->isDevNull()) { - \array_unshift($handlers, $handler); - } - } - - $this->router = new RouteHandler($container); - $handlers[] = $this->router; - $this->handler = new MiddlewareHandler($handlers); - $this->sapi = \PHP_SAPI === 'cli' ? new ReactiveHandler($container->getEnv('X_LISTEN')) : new SapiHandler(); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function get(string $route, $handler, ...$handlers): void - { - $this->map(['GET'], $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function head(string $route, $handler, ...$handlers): void - { - $this->map(['HEAD'], $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function post(string $route, $handler, ...$handlers): void - { - $this->map(['POST'], $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function put(string $route, $handler, ...$handlers): void - { - $this->map(['PUT'], $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function patch(string $route, $handler, ...$handlers): void - { - $this->map(['PATCH'], $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function delete(string $route, $handler, ...$handlers): void - { - $this->map(['DELETE'], $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function options(string $route, $handler, ...$handlers): void - { - // backward compatibility: `OPTIONS * HTTP/1.1` can be matched with empty path (legacy) - if ($route === '') { - $route = '*'; - } - - $this->map(['OPTIONS'], $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function any(string $route, $handler, ...$handlers): void - { - $this->map(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], $route, $handler, ...$handlers); - } - - /** - * - * @param string[] $methods - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function map(array $methods, string $route, $handler, ...$handlers): void - { - $this->router->map($methods, $route, $handler, ...$handlers); - } - - /** - * @param string $route - * @param string $target - * @param int $code - */ - public function redirect(string $route, string $target, int $code = Response::STATUS_FOUND): void - { - $this->any($route, new RedirectHandler($target, $code)); - } - - /** - * Runs the app to handle HTTP requests according to any registered routes and middleware. - * - * This is where the magic happens: When executed on the command line (CLI), - * this will run the powerful reactive request handler built on top of - * ReactPHP. This works by running the efficient built-in HTTP web server to - * handle incoming HTTP requests through ReactPHP's HTTP and socket server. - * This async execution mode is usually recommended as it can efficiently - * process a large number of concurrent connections and process multiple - * incoming requests simultaneously. The long-running server process will - * continue to run until it is interrupted by a signal. - * - * When executed behind traditional PHP SAPIs (PHP-FPM, FastCGI, Apache, etc.), - * this will handle a single request and run until a single response is sent. - * This is particularly useful because it allows you to run the exact same - * app in any environment. - * - * @see ReactiveHandler::run() - * @see SapiHandler::run() - */ - public function run(): void - { - $this->sapi->run(\Closure::fromCallable([$this, 'handleRequest'])); - } - - /** - * Invokes the app to handle a single HTTP request according to any registered routes and middleware. - * - * This method allows you to pass in a single HTTP request object that will - * be processed according to any registered routes and middleware and will - * return an HTTP response object as a result. - * - * ```php - * $app = new FrameworkX\App(); - * $app->get('/', fn() => React\Http\Message\Response::plaintext("Hello!\n")); - * - * $request = new React\Http\Message\ServerRequest('GET', 'https://example.com/'); - * $response = $app($request); - * - * assert($response instanceof Psr\Http\Message\ResponseInterface); - * assert($response->getStatusCode() === 200); - * assert($response->getBody()->getContents() === "Hello\n"); - * ``` - * - * This is particularly useful for higher-level integration test suites and - * for custom integrations with other runtime environments like serverless - * functions or other frameworks. Otherwise, most applications would likely - * want to use the `run()` method to run the application and automatically - * accept incoming HTTP requests according to the PHP SAPI in use. - * - * @param ServerRequestInterface $request The HTTP request object to process. - * @return ResponseInterface This method returns an HTTP response object - * according to any registered routes and middleware. If any handler is - * async, it will await its execution before returning, running the - * event loop as needed. If the request can not be routed or any handler - * fails, it will return a matching HTTP error response object. - * @throws void This method never throws. If the request can not be routed - * or any handler fails, it will be turned into a valid error response - * before returning. - * @see self::run() - */ - public function __invoke(ServerRequestInterface $request): ResponseInterface - { - $response = $this->handleRequest($request); - if ($response instanceof PromiseInterface) { - /** @throws void */ - $response = await($response); - assert($response instanceof ResponseInterface); - } - - return $response; - } - - /** - * @param ServerRequestInterface $request - * @return ResponseInterface|PromiseInterface - * Returns a response or a Promise which eventually fulfills with a - * response. This method never throws or resolves a rejected promise. - * If the request can not be routed or the handler fails, it will be - * turned into a valid error response before returning. - * @throws void - */ - private function handleRequest(ServerRequestInterface $request) - { - $response = ($this->handler)($request); - assert($response instanceof ResponseInterface || $response instanceof PromiseInterface || $response instanceof \Generator); - - if ($response instanceof \Generator) { - if ($response->valid()) { - $response = $this->coroutine($response); - } else { - $response = $response->getReturn(); - assert($response instanceof ResponseInterface); - } - } - - return $response; - } - - /** - * @return PromiseInterface - */ - private function coroutine(\Generator $generator): PromiseInterface - { - $next = null; - $deferred = new Deferred(); - $next = function () use ($generator, &$next, $deferred) { - if (!$generator->valid()) { - $deferred->resolve($generator->getReturn()); - return; - } - - $promise = $generator->current(); - assert($promise instanceof PromiseInterface); - - $promise->then(function ($value) use ($generator, $next) { - $generator->send($value); - $next(); - }, function ($reason) use ($generator, $next) { - $generator->throw($reason); - $next(); - }); - }; - - $next(); - - return $deferred->promise(); - } -} diff --git a/vendor/clue/framework-x/src/Container.php b/vendor/clue/framework-x/src/Container.php deleted file mode 100644 index 2058379..0000000 --- a/vendor/clue/framework-x/src/Container.php +++ /dev/null @@ -1,382 +0,0 @@ -|ContainerInterface */ - private $container; - - /** @var bool */ - private $useProcessEnv; - - /** @param array|ContainerInterface $loader */ - public function __construct($loader = []) - { - /** @var mixed $loader explicit type check for mixed if user ignores parameter type */ - if (!\is_array($loader) && !$loader instanceof ContainerInterface) { - throw new \TypeError( - 'Argument #1 ($loader) must be of type array|Psr\Container\ContainerInterface, ' . (\is_object($loader) ? get_class($loader) : gettype($loader)) . ' given' - ); - } - - foreach (($loader instanceof ContainerInterface ? [] : $loader) as $name => $value) { - if ( - (!\is_object($value) && !\is_scalar($value) && $value !== null) || - (!$value instanceof $name && !$value instanceof \Closure && !\is_string($value) && \strpos($name, '\\') !== false) - ) { - throw new \BadMethodCallException('Map for ' . $name . ' contains unexpected ' . (is_object($value) ? get_class($value) : gettype($value))); - } - } - $this->container = $loader; - - // prefer reading environment from `$_ENV` and `$_SERVER`, only fall back to `getenv()` in thread-safe environments - $this->useProcessEnv = \ZEND_THREAD_SAFE === false || \in_array(\PHP_SAPI, ['cli', 'cli-server', 'cgi-fcgi', 'fpm-fcgi'], true); - } - - /** @return mixed */ - public function __invoke(ServerRequestInterface $request, callable $next = null) - { - if ($next === null) { - // You don't want to end up here. This only happens if you use the - // container as a final request handler instead of as a middleware. - // In this case, you should omit the container or add another final - // request handler behind the container in the middleware chain. - throw new \BadMethodCallException('Container should not be used as final request handler'); - } - - // If the container is used as a middleware, simply forward to the next - // request handler. As an additional optimization, the container would - // usually be filtered out from a middleware chain as this is a NO-OP. - return $next($request); - } - - /** - * @param class-string $class - * @return callable(ServerRequestInterface,?callable=null) - * @internal - */ - public function callable(string $class): callable - { - return function (ServerRequestInterface $request, callable $next = null) use ($class) { - // Check `$class` references a valid class name that can be autoloaded - if (\is_array($this->container) && !\class_exists($class, true) && !interface_exists($class, false) && !trait_exists($class, false)) { - throw new \BadMethodCallException('Request handler class ' . $class . ' not found'); - } - - try { - if ($this->container instanceof ContainerInterface) { - $handler = $this->container->get($class); - } else { - $handler = $this->loadObject($class); - } - } catch (\Throwable $e) { - throw new \BadMethodCallException( - 'Request handler class ' . $class . ' failed to load: ' . $e->getMessage(), - 0, - $e - ); - } - - // Check `$handler` references a class name that is callable, i.e. has an `__invoke()` method. - // This initial version is intentionally limited to checking the method name only. - // A follow-up version will likely use reflection to check request handler argument types. - if (!is_callable($handler)) { - throw new \BadMethodCallException('Request handler class "' . $class . '" has no public __invoke() method'); - } - - // invoke request handler as middleware handler or final controller - if ($next === null) { - return $handler($request); - } - return $handler($request, $next); - }; - } - - /** @internal */ - public function getEnv(string $name): ?string - { - assert(\preg_match('/^[A-Z][A-Z0-9_]+$/', $name) === 1); - - if ($this->container instanceof ContainerInterface && $this->container->has($name)) { - $value = $this->container->get($name); - } elseif ($this->hasVariable($name)) { - $value = $this->loadVariable($name, 'mixed', true, 64); - } else { - return null; - } - - if (!\is_string($value) && $value !== null) { - throw new \TypeError('Environment variable $' . $name . ' expected type string|null, but got ' . (\is_object($value) ? \get_class($value) : \gettype($value))); - } - - return $value; - } - - /** @internal */ - public function getAccessLogHandler(): AccessLogHandler - { - if ($this->container instanceof ContainerInterface) { - if ($this->container->has(AccessLogHandler::class)) { - // @phpstan-ignore-next-line method return type will ensure correct type or throw `TypeError` - return $this->container->get(AccessLogHandler::class); - } else { - return new AccessLogHandler(); - } - } - return $this->loadObject(AccessLogHandler::class); - } - - /** @internal */ - public function getErrorHandler(): ErrorHandler - { - if ($this->container instanceof ContainerInterface) { - if ($this->container->has(ErrorHandler::class)) { - // @phpstan-ignore-next-line method return type will ensure correct type or throw `TypeError` - return $this->container->get(ErrorHandler::class); - } else { - return new ErrorHandler(); - } - } - return $this->loadObject(ErrorHandler::class); - } - - /** - * @template T of object - * @param class-string $name - * @return T - * @throws \BadMethodCallException if object of type $name can not be loaded - */ - private function loadObject(string $name, int $depth = 64) /*: object (PHP 7.2+) */ - { - assert(\is_array($this->container)); - - if (\array_key_exists($name, $this->container)) { - if (\is_string($this->container[$name])) { - if ($depth < 1) { - throw new \BadMethodCallException('Factory for ' . $name . ' is recursive'); - } - - // @phpstan-ignore-next-line because type of container value is explicitly checked after getting here - $value = $this->loadObject($this->container[$name], $depth - 1); - if (!$value instanceof $name) { - throw new \BadMethodCallException('Factory for ' . $name . ' returned unexpected ' . \get_class($value)); - } - - $this->container[$name] = $value; - } elseif ($this->container[$name] instanceof \Closure) { - // build list of factory parameters based on parameter types - $closure = new \ReflectionFunction($this->container[$name]); - $params = $this->loadFunctionParams($closure, $depth, true); - - // invoke factory with list of parameters - $value = $params === [] ? ($this->container[$name])() : ($this->container[$name])(...$params); - - if (\is_string($value)) { - if ($depth < 1) { - throw new \BadMethodCallException('Factory for ' . $name . ' is recursive'); - } - - // @phpstan-ignore-next-line because type of container value is explicitly checked after getting here - $value = $this->loadObject($value, $depth - 1); - } - if (!$value instanceof $name) { - throw new \BadMethodCallException('Factory for ' . $name . ' returned unexpected ' . (is_object($value) ? get_class($value) : gettype($value))); - } - - $this->container[$name] = $value; - } elseif (!$this->container[$name] instanceof $name) { - throw new \BadMethodCallException('Map for ' . $name . ' contains unexpected ' . (\is_object($this->container[$name]) ? \get_class($this->container[$name]) : \gettype($this->container[$name]))); - } - - assert($this->container[$name] instanceof $name); - - return $this->container[$name]; - } - - // Check `$name` references a valid class name that can be autoloaded - if (!\class_exists($name, true) && !interface_exists($name, false) && !trait_exists($name, false)) { - throw new \BadMethodCallException('Class ' . $name . ' not found'); - } - - $class = new \ReflectionClass($name); - if (!$class->isInstantiable()) { - $modifier = 'class'; - if ($class->isInterface()) { - $modifier = 'interface'; - } elseif ($class->isAbstract()) { - $modifier = 'abstract class'; - } elseif ($class->isTrait()) { - $modifier = 'trait'; - } - throw new \BadMethodCallException('Cannot instantiate ' . $modifier . ' '. $name); - } - - // build list of constructor parameters based on parameter types - $ctor = $class->getConstructor(); - $params = $ctor === null ? [] : $this->loadFunctionParams($ctor, $depth, false); - - // instantiate with list of parameters - // @phpstan-ignore-next-line because `$class->newInstance()` is known to return `T` - return $this->container[$name] = $params === [] ? new $name() : $class->newInstance(...$params); - } - - /** - * @return list - * @throws \BadMethodCallException if either parameter can not be loaded - */ - private function loadFunctionParams(\ReflectionFunctionAbstract $function, int $depth, bool $allowVariables): array - { - $params = []; - foreach ($function->getParameters() as $parameter) { - $params[] = $this->loadParameter($parameter, $depth, $allowVariables); - } - - return $params; - } - - /** - * @return mixed - * @throws \BadMethodCallException if $parameter can not be loaded - */ - private function loadParameter(\ReflectionParameter $parameter, int $depth, bool $allowVariables) /*: mixed (PHP 8.0+) */ - { - assert(\is_array($this->container)); - - $type = $parameter->getType(); - $hasDefault = $parameter->isDefaultValueAvailable() || ((!$type instanceof \ReflectionNamedType || $type->getName() !== 'mixed') && $parameter->allowsNull()); - - // abort for union types (PHP 8.0+) and intersection types (PHP 8.1+) - // @phpstan-ignore-next-line for PHP < 8 - if ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { // @codeCoverageIgnoreStart - if ($hasDefault) { - return $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null; - } - throw new \BadMethodCallException(self::parameterError($parameter) . ' expects unsupported type ' . $type); - } // @codeCoverageIgnoreEnd - - // load container variables if parameter name is known - assert($type === null || $type instanceof \ReflectionNamedType); - if ($allowVariables && $this->hasVariable($parameter->getName())) { - return $this->loadVariable($parameter->getName(), $type === null ? 'mixed' : $type->getName(), $parameter->allowsNull(), $depth); - } - - // abort if parameter is untyped and not explicitly defined by container variable - if ($type === null) { - assert($parameter->allowsNull()); - if ($parameter->isDefaultValueAvailable()) { - return $parameter->getDefaultValue(); - } - throw new \BadMethodCallException(self::parameterError($parameter) . ' has no type'); - } - - // use default/nullable argument if not loadable as container variable or by type - assert($type instanceof \ReflectionNamedType); - if ($hasDefault && ($type->isBuiltin() || !\array_key_exists($type->getName(), $this->container))) { - return $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null; - } - - // abort if required container variable is not defined or for any other primitive types (array etc.) - if ($type->isBuiltin()) { - if ($allowVariables) { - throw new \BadMethodCallException(self::parameterError($parameter) . ' is not defined'); - } else { - throw new \BadMethodCallException(self::parameterError($parameter) . ' expects unsupported type ' . $type->getName()); - } - } - - // abort for unreasonably deep nesting or recursive types - if ($depth < 1) { - throw new \BadMethodCallException(self::parameterError($parameter) . ' is recursive'); - } - - // @phpstan-ignore-next-line because `$type->getName()` is a `class-string` by definition - return $this->loadObject($type->getName(), $depth - 1); - } - - private function hasVariable(string $name): bool - { - return (\is_array($this->container) && \array_key_exists($name, $this->container)) || (isset($_ENV[$name]) || (\is_string($_SERVER[$name] ?? null) || ($this->useProcessEnv && \getenv($name) !== false)) && \preg_match('/^[A-Z][A-Z0-9_]+$/', $name)); - } - - /** - * @return object|string|int|float|bool|null - * @throws \BadMethodCallException if $name is not a valid container variable - */ - private function loadVariable(string $name, string $type, bool $nullable, int $depth) /*: object|string|int|float|bool|null (PHP 8.0+) */ - { - assert($this->hasVariable($name)); - assert(\is_array($this->container) || !$this->container->has($name)); - - if (\is_array($this->container) && ($this->container[$name] ?? null) instanceof \Closure) { - if ($depth < 1) { - throw new \BadMethodCallException('Container variable $' . $name . ' is recursive'); - } - - // build list of factory parameters based on parameter types - $factory = $this->container[$name]; - assert($factory instanceof \Closure); - $closure = new \ReflectionFunction($factory); - $params = $this->loadFunctionParams($closure, $depth - 1, true); - - // invoke factory with list of parameters - $value = $params === [] ? $factory() : $factory(...$params); - - if (!\is_object($value) && !\is_scalar($value) && $value !== null) { - throw new \BadMethodCallException('Container variable $' . $name . ' expected type object|scalar|null from factory, but got ' . \gettype($value)); - } - - $this->container[$name] = $value; - } elseif (\is_array($this->container) && \array_key_exists($name, $this->container)) { - $value = $this->container[$name]; - } elseif (isset($_ENV[$name])) { - assert(\is_string($_ENV[$name])); - $value = $_ENV[$name]; - } elseif (isset($_SERVER[$name])) { - assert(\is_string($_SERVER[$name])); - $value = $_SERVER[$name]; - } else { - $value = \getenv($name); - assert($this->useProcessEnv && $value !== false); - } - - assert(\is_object($value) || \is_scalar($value) || $value === null); - - // allow null values if parameter is marked nullable or untyped or mixed - if ($nullable && $value === null) { - return null; - } - - // skip type checks and allow all values if expected type is undefined or mixed (PHP 8+) - if ($type === 'mixed') { - return $value; - } - - if ( - (\is_object($value) && !$value instanceof $type) || - (!\is_object($value) && !\in_array($type, ['string', 'int', 'float', 'bool'])) || - ($type === 'string' && !\is_string($value)) || ($type === 'int' && !\is_int($value)) || ($type === 'float' && !\is_float($value)) || ($type === 'bool' && !\is_bool($value)) - ) { - throw new \BadMethodCallException('Container variable $' . $name . ' expected type ' . $type . ', but got ' . (\is_object($value) ? \get_class($value) : \gettype($value))); - } - - return $value; - } - - /** @throws void */ - private static function parameterError(\ReflectionParameter $parameter): string - { - $name = $parameter->getDeclaringFunction()->getShortName(); - if (!$parameter->getDeclaringFunction()->isClosure() && ($class = $parameter->getDeclaringClass()) !== null) { - $name = explode("\0", $class->getName())[0] . '::' . $name; - } - - return 'Argument ' . ($parameter->getPosition() + 1) . ' ($' . $parameter->getName() . ') of ' . $name . '()'; - } -} diff --git a/vendor/clue/framework-x/src/ErrorHandler.php b/vendor/clue/framework-x/src/ErrorHandler.php deleted file mode 100644 index e26d184..0000000 --- a/vendor/clue/framework-x/src/ErrorHandler.php +++ /dev/null @@ -1,211 +0,0 @@ -html = new HtmlHandler(); - } - - /** - * @return ResponseInterface|PromiseInterface|\Generator - * Returns a response, a Promise which eventually fulfills with a - * response or a Generator which eventually returns a response. This - * method never throws or resolves a rejected promise. If the next - * handler fails to return a valid response, it will be turned into a - * valid error response before returning. - * @throws void - */ - public function __invoke(ServerRequestInterface $request, callable $next) - { - try { - $response = $next($request); - } catch (\Throwable $e) { - return $this->errorInvalidException($e); - } - - if ($response instanceof ResponseInterface) { - return $response; - } elseif ($response instanceof PromiseInterface) { - return $response->then(function ($response) { - if ($response instanceof ResponseInterface) { - return $response; - } else { - return $this->errorInvalidResponse($response); - } - }, function ($e) { - // Promise rejected, always a `\Throwable` as of Promise v3 - assert($e instanceof \Throwable || !\method_exists(PromiseInterface::class, 'catch')); // @phpstan-ignore-line - - if ($e instanceof \Throwable) { - return $this->errorInvalidException($e); - } else { // @phpstan-ignore-line - // @phpstan-ignore-next-line - return $this->errorInvalidResponse(\React\Promise\reject($e)); // @codeCoverageIgnore - } - }); - } elseif ($response instanceof \Generator) { - return $this->coroutine($response); - } else { - return $this->errorInvalidResponse($response); - } - } - - private function coroutine(\Generator $generator): \Generator - { - do { - try { - if (!$generator->valid()) { - $response = $generator->getReturn(); - if ($response instanceof ResponseInterface) { - return $response; - } else { - return $this->errorInvalidResponse($response); - } - } - } catch (\Throwable $e) { - return $this->errorInvalidException($e); - } - - $promise = $generator->current(); - if (!$promise instanceof PromiseInterface) { - $gref = new \ReflectionGenerator($generator); - - return $this->errorInvalidCoroutine( - $promise, - $gref->getExecutingFile(), - $gref->getExecutingLine() - ); - } - - try { - $next = yield $promise; - } catch (\Throwable $e) { - try { - $generator->throw($e); - continue; - } catch (\Throwable $e) { - return $this->errorInvalidException($e); - } - } - - try { - $generator->send($next); - } catch (\Throwable $e) { - return $this->errorInvalidException($e); - } - } while (true); - } // @codeCoverageIgnore - - /** @internal */ - public function requestNotFound(): ResponseInterface - { - return $this->htmlResponse( - Response::STATUS_NOT_FOUND, - 'Page Not Found', - 'Please check the URL in the address bar and try again.' - ); - } - - /** - * @internal - * @param list $allowedMethods - */ - public function requestMethodNotAllowed(array $allowedMethods): ResponseInterface - { - $methods = \implode('/', \array_map(function (string $method) { return '' . $method . ''; }, $allowedMethods)); - - return $this->htmlResponse( - Response::STATUS_METHOD_NOT_ALLOWED, - 'Method Not Allowed', - 'Please check the URL in the address bar and try again with ' . $methods . ' request.' - )->withHeader('Allow', \implode(', ', $allowedMethods)); - } - - /** @internal */ - public function requestProxyUnsupported(): ResponseInterface - { - return $this->htmlResponse( - Response::STATUS_BAD_REQUEST, - 'Proxy Requests Not Allowed', - 'Please check your settings and retry.' - ); - } - - private function errorInvalidException(\Throwable $e): ResponseInterface - { - $where = ' in ' . $this->where($e->getFile(), $e->getLine()); - $message = '' . $this->html->escape($e->getMessage()) . ''; - - return $this->htmlResponse( - Response::STATUS_INTERNAL_SERVER_ERROR, - 'Internal Server Error', - 'The requested page failed to load, please try again later.', - 'Expected request handler to return ' . ResponseInterface::class . ' but got uncaught ' . \get_class($e) . ' with message ' . $message . $where . '.' - ); - } - - /** @param mixed $value */ - private function errorInvalidResponse($value): ResponseInterface - { - return $this->htmlResponse( - Response::STATUS_INTERNAL_SERVER_ERROR, - 'Internal Server Error', - 'The requested page failed to load, please try again later.', - 'Expected request handler to return ' . ResponseInterface::class . ' but got ' . $this->describeType($value) . '.' - ); - } - - /** @param mixed $value */ - private function errorInvalidCoroutine($value, string $file, int $line): ResponseInterface - { - $where = ' near or before '. $this->where($file, $line) . '.'; - - return $this->htmlResponse( - Response::STATUS_INTERNAL_SERVER_ERROR, - 'Internal Server Error', - 'The requested page failed to load, please try again later.', - 'Expected request handler to yield ' . PromiseInterface::class . ' but got ' . $this->describeType($value) . '' . $where - ); - } - - private function where(string $file, int $line): string - { - return '' . \basename($file) . ':' . $line . ''; - } - - private function htmlResponse(int $statusCode, string $title, string ...$info): ResponseInterface - { - return $this->html->statusResponse( - $statusCode, - 'Error ' . $statusCode . ': ' .$title, - $title, - \implode('', \array_map(function (string $info) { return "

$info

\n"; }, $info)) - ); - } - - /** @param mixed $value */ - private function describeType($value): string - { - if ($value === null) { - return 'null'; - } elseif (\is_scalar($value) && !\is_string($value)) { - return \var_export($value, true); - } - return \is_object($value) ? \get_class($value) : \gettype($value); - } -} diff --git a/vendor/clue/framework-x/src/FilesystemHandler.php b/vendor/clue/framework-x/src/FilesystemHandler.php deleted file mode 100644 index 567b53c..0000000 --- a/vendor/clue/framework-x/src/FilesystemHandler.php +++ /dev/null @@ -1,130 +0,0 @@ - - */ - private $mimetypes = array( - 'atom' => 'application/atom+xml', - 'bz2' => 'application/x-bzip2', - 'css' => 'text/css', - 'gif' => 'image/gif', - 'gz' => 'application/gzip', - 'htm' => 'text/html', - 'html' => 'text/html', - 'ico' => 'image/x-icon', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'js' => 'text/javascript', - 'json' => 'application/json', - 'pdf' => 'application/pdf', - 'png' => 'image/png', - 'rss' => 'application/rss+xml', - 'svg' => 'image/svg+xml', - 'tar' => 'application/x-tar', - 'xml' => 'application/xml', - 'zip' => 'application/zip', - ); - - /** - * Assign default MIME type to send in `Content-Type` response header (same as nginx/Apache) - * - * @var string - * @see self::$mimetypes - */ - private $defaultMimetype = 'text/plain'; - - /** @var ErrorHandler */ - private $errorHandler; - - /** @var HtmlHandler */ - private $html; - - public function __construct(string $root) - { - $this->root = $root; - $this->errorHandler = new ErrorHandler(); - $this->html = new HtmlHandler(); - } - - public function __invoke(ServerRequestInterface $request): ResponseInterface - { - $local = $request->getAttribute('path', ''); - assert(\is_string($local)); - $path = \rtrim($this->root . '/' . $local, '/'); - - // local path should not contain "./", "../", "//" or null bytes or start with slash - $valid = !\preg_match('#(?:^|/)\.\.?(?:$|/)|^/|//|\x00#', $local); - - \clearstatcache(); - if ($valid && \is_dir($path)) { - if ($local !== '' && \substr($local, -1) !== '/') { - return (new RedirectHandler(\basename($path) . '/'))(); - } - - $response = '' . $this->html->escape($local === '' ? '/' : $local) . '' . "\n
    \n"; - - if ($local !== '') { - $response .= '
  • ../
  • ' . "\n"; - } - - $files = \scandir($path); - // @phpstan-ignore-next-line TODO handle error if directory can not be accessed - foreach ($files as $file) { - if ($file === '.' || $file === '..') { - continue; - } - - $dir = \is_dir($path . '/' . $file) ? '/' : ''; - $response .= '
  • ' . $this->html->escape($file) . $dir . '
  • ' . "\n"; - } - $response .= '
' . "\n"; - - return Response::html( - $response - ); - } elseif ($valid && \is_file($path)) { - if ($local !== '' && \substr($local, -1) === '/') { - return (new RedirectHandler('../' . \basename($path)))(); - } - - // Assign MIME type based on file extension (same as nginx/Apache) or fall back to given default otherwise. - // Browsers are pretty good at figuring out the correct type if no charset attribute is given. - $ext = \strtolower(\substr($path, \strrpos($path, '.') + 1)); - $headers = [ - 'Content-Type' => $this->mimetypes[$ext] ?? $this->defaultMimetype - ]; - - $stat = @\stat($path); - if ($stat !== false) { - $headers['Last-Modified'] = \gmdate('D, d M Y H:i:s', $stat['mtime']) . ' GMT'; - - if ($request->getHeaderLine('If-Modified-Since') === $headers['Last-Modified']) { - return new Response(Response::STATUS_NOT_MODIFIED); - } - } - - return new Response( - Response::STATUS_OK, - $headers, - \file_get_contents($path) // @phpstan-ignore-line TODO handle error if file can not be accessed - ); - } else { - return $this->errorHandler->requestNotFound(); - } - } -} diff --git a/vendor/clue/framework-x/src/Io/FiberHandler.php b/vendor/clue/framework-x/src/Io/FiberHandler.php deleted file mode 100644 index 07dc713..0000000 --- a/vendor/clue/framework-x/src/Io/FiberHandler.php +++ /dev/null @@ -1,67 +0,0 @@ -|\Generator - * Returns a `ResponseInterface` from the next request handler in the - * chain. If the next request handler returns immediately, this method - * will return immediately. If the next request handler suspends the - * fiber (see `await()`), this method will return a `PromiseInterface` - * that is fulfilled with a `ResponseInterface` when the fiber is - * terminated successfully. If the next request handler returns a - * promise, this method will return a promise that follows its - * resolution. If the next request handler returns a Generator-based - * coroutine, this method returns a `Generator`. This method never - * throws or resolves a rejected promise. If the handler fails, it will - * be turned into a valid error response before returning. - * @throws void - */ - public function __invoke(ServerRequestInterface $request, callable $next) - { - $deferred = null; - $fiber = new \Fiber(function () use ($request, $next, &$deferred) { - $response = $next($request); - assert($response instanceof ResponseInterface || $response instanceof PromiseInterface || $response instanceof \Generator); - - // if the next request handler returns immediately, the fiber can terminate immediately without using a Deferred - // if the next request handler suspends the fiber, we only reach this point after resuming the fiber, so the code below will have assigned a Deferred - /** @var ?Deferred $deferred */ - if ($deferred !== null) { - assert($response instanceof ResponseInterface); - $deferred->resolve($response); - } - - return $response; - }); - - /** @throws void because the next handler will always be an `ErrorHandler` */ - $fiber->start(); - if ($fiber->isTerminated()) { - /** @throws void because fiber is known to have terminated successfully */ - /** @var ResponseInterface|PromiseInterface|\Generator */ - return $fiber->getReturn(); - } - - $deferred = new Deferred(); - return $deferred->promise(); - } -} diff --git a/vendor/clue/framework-x/src/Io/HtmlHandler.php b/vendor/clue/framework-x/src/Io/HtmlHandler.php deleted file mode 100644 index 0e87e65..0000000 --- a/vendor/clue/framework-x/src/Io/HtmlHandler.php +++ /dev/null @@ -1,65 +0,0 @@ - - - -$title - - - -
-

$statusCode

-$subtitle -$info
- - - -HTML; - - return new Response( - $statusCode, - [ - 'Content-Type' => 'text/html; charset=utf-8', - 'Content-Security-Policy' => "style-src 'nonce-$nonce'; img-src 'self'; default-src 'none'" - ], - $html - ); - } - - public function escape(string $s): string - { - return (string) \preg_replace_callback( - '/[\x00-\x1F]+/', - function (array $match): string { - return '' . \addcslashes($match[0], "\x00..\xff") . ''; - }, - (string) \preg_replace( - '/(^| ) |(?: $)/', - '$1 ', - \htmlspecialchars($s, \ENT_NOQUOTES | \ENT_SUBSTITUTE | \ENT_DISALLOWED, 'utf-8') - ) - ); - } -} diff --git a/vendor/clue/framework-x/src/Io/LogStreamHandler.php b/vendor/clue/framework-x/src/Io/LogStreamHandler.php deleted file mode 100644 index 8c93349..0000000 --- a/vendor/clue/framework-x/src/Io/LogStreamHandler.php +++ /dev/null @@ -1,95 +0,0 @@ -isAbsolutePath($path))) { - throw new \InvalidArgumentException( - 'Unable to open log file "' . \addslashes($path) . '": Invalid path given' - ); - } - - $errstr = ''; - \set_error_handler(function (int $_, string $error) use (&$errstr): bool { - // Match errstr from PHP's warning message. - // fopen(/dev/not-a-valid-path): Failed to open stream: Permission denied - $errstr = \preg_replace('#.*: #', '', $error); - - return true; - }); - - $stream = \fopen($path, 'ae'); - - // try to fstat($stream) to see if this points to /dev/null (skip on Windows) - // @codeCoverageIgnoreStart - $stat = false; - if ($stream !== false && \DIRECTORY_SEPARATOR !== '\\') { - if (\strtolower($path) === 'php://output') { - // php://output doesn't support stat, so assume php://output will go to php://stdout - $stdout = \defined('STDOUT') ? \STDOUT : \fopen('php://stdout', 'w'); - if (\is_resource($stdout)) { - $stat = \fstat($stdout); - } else { - // STDOUT can not be opened => assume piping to /dev/null - $stream = null; - } - } else { - $stat = \fstat($stream); - } - - // close stream if it points to /dev/null - if ($stat !== false && $stat === \stat('/dev/null')) { - $stream = null; - } - } - // @codeCoverageIgnoreEnd - - \restore_error_handler(); - - if ($stream === false) { - throw new \RuntimeException( - 'Unable to open log file "' . $path . '": ' . $errstr - ); - } - - $this->stream = $stream; - } - - public function isDevNull(): bool - { - return $this->stream === null; - } - - public function log(string $message): void - { - // nothing to do if we're writing to /dev/null - if ($this->stream === null) { - return; // @codeCoverageIgnore - } - - $time = \microtime(true); - $prefix = \date('Y-m-d H:i:s', (int) $time) . \sprintf('.%03d ', (int) (($time - (int) $time) * 1e3)); - - $ret = \fwrite($this->stream, $prefix . $message . \PHP_EOL); - assert(\is_int($ret)); - } - - private function isAbsolutePath(string $path): bool - { - return \DIRECTORY_SEPARATOR !== '\\' ? \substr($path, 0, 1) === '/' : (bool) \preg_match('#^[A-Z]:[/\\\\]#i', $path); - } -} diff --git a/vendor/clue/framework-x/src/Io/MiddlewareHandler.php b/vendor/clue/framework-x/src/Io/MiddlewareHandler.php deleted file mode 100644 index dd62f01..0000000 --- a/vendor/clue/framework-x/src/Io/MiddlewareHandler.php +++ /dev/null @@ -1,40 +0,0 @@ - $handlers */ - private $handlers; - - /** @param list $handlers */ - public function __construct(array $handlers) - { - assert(count($handlers) >= 2); - - $this->handlers = $handlers; - } - - /** @return mixed */ - public function __invoke(ServerRequestInterface $request) - { - return $this->call($request, 0); - } - - /** @return mixed */ - private function call(ServerRequestInterface $request, int $position) - { - if (!isset($this->handlers[$position + 2])) { - return $this->handlers[$position]($request, $this->handlers[$position + 1]); - } - - return $this->handlers[$position]($request, function (ServerRequestInterface $request) use ($position) { - return $this->call($request, $position + 1); - }); - } -} diff --git a/vendor/clue/framework-x/src/Io/ReactiveHandler.php b/vendor/clue/framework-x/src/Io/ReactiveHandler.php deleted file mode 100644 index 579eb8a..0000000 --- a/vendor/clue/framework-x/src/Io/ReactiveHandler.php +++ /dev/null @@ -1,91 +0,0 @@ -logger = new LogStreamHandler('php://output'); - $this->listenAddress = $listenAddress ?? '127.0.0.1:8080'; - } - - public function run(callable $handler): void - { - $socket = new SocketServer($this->listenAddress); - - // create HTTP server, automatically start new fiber for each request on PHP 8.1+ - $http = new HttpServer(...(\PHP_VERSION_ID >= 80100 ? [new FiberHandler(), $handler] : [$handler])); - $http->listen($socket); - - $logger = $this->logger; - $logger->log('Listening on ' . \str_replace('tcp:', 'http:', (string) $socket->getAddress())); - - $http->on('error', static function (\Exception $e) use ($logger): void { - $logger->log('HTTP error: ' . $e->getMessage()); - }); - - // @codeCoverageIgnoreStart - try { - Loop::addSignal(\defined('SIGINT') ? \SIGINT : 2, $f1 = static function () use ($socket, $logger): void { - if (\PHP_VERSION_ID >= 70200 && \stream_isatty(\STDIN)) { - echo "\r"; - } - $logger->log('Received SIGINT, stopping loop'); - - $socket->close(); - Loop::stop(); - }); - Loop::addSignal(\defined('SIGTERM') ? \SIGTERM : 15, $f2 = static function () use ($socket, $logger): void { - $logger->log('Received SIGTERM, stopping loop'); - - $socket->close(); - Loop::stop(); - }); - } catch (\BadMethodCallException $e) { - $logger->log('Notice: No signal handler support, installing ext-ev or ext-pcntl recommended for production use.'); - } - // @codeCoverageIgnoreEnd - - do { - Loop::run(); - - if ($socket->getAddress() !== null) { - // Fiber compatibility mode for PHP < 8.1: Restart loop as long as socket is available - $logger->log('Warning: Loop restarted. Upgrade to react/async v4 recommended for production use.'); - } else { - break; - } - } while (true); - - // remove signal handlers when loop stops (if registered) - Loop::removeSignal(\defined('SIGINT') ? \SIGINT : 2, $f1 ?? 'printf'); - Loop::removeSignal(\defined('SIGTERM') ? \SIGTERM : 15, $f2 ?? 'printf'); - } -} diff --git a/vendor/clue/framework-x/src/Io/RedirectHandler.php b/vendor/clue/framework-x/src/Io/RedirectHandler.php deleted file mode 100644 index 3924ccb..0000000 --- a/vendor/clue/framework-x/src/Io/RedirectHandler.php +++ /dev/null @@ -1,48 +0,0 @@ -= 400) { - throw new \InvalidArgumentException('Invalid redirect status code given'); - } - - $this->target = $target; - $this->code = $redirectStatusCode; - $this->reason = \ucwords((new Response($redirectStatusCode))->getReasonPhrase()) ?: 'Redirect'; - $this->html = new HtmlHandler(); - } - - public function __invoke(): ResponseInterface - { - $url = $this->html->escape($this->target); - - return $this->html->statusResponse( - $this->code, - 'Redirecting to ' . $url, - $this->reason, - "

Redirecting to $url...

\n" - )->withHeader('Location', $this->target); - } -} diff --git a/vendor/clue/framework-x/src/Io/RouteHandler.php b/vendor/clue/framework-x/src/Io/RouteHandler.php deleted file mode 100644 index 2b0cf1c..0000000 --- a/vendor/clue/framework-x/src/Io/RouteHandler.php +++ /dev/null @@ -1,116 +0,0 @@ -routeCollector = new RouteCollector(new RouteParser(), new RouteGenerator()); - $this->errorHandler = new ErrorHandler(); - $this->container = $container ?? new Container(); - } - - /** - * @param string[] $methods - * @param string $route - * @param callable|class-string $handler - * @param callable|class-string ...$handlers - */ - public function map(array $methods, string $route, $handler, ...$handlers): void - { - if ($handlers) { - \array_unshift($handlers, $handler); - \end($handlers); - } else { - $handlers = [$handler]; - } - - $last = key($handlers); - $container = $this->container; - foreach ($handlers as $i => $handler) { - if ($handler instanceof Container && $i !== $last) { - $container = $handler; - unset($handlers[$i]); - } elseif ($handler instanceof AccessLogHandler || $handler === AccessLogHandler::class) { - throw new \TypeError('AccessLogHandler may currently only be passed as a global middleware'); - } elseif (!\is_callable($handler)) { - $handlers[$i] = $container->callable($handler); - } - } - - /** @var non-empty-array $handlers */ - $handler = \count($handlers) > 1 ? new MiddlewareHandler(array_values($handlers)) : \reset($handlers); - $this->routeDispatcher = null; - $this->routeCollector->addRoute($methods, $route, $handler); - } - - /** - * @return ResponseInterface|PromiseInterface|\Generator - */ - public function __invoke(ServerRequestInterface $request) - { - $target = $request->getRequestTarget(); - if ($target[0] !== '/' && $target !== '*') { - return $this->errorHandler->requestProxyUnsupported(); - } elseif ($target !== '*') { - $target = $request->getUri()->getPath(); - } - - if ($this->routeDispatcher === null) { - $this->routeDispatcher = new RouteDispatcher($this->routeCollector->getData()); - } - - $routeInfo = $this->routeDispatcher->dispatch($request->getMethod(), $target); - assert(\is_array($routeInfo) && isset($routeInfo[0])); - - // happy path: matching route found, assign route attributes and invoke request handler - if ($routeInfo[0] === \FastRoute\Dispatcher::FOUND) { - $handler = $routeInfo[1]; - $vars = $routeInfo[2]; - - foreach ($vars as $key => $value) { - $request = $request->withAttribute($key, rawurldecode($value)); - } - - return $handler($request); - } - - // no matching route found: report error `404 Not Found` - if ($routeInfo[0] === \FastRoute\Dispatcher::NOT_FOUND) { - return $this->errorHandler->requestNotFound(); - } - - // unexpected request method for route: report error `405 Method Not Allowed` - assert($routeInfo[0] === \FastRoute\Dispatcher::METHOD_NOT_ALLOWED); - assert(\is_array($routeInfo[1]) && \count($routeInfo[1]) > 0); - - return $this->errorHandler->requestMethodNotAllowed($routeInfo[1]); - } -} diff --git a/vendor/clue/framework-x/src/Io/SapiHandler.php b/vendor/clue/framework-x/src/Io/SapiHandler.php deleted file mode 100644 index d3faf27..0000000 --- a/vendor/clue/framework-x/src/Io/SapiHandler.php +++ /dev/null @@ -1,164 +0,0 @@ -requestFromGlobals(); - - $response = $handler($request); - - if ($response instanceof ResponseInterface) { - $this->sendResponse($response); - } elseif ($response instanceof PromiseInterface) { - /** @var PromiseInterface $response */ - $response->then(function (ResponseInterface $response): void { - $this->sendResponse($response); - }); - } - - Loop::run(); - } - - public function requestFromGlobals(): ServerRequestInterface - { - $host = null; - $headers = array(); - // @codeCoverageIgnoreStart - if (\function_exists('getallheaders')) { - $headers = \getallheaders(); - $host = \array_change_key_case($headers, \CASE_LOWER)['host'] ?? null; - } else { - foreach ($_SERVER as $key => $value) { - if (\strpos($key, 'HTTP_') === 0) { - $key = str_replace(' ', '-', \ucwords(\strtolower(\str_replace('_', ' ', \substr($key, 5))))); - $headers[$key] = $value; - - if ($host === null && $key === 'Host') { - $host = $value; - } - } - } - } - // @codeCoverageIgnoreEnd - - $target = ($_SERVER['REQUEST_URI'] ?? '/'); - $url = $target; - if (($target[0] ?? '/') === '/' || $target === '*') { - $url = (($_SERVER['HTTPS'] ?? null) === 'on' ? 'https://' : 'http://') . ($host ?? 'localhost') . ($target === '*' ? '' : $target); - } - - $body = file_get_contents('php://input'); - assert(\is_string($body)); - - $request = new ServerRequest( - $_SERVER['REQUEST_METHOD'] ?? 'GET', - $url, - $headers, - $body, - substr($_SERVER['SERVER_PROTOCOL'] ?? 'http/1.1', 5), - $_SERVER - ); - if ($host === null) { - $request = $request->withoutHeader('Host'); - } - if (isset($target[0]) && $target[0] !== '/') { - $request = $request->withRequestTarget($target); - } - $request = $request->withParsedBody($_POST); - - // Content-Length / Content-Type are special <3 - if ($request->getHeaderLine('Content-Length') === '') { - $request = $request->withoutHeader('Content-Length'); - } - if ($request->getHeaderLine('Content-Type') === '' && !isset($_SERVER['HTTP_CONTENT_TYPE'])) { - $request = $request->withoutHeader('Content-Type'); - } - - return $request; - } - - /** - * @param ResponseInterface $response - */ - public function sendResponse(ResponseInterface $response): void - { - $status = $response->getStatusCode(); - $body = $response->getBody(); - - if ($status === Response::STATUS_NO_CONTENT) { - // `204 No Content` MUST NOT include "Content-Length" response header - $response = $response->withoutHeader('Content-Length'); - } elseif (!$response->hasHeader('Content-Length') && $body->getSize() !== null && ($status !== Response::STATUS_NOT_MODIFIED || $body->getSize() !== 0)) { - // automatically assign "Content-Length" response header if known and not already present - $response = $response->withHeader('Content-Length', (string) $body->getSize()); - } - - // remove default "Content-Type" header set by PHP (default_mimetype) - if (!$response->hasHeader('Content-Type')) { - header('Content-Type:'); - header_remove('Content-Type'); - } - - // send all headers without applying default "; charset=utf-8" set by PHP (default_charset) - $old = ini_get('default_charset'); - assert(\is_string($old)); - ini_set('default_charset', ''); - foreach ($response->getHeaders() as $name => $values) { - foreach ($values as $value) { - header($name . ': ' . $value, false); - } - } - ini_set('default_charset', $old); - - header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status . ' ' . $response->getReasonPhrase()); - - if (($_SERVER['REQUEST_METHOD'] ?? '') === 'HEAD' || $status === Response::STATUS_NO_CONTENT || $status === Response::STATUS_NOT_MODIFIED) { - $body->close(); - return; - } - - if ($body instanceof ReadableStreamInterface) { - // try to disable nginx buffering (http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering) - if (isset($_SERVER['SERVER_SOFTWARE']) && strpos($_SERVER['SERVER_SOFTWARE'], 'nginx') === 0) { - header('X-Accel-Buffering: no'); - } - - // clear output buffer to show streaming output (default in cli-server) - if (\PHP_SAPI === 'cli-server') { - \ob_end_flush(); // @codeCoverageIgnore - } - - // flush data whenever stream reports one data chunk - $body->on('data', function ($chunk) { - echo $chunk; - flush(); - }); - } else { - echo $body; - } - } -} diff --git a/vendor/fig/http-message-util/.gitignore b/vendor/fig/http-message-util/.gitignore deleted file mode 100644 index 48b8bf9..0000000 --- a/vendor/fig/http-message-util/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vendor/ diff --git a/vendor/fig/http-message-util/CHANGELOG.md b/vendor/fig/http-message-util/CHANGELOG.md deleted file mode 100644 index 1a02e54..0000000 --- a/vendor/fig/http-message-util/CHANGELOG.md +++ /dev/null @@ -1,147 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file, in reverse chronological order by release. - -## 1.1.5 - 2020-11-24 - -### Added - -- [#19](https://github.com/php-fig/http-message-util/pull/19) adds support for PHP 8. - -### Changed - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 1.1.4 - 2020-02-05 - -### Added - -- Nothing. - -### Changed - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- [#15](https://github.com/php-fig/http-message-util/pull/15) removes the dependency on psr/http-message, as it is not technically necessary for usage of this package. - -### Fixed - -- Nothing. - -## 1.1.3 - 2018-11-19 - -### Added - -- [#10](https://github.com/php-fig/http-message-util/pull/10) adds the constants `StatusCodeInterface::STATUS_EARLY_HINTS` (103) and - `StatusCodeInterface::STATUS_TOO_EARLY` (425). - -### Changed - -- Nothing. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 1.1.2 - 2017-02-09 - -### Added - -- [#4](https://github.com/php-fig/http-message-util/pull/4) adds the constant - `StatusCodeInterface::STATUS_MISDIRECTED_REQUEST` (421). - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 1.1.1 - 2017-02-06 - -### Added - -- [#3](https://github.com/php-fig/http-message-util/pull/3) adds the constant - `StatusCodeInterface::STATUS_IM_A_TEAPOT` (418). - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 1.1.0 - 2016-09-19 - -### Added - -- [#1](https://github.com/php-fig/http-message-util/pull/1) adds - `Fig\Http\Message\StatusCodeInterface`, with constants named after common - status reason phrases, with values indicating the status codes themselves. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. - -## 1.0.0 - 2017-08-05 - -### Added - -- Adds `Fig\Http\Message\RequestMethodInterface`, with constants covering the - most common HTTP request methods as specified by the IETF. - -### Deprecated - -- Nothing. - -### Removed - -- Nothing. - -### Fixed - -- Nothing. diff --git a/vendor/fig/http-message-util/LICENSE b/vendor/fig/http-message-util/LICENSE deleted file mode 100644 index e2fa347..0000000 --- a/vendor/fig/http-message-util/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2016 PHP Framework Interoperability Group - -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/fig/http-message-util/README.md b/vendor/fig/http-message-util/README.md deleted file mode 100644 index ea5b5aa..0000000 --- a/vendor/fig/http-message-util/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# PSR Http Message Util - -This repository holds utility classes and constants to facilitate common -operations of [PSR-7](https://www.php-fig.org/psr/psr-7/); the primary purpose is -to provide constants for referring to request methods, response status codes and -messages, and potentially common headers. - -Implementation of PSR-7 interfaces is **not** within the scope of this package. - -## Installation - -Install by adding the package as a [Composer](https://getcomposer.org) -requirement: - -```bash -$ composer require fig/http-message-util -``` diff --git a/vendor/fig/http-message-util/composer.json b/vendor/fig/http-message-util/composer.json deleted file mode 100644 index 8645893..0000000 --- a/vendor/fig/http-message-util/composer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "fig/http-message-util", - "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", - "keywords": ["psr", "psr-7", "http", "http-message", "request", "response"], - "license": "MIT", - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "require": { - "php": "^5.3 || ^7.0 || ^8.0" - }, - "suggest": { - "psr/http-message": "The package containing the PSR-7 interfaces" - }, - "autoload": { - "psr-4": { - "Fig\\Http\\Message\\": "src/" - } - }, - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - } -} diff --git a/vendor/fig/http-message-util/src/RequestMethodInterface.php b/vendor/fig/http-message-util/src/RequestMethodInterface.php deleted file mode 100644 index 97d9a93..0000000 --- a/vendor/fig/http-message-util/src/RequestMethodInterface.php +++ /dev/null @@ -1,34 +0,0 @@ - - * class RequestFactory implements RequestMethodInterface - * { - * public static function factory( - * $uri = '/', - * $method = self::METHOD_GET, - * $data = [] - * ) { - * } - * } - * - */ -interface RequestMethodInterface -{ - const METHOD_HEAD = 'HEAD'; - const METHOD_GET = 'GET'; - const METHOD_POST = 'POST'; - const METHOD_PUT = 'PUT'; - const METHOD_PATCH = 'PATCH'; - const METHOD_DELETE = 'DELETE'; - const METHOD_PURGE = 'PURGE'; - const METHOD_OPTIONS = 'OPTIONS'; - const METHOD_TRACE = 'TRACE'; - const METHOD_CONNECT = 'CONNECT'; -} diff --git a/vendor/fig/http-message-util/src/StatusCodeInterface.php b/vendor/fig/http-message-util/src/StatusCodeInterface.php deleted file mode 100644 index 99b7e78..0000000 --- a/vendor/fig/http-message-util/src/StatusCodeInterface.php +++ /dev/null @@ -1,107 +0,0 @@ - - * class ResponseFactory implements StatusCodeInterface - * { - * public function createResponse($code = self::STATUS_OK) - * { - * } - * } - * - */ -interface StatusCodeInterface -{ - // Informational 1xx - const STATUS_CONTINUE = 100; - const STATUS_SWITCHING_PROTOCOLS = 101; - const STATUS_PROCESSING = 102; - const STATUS_EARLY_HINTS = 103; - // Successful 2xx - const STATUS_OK = 200; - const STATUS_CREATED = 201; - const STATUS_ACCEPTED = 202; - const STATUS_NON_AUTHORITATIVE_INFORMATION = 203; - const STATUS_NO_CONTENT = 204; - const STATUS_RESET_CONTENT = 205; - const STATUS_PARTIAL_CONTENT = 206; - const STATUS_MULTI_STATUS = 207; - const STATUS_ALREADY_REPORTED = 208; - const STATUS_IM_USED = 226; - // Redirection 3xx - const STATUS_MULTIPLE_CHOICES = 300; - const STATUS_MOVED_PERMANENTLY = 301; - const STATUS_FOUND = 302; - const STATUS_SEE_OTHER = 303; - const STATUS_NOT_MODIFIED = 304; - const STATUS_USE_PROXY = 305; - const STATUS_RESERVED = 306; - const STATUS_TEMPORARY_REDIRECT = 307; - const STATUS_PERMANENT_REDIRECT = 308; - // Client Errors 4xx - const STATUS_BAD_REQUEST = 400; - const STATUS_UNAUTHORIZED = 401; - const STATUS_PAYMENT_REQUIRED = 402; - const STATUS_FORBIDDEN = 403; - const STATUS_NOT_FOUND = 404; - const STATUS_METHOD_NOT_ALLOWED = 405; - const STATUS_NOT_ACCEPTABLE = 406; - const STATUS_PROXY_AUTHENTICATION_REQUIRED = 407; - const STATUS_REQUEST_TIMEOUT = 408; - const STATUS_CONFLICT = 409; - const STATUS_GONE = 410; - const STATUS_LENGTH_REQUIRED = 411; - const STATUS_PRECONDITION_FAILED = 412; - const STATUS_PAYLOAD_TOO_LARGE = 413; - const STATUS_URI_TOO_LONG = 414; - const STATUS_UNSUPPORTED_MEDIA_TYPE = 415; - const STATUS_RANGE_NOT_SATISFIABLE = 416; - const STATUS_EXPECTATION_FAILED = 417; - const STATUS_IM_A_TEAPOT = 418; - const STATUS_MISDIRECTED_REQUEST = 421; - const STATUS_UNPROCESSABLE_ENTITY = 422; - const STATUS_LOCKED = 423; - const STATUS_FAILED_DEPENDENCY = 424; - const STATUS_TOO_EARLY = 425; - const STATUS_UPGRADE_REQUIRED = 426; - const STATUS_PRECONDITION_REQUIRED = 428; - const STATUS_TOO_MANY_REQUESTS = 429; - const STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; - const STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451; - // Server Errors 5xx - const STATUS_INTERNAL_SERVER_ERROR = 500; - const STATUS_NOT_IMPLEMENTED = 501; - const STATUS_BAD_GATEWAY = 502; - const STATUS_SERVICE_UNAVAILABLE = 503; - const STATUS_GATEWAY_TIMEOUT = 504; - const STATUS_VERSION_NOT_SUPPORTED = 505; - const STATUS_VARIANT_ALSO_NEGOTIATES = 506; - const STATUS_INSUFFICIENT_STORAGE = 507; - const STATUS_LOOP_DETECTED = 508; - const STATUS_NOT_EXTENDED = 510; - const STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511; -} diff --git a/vendor/nikic/fast-route/.gitignore b/vendor/nikic/fast-route/.gitignore deleted file mode 100644 index e378a07..0000000 --- a/vendor/nikic/fast-route/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/vendor/ -.idea/ - -# ignore lock file since we have no extra dependencies -composer.lock diff --git a/vendor/nikic/fast-route/.hhconfig b/vendor/nikic/fast-route/.hhconfig deleted file mode 100644 index 0c2153c..0000000 --- a/vendor/nikic/fast-route/.hhconfig +++ /dev/null @@ -1 +0,0 @@ -assume_php=false diff --git a/vendor/nikic/fast-route/.travis.yml b/vendor/nikic/fast-route/.travis.yml deleted file mode 100644 index 10f8381..0000000 --- a/vendor/nikic/fast-route/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -sudo: false -language: php - -php: - - 5.4 - - 5.5 - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - hhvm - -script: - - ./vendor/bin/phpunit - -before_install: - - travis_retry composer self-update - -install: - - composer install diff --git a/vendor/nikic/fast-route/FastRoute.hhi b/vendor/nikic/fast-route/FastRoute.hhi deleted file mode 100644 index 8d50738..0000000 --- a/vendor/nikic/fast-route/FastRoute.hhi +++ /dev/null @@ -1,126 +0,0 @@ -; - } - - class RouteCollector { - public function __construct(RouteParser $routeParser, DataGenerator $dataGenerator); - public function addRoute(mixed $httpMethod, string $route, mixed $handler): void; - public function getData(): array; - } - - class Route { - public function __construct(string $httpMethod, mixed $handler, string $regex, array $variables); - public function matches(string $str): bool; - } - - interface DataGenerator { - public function addRoute(string $httpMethod, array $routeData, mixed $handler); - public function getData(): array; - } - - interface Dispatcher { - const int NOT_FOUND = 0; - const int FOUND = 1; - const int METHOD_NOT_ALLOWED = 2; - public function dispatch(string $httpMethod, string $uri): array; - } - - function simpleDispatcher( - (function(RouteCollector): void) $routeDefinitionCallback, - shape( - ?'routeParser' => classname, - ?'dataGenerator' => classname, - ?'dispatcher' => classname, - ?'routeCollector' => classname, - ) $options = shape()): Dispatcher; - - function cachedDispatcher( - (function(RouteCollector): void) $routeDefinitionCallback, - shape( - ?'routeParser' => classname, - ?'dataGenerator' => classname, - ?'dispatcher' => classname, - ?'routeCollector' => classname, - ?'cacheDisabled' => bool, - ?'cacheFile' => string, - ) $options = shape()): Dispatcher; -} - -namespace FastRoute\DataGenerator { - abstract class RegexBasedAbstract implements \FastRoute\DataGenerator { - protected abstract function getApproxChunkSize(); - protected abstract function processChunk($regexToRoutesMap); - - public function addRoute(string $httpMethod, array $routeData, mixed $handler): void; - public function getData(): array; - } - - class CharCountBased extends RegexBasedAbstract { - protected function getApproxChunkSize(): int; - protected function processChunk(array $regexToRoutesMap): array; - } - - class GroupCountBased extends RegexBasedAbstract { - protected function getApproxChunkSize(): int; - protected function processChunk(array $regexToRoutesMap): array; - } - - class GroupPosBased extends RegexBasedAbstract { - protected function getApproxChunkSize(): int; - protected function processChunk(array $regexToRoutesMap): array; - } - - class MarkBased extends RegexBasedAbstract { - protected function getApproxChunkSize(): int; - protected function processChunk(array $regexToRoutesMap): array; - } -} - -namespace FastRoute\Dispatcher { - abstract class RegexBasedAbstract implements \FastRoute\Dispatcher { - protected abstract function dispatchVariableRoute(array $routeData, string $uri): array; - - public function dispatch(string $httpMethod, string $uri): array; - } - - class GroupPosBased extends RegexBasedAbstract { - public function __construct(array $data); - protected function dispatchVariableRoute(array $routeData, string $uri): array; - } - - class GroupCountBased extends RegexBasedAbstract { - public function __construct(array $data); - protected function dispatchVariableRoute(array $routeData, string $uri): array; - } - - class CharCountBased extends RegexBasedAbstract { - public function __construct(array $data); - protected function dispatchVariableRoute(array $routeData, string $uri): array; - } - - class MarkBased extends RegexBasedAbstract { - public function __construct(array $data); - protected function dispatchVariableRoute(array $routeData, string $uri): array; - } -} - -namespace FastRoute\RouteParser { - class Std implements \FastRoute\RouteParser { - const string VARIABLE_REGEX = <<<'REGEX' -\{ - \s* ([a-zA-Z][a-zA-Z0-9_]*) \s* - (?: - : \s* ([^{}]*(?:\{(?-1)\}[^{}]*)*) - )? -\} -REGEX; - const string DEFAULT_DISPATCH_REGEX = '[^/]+'; - public function parse(string $route): array; - } -} diff --git a/vendor/nikic/fast-route/LICENSE b/vendor/nikic/fast-route/LICENSE deleted file mode 100644 index 478e764..0000000 --- a/vendor/nikic/fast-route/LICENSE +++ /dev/null @@ -1,31 +0,0 @@ -Copyright (c) 2013 by Nikita Popov. - -Some rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * The names of the contributors may not be used to endorse or - promote products derived from this software without specific - prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/nikic/fast-route/README.md b/vendor/nikic/fast-route/README.md deleted file mode 100644 index 91bd466..0000000 --- a/vendor/nikic/fast-route/README.md +++ /dev/null @@ -1,313 +0,0 @@ -FastRoute - Fast request router for PHP -======================================= - -This library provides a fast implementation of a regular expression based router. [Blog post explaining how the -implementation works and why it is fast.][blog_post] - -Install -------- - -To install with composer: - -```sh -composer require nikic/fast-route -``` - -Requires PHP 5.4 or newer. - -Usage ------ - -Here's a basic usage example: - -```php -addRoute('GET', '/users', 'get_all_users_handler'); - // {id} must be a number (\d+) - $r->addRoute('GET', '/user/{id:\d+}', 'get_user_handler'); - // The /{title} suffix is optional - $r->addRoute('GET', '/articles/{id:\d+}[/{title}]', 'get_article_handler'); -}); - -// Fetch method and URI from somewhere -$httpMethod = $_SERVER['REQUEST_METHOD']; -$uri = $_SERVER['REQUEST_URI']; - -// Strip query string (?foo=bar) and decode URI -if (false !== $pos = strpos($uri, '?')) { - $uri = substr($uri, 0, $pos); -} -$uri = rawurldecode($uri); - -$routeInfo = $dispatcher->dispatch($httpMethod, $uri); -switch ($routeInfo[0]) { - case FastRoute\Dispatcher::NOT_FOUND: - // ... 404 Not Found - break; - case FastRoute\Dispatcher::METHOD_NOT_ALLOWED: - $allowedMethods = $routeInfo[1]; - // ... 405 Method Not Allowed - break; - case FastRoute\Dispatcher::FOUND: - $handler = $routeInfo[1]; - $vars = $routeInfo[2]; - // ... call $handler with $vars - break; -} -``` - -### Defining routes - -The routes are defined by calling the `FastRoute\simpleDispatcher()` function, which accepts -a callable taking a `FastRoute\RouteCollector` instance. The routes are added by calling -`addRoute()` on the collector instance: - -```php -$r->addRoute($method, $routePattern, $handler); -``` - -The `$method` is an uppercase HTTP method string for which a certain route should match. It -is possible to specify multiple valid methods using an array: - -```php -// These two calls -$r->addRoute('GET', '/test', 'handler'); -$r->addRoute('POST', '/test', 'handler'); -// Are equivalent to this one call -$r->addRoute(['GET', 'POST'], '/test', 'handler'); -``` - -By default the `$routePattern` uses a syntax where `{foo}` specifies a placeholder with name `foo` -and matching the regex `[^/]+`. To adjust the pattern the placeholder matches, you can specify -a custom pattern by writing `{bar:[0-9]+}`. Some examples: - -```php -// Matches /user/42, but not /user/xyz -$r->addRoute('GET', '/user/{id:\d+}', 'handler'); - -// Matches /user/foobar, but not /user/foo/bar -$r->addRoute('GET', '/user/{name}', 'handler'); - -// Matches /user/foo/bar as well -$r->addRoute('GET', '/user/{name:.+}', 'handler'); -``` - -Custom patterns for route placeholders cannot use capturing groups. For example `{lang:(en|de)}` -is not a valid placeholder, because `()` is a capturing group. Instead you can use either -`{lang:en|de}` or `{lang:(?:en|de)}`. - -Furthermore parts of the route enclosed in `[...]` are considered optional, so that `/foo[bar]` -will match both `/foo` and `/foobar`. Optional parts are only supported in a trailing position, -not in the middle of a route. - -```php -// This route -$r->addRoute('GET', '/user/{id:\d+}[/{name}]', 'handler'); -// Is equivalent to these two routes -$r->addRoute('GET', '/user/{id:\d+}', 'handler'); -$r->addRoute('GET', '/user/{id:\d+}/{name}', 'handler'); - -// Multiple nested optional parts are possible as well -$r->addRoute('GET', '/user[/{id:\d+}[/{name}]]', 'handler'); - -// This route is NOT valid, because optional parts can only occur at the end -$r->addRoute('GET', '/user[/{id:\d+}]/{name}', 'handler'); -``` - -The `$handler` parameter does not necessarily have to be a callback, it could also be a controller -class name or any other kind of data you wish to associate with the route. FastRoute only tells you -which handler corresponds to your URI, how you interpret it is up to you. - -#### Shorcut methods for common request methods - -For the `GET`, `POST`, `PUT`, `PATCH`, `DELETE` and `HEAD` request methods shortcut methods are available. For example: - -```php -$r->get('/get-route', 'get_handler'); -$r->post('/post-route', 'post_handler'); -``` - -Is equivalent to: - -```php -$r->addRoute('GET', '/get-route', 'get_handler'); -$r->addRoute('POST', '/post-route', 'post_handler'); -``` - -#### Route Groups - -Additionally, you can specify routes inside of a group. All routes defined inside a group will have a common prefix. - -For example, defining your routes as: - -```php -$r->addGroup('/admin', function (RouteCollector $r) { - $r->addRoute('GET', '/do-something', 'handler'); - $r->addRoute('GET', '/do-another-thing', 'handler'); - $r->addRoute('GET', '/do-something-else', 'handler'); -}); -``` - -Will have the same result as: - - ```php -$r->addRoute('GET', '/admin/do-something', 'handler'); -$r->addRoute('GET', '/admin/do-another-thing', 'handler'); -$r->addRoute('GET', '/admin/do-something-else', 'handler'); - ``` - -Nested groups are also supported, in which case the prefixes of all the nested groups are combined. - -### Caching - -The reason `simpleDispatcher` accepts a callback for defining the routes is to allow seamless -caching. By using `cachedDispatcher` instead of `simpleDispatcher` you can cache the generated -routing data and construct the dispatcher from the cached information: - -```php -addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); - $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); - $r->addRoute('GET', '/user/{name}', 'handler2'); -}, [ - 'cacheFile' => __DIR__ . '/route.cache', /* required */ - 'cacheDisabled' => IS_DEBUG_ENABLED, /* optional, enabled by default */ -]); -``` - -The second parameter to the function is an options array, which can be used to specify the cache -file location, among other things. - -### Dispatching a URI - -A URI is dispatched by calling the `dispatch()` method of the created dispatcher. This method -accepts the HTTP method and a URI. Getting those two bits of information (and normalizing them -appropriately) is your job - this library is not bound to the PHP web SAPIs. - -The `dispatch()` method returns an array whose first element contains a status code. It is one -of `Dispatcher::NOT_FOUND`, `Dispatcher::METHOD_NOT_ALLOWED` and `Dispatcher::FOUND`. For the -method not allowed status the second array element contains a list of HTTP methods allowed for -the supplied URI. For example: - - [FastRoute\Dispatcher::METHOD_NOT_ALLOWED, ['GET', 'POST']] - -> **NOTE:** The HTTP specification requires that a `405 Method Not Allowed` response include the -`Allow:` header to detail available methods for the requested resource. Applications using FastRoute -should use the second array element to add this header when relaying a 405 response. - -For the found status the second array element is the handler that was associated with the route -and the third array element is a dictionary of placeholder names to their values. For example: - - /* Routing against GET /user/nikic/42 */ - - [FastRoute\Dispatcher::FOUND, 'handler0', ['name' => 'nikic', 'id' => '42']] - -### Overriding the route parser and dispatcher - -The routing process makes use of three components: A route parser, a data generator and a -dispatcher. The three components adhere to the following interfaces: - -```php - 'FastRoute\\RouteParser\\Std', - 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', - 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', -]); -``` - -The above options array corresponds to the defaults. By replacing `GroupCountBased` by -`GroupPosBased` you could switch to a different dispatching strategy. - -### A Note on HEAD Requests - -The HTTP spec requires servers to [support both GET and HEAD methods][2616-511]: - -> The methods GET and HEAD MUST be supported by all general-purpose servers - -To avoid forcing users to manually register HEAD routes for each resource we fallback to matching an -available GET route for a given resource. The PHP web SAPI transparently removes the entity body -from HEAD responses so this behavior has no effect on the vast majority of users. - -However, implementers using FastRoute outside the web SAPI environment (e.g. a custom server) MUST -NOT send entity bodies generated in response to HEAD requests. If you are a non-SAPI user this is -*your responsibility*; FastRoute has no purview to prevent you from breaking HTTP in such cases. - -Finally, note that applications MAY always specify their own HEAD method route for a given -resource to bypass this behavior entirely. - -### Credits - -This library is based on a router that [Levi Morrison][levi] implemented for the Aerys server. - -A large number of tests, as well as HTTP compliance considerations, were provided by [Daniel Lowrey][rdlowrey]. - - -[2616-511]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1 "RFC 2616 Section 5.1.1" -[blog_post]: http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html -[levi]: https://github.com/morrisonlevi -[rdlowrey]: https://github.com/rdlowrey diff --git a/vendor/nikic/fast-route/composer.json b/vendor/nikic/fast-route/composer.json deleted file mode 100644 index fb446a2..0000000 --- a/vendor/nikic/fast-route/composer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "nikic/fast-route", - "description": "Fast request router for PHP", - "keywords": ["routing", "router"], - "license": "BSD-3-Clause", - "authors": [ - { - "name": "Nikita Popov", - "email": "nikic@php.net" - } - ], - "autoload": { - "psr-4": { - "FastRoute\\": "src/" - }, - "files": ["src/functions.php"] - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "^4.8.35|~5.7" - } -} diff --git a/vendor/nikic/fast-route/phpunit.xml b/vendor/nikic/fast-route/phpunit.xml deleted file mode 100644 index 3c807b6..0000000 --- a/vendor/nikic/fast-route/phpunit.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - ./test/ - - - - - - ./src/ - - - diff --git a/vendor/nikic/fast-route/psalm.xml b/vendor/nikic/fast-route/psalm.xml deleted file mode 100644 index 0dca5d7..0000000 --- a/vendor/nikic/fast-route/psalm.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/vendor/nikic/fast-route/src/BadRouteException.php b/vendor/nikic/fast-route/src/BadRouteException.php deleted file mode 100644 index 62262ec..0000000 --- a/vendor/nikic/fast-route/src/BadRouteException.php +++ /dev/null @@ -1,7 +0,0 @@ - $route) { - $suffixLen++; - $suffix .= "\t"; - - $regexes[] = '(?:' . $regex . '/(\t{' . $suffixLen . '})\t{' . ($count - $suffixLen) . '})'; - $routeMap[$suffix] = [$route->handler, $route->variables]; - } - - $regex = '~^(?|' . implode('|', $regexes) . ')$~'; - return ['regex' => $regex, 'suffix' => '/' . $suffix, 'routeMap' => $routeMap]; - } -} diff --git a/vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php b/vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php deleted file mode 100644 index 54d9a05..0000000 --- a/vendor/nikic/fast-route/src/DataGenerator/GroupCountBased.php +++ /dev/null @@ -1,30 +0,0 @@ - $route) { - $numVariables = count($route->variables); - $numGroups = max($numGroups, $numVariables); - - $regexes[] = $regex . str_repeat('()', $numGroups - $numVariables); - $routeMap[$numGroups + 1] = [$route->handler, $route->variables]; - - ++$numGroups; - } - - $regex = '~^(?|' . implode('|', $regexes) . ')$~'; - return ['regex' => $regex, 'routeMap' => $routeMap]; - } -} diff --git a/vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php b/vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php deleted file mode 100644 index fc4dc0a..0000000 --- a/vendor/nikic/fast-route/src/DataGenerator/GroupPosBased.php +++ /dev/null @@ -1,27 +0,0 @@ - $route) { - $regexes[] = $regex; - $routeMap[$offset] = [$route->handler, $route->variables]; - - $offset += count($route->variables); - } - - $regex = '~^(?:' . implode('|', $regexes) . ')$~'; - return ['regex' => $regex, 'routeMap' => $routeMap]; - } -} diff --git a/vendor/nikic/fast-route/src/DataGenerator/MarkBased.php b/vendor/nikic/fast-route/src/DataGenerator/MarkBased.php deleted file mode 100644 index 0aebed9..0000000 --- a/vendor/nikic/fast-route/src/DataGenerator/MarkBased.php +++ /dev/null @@ -1,27 +0,0 @@ - $route) { - $regexes[] = $regex . '(*MARK:' . $markName . ')'; - $routeMap[$markName] = [$route->handler, $route->variables]; - - ++$markName; - } - - $regex = '~^(?|' . implode('|', $regexes) . ')$~'; - return ['regex' => $regex, 'routeMap' => $routeMap]; - } -} diff --git a/vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php b/vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php deleted file mode 100644 index 6457290..0000000 --- a/vendor/nikic/fast-route/src/DataGenerator/RegexBasedAbstract.php +++ /dev/null @@ -1,186 +0,0 @@ -isStaticRoute($routeData)) { - $this->addStaticRoute($httpMethod, $routeData, $handler); - } else { - $this->addVariableRoute($httpMethod, $routeData, $handler); - } - } - - /** - * @return mixed[] - */ - public function getData() - { - if (empty($this->methodToRegexToRoutesMap)) { - return [$this->staticRoutes, []]; - } - - return [$this->staticRoutes, $this->generateVariableRouteData()]; - } - - /** - * @return mixed[] - */ - private function generateVariableRouteData() - { - $data = []; - foreach ($this->methodToRegexToRoutesMap as $method => $regexToRoutesMap) { - $chunkSize = $this->computeChunkSize(count($regexToRoutesMap)); - $chunks = array_chunk($regexToRoutesMap, $chunkSize, true); - $data[$method] = array_map([$this, 'processChunk'], $chunks); - } - return $data; - } - - /** - * @param int - * @return int - */ - private function computeChunkSize($count) - { - $numParts = max(1, round($count / $this->getApproxChunkSize())); - return (int) ceil($count / $numParts); - } - - /** - * @param mixed[] - * @return bool - */ - private function isStaticRoute($routeData) - { - return count($routeData) === 1 && is_string($routeData[0]); - } - - private function addStaticRoute($httpMethod, $routeData, $handler) - { - $routeStr = $routeData[0]; - - if (isset($this->staticRoutes[$httpMethod][$routeStr])) { - throw new BadRouteException(sprintf( - 'Cannot register two routes matching "%s" for method "%s"', - $routeStr, $httpMethod - )); - } - - if (isset($this->methodToRegexToRoutesMap[$httpMethod])) { - foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) { - if ($route->matches($routeStr)) { - throw new BadRouteException(sprintf( - 'Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"', - $routeStr, $route->regex, $httpMethod - )); - } - } - } - - $this->staticRoutes[$httpMethod][$routeStr] = $handler; - } - - private function addVariableRoute($httpMethod, $routeData, $handler) - { - list($regex, $variables) = $this->buildRegexForRoute($routeData); - - if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { - throw new BadRouteException(sprintf( - 'Cannot register two routes matching "%s" for method "%s"', - $regex, $httpMethod - )); - } - - $this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route( - $httpMethod, $handler, $regex, $variables - ); - } - - /** - * @param mixed[] - * @return mixed[] - */ - private function buildRegexForRoute($routeData) - { - $regex = ''; - $variables = []; - foreach ($routeData as $part) { - if (is_string($part)) { - $regex .= preg_quote($part, '~'); - continue; - } - - list($varName, $regexPart) = $part; - - if (isset($variables[$varName])) { - throw new BadRouteException(sprintf( - 'Cannot use the same placeholder "%s" twice', $varName - )); - } - - if ($this->regexHasCapturingGroups($regexPart)) { - throw new BadRouteException(sprintf( - 'Regex "%s" for parameter "%s" contains a capturing group', - $regexPart, $varName - )); - } - - $variables[$varName] = $varName; - $regex .= '(' . $regexPart . ')'; - } - - return [$regex, $variables]; - } - - /** - * @param string - * @return bool - */ - private function regexHasCapturingGroups($regex) - { - if (false === strpos($regex, '(')) { - // Needs to have at least a ( to contain a capturing group - return false; - } - - // Semi-accurate detection for capturing groups - return (bool) preg_match( - '~ - (?: - \(\?\( - | \[ [^\]\\\\]* (?: \\\\ . [^\]\\\\]* )* \] - | \\\\ . - ) (*SKIP)(*FAIL) | - \( - (?! - \? (?! <(?![!=]) | P< | \' ) - | \* - ) - ~x', - $regex - ); - } -} diff --git a/vendor/nikic/fast-route/src/Dispatcher.php b/vendor/nikic/fast-route/src/Dispatcher.php deleted file mode 100644 index 4ae72a3..0000000 --- a/vendor/nikic/fast-route/src/Dispatcher.php +++ /dev/null @@ -1,26 +0,0 @@ - 'value', ...]] - * - * @param string $httpMethod - * @param string $uri - * - * @return array - */ - public function dispatch($httpMethod, $uri); -} diff --git a/vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php b/vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php deleted file mode 100644 index ef1eec1..0000000 --- a/vendor/nikic/fast-route/src/Dispatcher/CharCountBased.php +++ /dev/null @@ -1,31 +0,0 @@ -staticRouteMap, $this->variableRouteData) = $data; - } - - protected function dispatchVariableRoute($routeData, $uri) - { - foreach ($routeData as $data) { - if (!preg_match($data['regex'], $uri . $data['suffix'], $matches)) { - continue; - } - - list($handler, $varNames) = $data['routeMap'][end($matches)]; - - $vars = []; - $i = 0; - foreach ($varNames as $varName) { - $vars[$varName] = $matches[++$i]; - } - return [self::FOUND, $handler, $vars]; - } - - return [self::NOT_FOUND]; - } -} diff --git a/vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php b/vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php deleted file mode 100644 index 493e7a9..0000000 --- a/vendor/nikic/fast-route/src/Dispatcher/GroupCountBased.php +++ /dev/null @@ -1,31 +0,0 @@ -staticRouteMap, $this->variableRouteData) = $data; - } - - protected function dispatchVariableRoute($routeData, $uri) - { - foreach ($routeData as $data) { - if (!preg_match($data['regex'], $uri, $matches)) { - continue; - } - - list($handler, $varNames) = $data['routeMap'][count($matches)]; - - $vars = []; - $i = 0; - foreach ($varNames as $varName) { - $vars[$varName] = $matches[++$i]; - } - return [self::FOUND, $handler, $vars]; - } - - return [self::NOT_FOUND]; - } -} diff --git a/vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php b/vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php deleted file mode 100644 index 498220e..0000000 --- a/vendor/nikic/fast-route/src/Dispatcher/GroupPosBased.php +++ /dev/null @@ -1,33 +0,0 @@ -staticRouteMap, $this->variableRouteData) = $data; - } - - protected function dispatchVariableRoute($routeData, $uri) - { - foreach ($routeData as $data) { - if (!preg_match($data['regex'], $uri, $matches)) { - continue; - } - - // find first non-empty match - for ($i = 1; '' === $matches[$i]; ++$i); - - list($handler, $varNames) = $data['routeMap'][$i]; - - $vars = []; - foreach ($varNames as $varName) { - $vars[$varName] = $matches[$i++]; - } - return [self::FOUND, $handler, $vars]; - } - - return [self::NOT_FOUND]; - } -} diff --git a/vendor/nikic/fast-route/src/Dispatcher/MarkBased.php b/vendor/nikic/fast-route/src/Dispatcher/MarkBased.php deleted file mode 100644 index 22eb09b..0000000 --- a/vendor/nikic/fast-route/src/Dispatcher/MarkBased.php +++ /dev/null @@ -1,31 +0,0 @@ -staticRouteMap, $this->variableRouteData) = $data; - } - - protected function dispatchVariableRoute($routeData, $uri) - { - foreach ($routeData as $data) { - if (!preg_match($data['regex'], $uri, $matches)) { - continue; - } - - list($handler, $varNames) = $data['routeMap'][$matches['MARK']]; - - $vars = []; - $i = 0; - foreach ($varNames as $varName) { - $vars[$varName] = $matches[++$i]; - } - return [self::FOUND, $handler, $vars]; - } - - return [self::NOT_FOUND]; - } -} diff --git a/vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php b/vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php deleted file mode 100644 index 206e879..0000000 --- a/vendor/nikic/fast-route/src/Dispatcher/RegexBasedAbstract.php +++ /dev/null @@ -1,88 +0,0 @@ -staticRouteMap[$httpMethod][$uri])) { - $handler = $this->staticRouteMap[$httpMethod][$uri]; - return [self::FOUND, $handler, []]; - } - - $varRouteData = $this->variableRouteData; - if (isset($varRouteData[$httpMethod])) { - $result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri); - if ($result[0] === self::FOUND) { - return $result; - } - } - - // For HEAD requests, attempt fallback to GET - if ($httpMethod === 'HEAD') { - if (isset($this->staticRouteMap['GET'][$uri])) { - $handler = $this->staticRouteMap['GET'][$uri]; - return [self::FOUND, $handler, []]; - } - if (isset($varRouteData['GET'])) { - $result = $this->dispatchVariableRoute($varRouteData['GET'], $uri); - if ($result[0] === self::FOUND) { - return $result; - } - } - } - - // If nothing else matches, try fallback routes - if (isset($this->staticRouteMap['*'][$uri])) { - $handler = $this->staticRouteMap['*'][$uri]; - return [self::FOUND, $handler, []]; - } - if (isset($varRouteData['*'])) { - $result = $this->dispatchVariableRoute($varRouteData['*'], $uri); - if ($result[0] === self::FOUND) { - return $result; - } - } - - // Find allowed methods for this URI by matching against all other HTTP methods as well - $allowedMethods = []; - - foreach ($this->staticRouteMap as $method => $uriMap) { - if ($method !== $httpMethod && isset($uriMap[$uri])) { - $allowedMethods[] = $method; - } - } - - foreach ($varRouteData as $method => $routeData) { - if ($method === $httpMethod) { - continue; - } - - $result = $this->dispatchVariableRoute($routeData, $uri); - if ($result[0] === self::FOUND) { - $allowedMethods[] = $method; - } - } - - // If there are no allowed methods the route simply does not exist - if ($allowedMethods) { - return [self::METHOD_NOT_ALLOWED, $allowedMethods]; - } - - return [self::NOT_FOUND]; - } -} diff --git a/vendor/nikic/fast-route/src/Route.php b/vendor/nikic/fast-route/src/Route.php deleted file mode 100644 index e1bf7dd..0000000 --- a/vendor/nikic/fast-route/src/Route.php +++ /dev/null @@ -1,47 +0,0 @@ -httpMethod = $httpMethod; - $this->handler = $handler; - $this->regex = $regex; - $this->variables = $variables; - } - - /** - * Tests whether this route matches the given string. - * - * @param string $str - * - * @return bool - */ - public function matches($str) - { - $regex = '~^' . $this->regex . '$~'; - return (bool) preg_match($regex, $str); - } -} diff --git a/vendor/nikic/fast-route/src/RouteCollector.php b/vendor/nikic/fast-route/src/RouteCollector.php deleted file mode 100644 index c1c1762..0000000 --- a/vendor/nikic/fast-route/src/RouteCollector.php +++ /dev/null @@ -1,152 +0,0 @@ -routeParser = $routeParser; - $this->dataGenerator = $dataGenerator; - $this->currentGroupPrefix = ''; - } - - /** - * Adds a route to the collection. - * - * The syntax used in the $route string depends on the used route parser. - * - * @param string|string[] $httpMethod - * @param string $route - * @param mixed $handler - */ - public function addRoute($httpMethod, $route, $handler) - { - $route = $this->currentGroupPrefix . $route; - $routeDatas = $this->routeParser->parse($route); - foreach ((array) $httpMethod as $method) { - foreach ($routeDatas as $routeData) { - $this->dataGenerator->addRoute($method, $routeData, $handler); - } - } - } - - /** - * Create a route group with a common prefix. - * - * All routes created in the passed callback will have the given group prefix prepended. - * - * @param string $prefix - * @param callable $callback - */ - public function addGroup($prefix, callable $callback) - { - $previousGroupPrefix = $this->currentGroupPrefix; - $this->currentGroupPrefix = $previousGroupPrefix . $prefix; - $callback($this); - $this->currentGroupPrefix = $previousGroupPrefix; - } - - /** - * Adds a GET route to the collection - * - * This is simply an alias of $this->addRoute('GET', $route, $handler) - * - * @param string $route - * @param mixed $handler - */ - public function get($route, $handler) - { - $this->addRoute('GET', $route, $handler); - } - - /** - * Adds a POST route to the collection - * - * This is simply an alias of $this->addRoute('POST', $route, $handler) - * - * @param string $route - * @param mixed $handler - */ - public function post($route, $handler) - { - $this->addRoute('POST', $route, $handler); - } - - /** - * Adds a PUT route to the collection - * - * This is simply an alias of $this->addRoute('PUT', $route, $handler) - * - * @param string $route - * @param mixed $handler - */ - public function put($route, $handler) - { - $this->addRoute('PUT', $route, $handler); - } - - /** - * Adds a DELETE route to the collection - * - * This is simply an alias of $this->addRoute('DELETE', $route, $handler) - * - * @param string $route - * @param mixed $handler - */ - public function delete($route, $handler) - { - $this->addRoute('DELETE', $route, $handler); - } - - /** - * Adds a PATCH route to the collection - * - * This is simply an alias of $this->addRoute('PATCH', $route, $handler) - * - * @param string $route - * @param mixed $handler - */ - public function patch($route, $handler) - { - $this->addRoute('PATCH', $route, $handler); - } - - /** - * Adds a HEAD route to the collection - * - * This is simply an alias of $this->addRoute('HEAD', $route, $handler) - * - * @param string $route - * @param mixed $handler - */ - public function head($route, $handler) - { - $this->addRoute('HEAD', $route, $handler); - } - - /** - * Returns the collected route data, as provided by the data generator. - * - * @return array - */ - public function getData() - { - return $this->dataGenerator->getData(); - } -} diff --git a/vendor/nikic/fast-route/src/RouteParser.php b/vendor/nikic/fast-route/src/RouteParser.php deleted file mode 100644 index 6a7685c..0000000 --- a/vendor/nikic/fast-route/src/RouteParser.php +++ /dev/null @@ -1,37 +0,0 @@ - $segment) { - if ($segment === '' && $n !== 0) { - throw new BadRouteException('Empty optional part'); - } - - $currentRoute .= $segment; - $routeDatas[] = $this->parsePlaceholders($currentRoute); - } - return $routeDatas; - } - - /** - * Parses a route string that does not contain optional segments. - * - * @param string - * @return mixed[] - */ - private function parsePlaceholders($route) - { - if (!preg_match_all( - '~' . self::VARIABLE_REGEX . '~x', $route, $matches, - PREG_OFFSET_CAPTURE | PREG_SET_ORDER - )) { - return [$route]; - } - - $offset = 0; - $routeData = []; - foreach ($matches as $set) { - if ($set[0][1] > $offset) { - $routeData[] = substr($route, $offset, $set[0][1] - $offset); - } - $routeData[] = [ - $set[1][0], - isset($set[2]) ? trim($set[2][0]) : self::DEFAULT_DISPATCH_REGEX - ]; - $offset = $set[0][1] + strlen($set[0][0]); - } - - if ($offset !== strlen($route)) { - $routeData[] = substr($route, $offset); - } - - return $routeData; - } -} diff --git a/vendor/nikic/fast-route/src/bootstrap.php b/vendor/nikic/fast-route/src/bootstrap.php deleted file mode 100644 index 0bce3a4..0000000 --- a/vendor/nikic/fast-route/src/bootstrap.php +++ /dev/null @@ -1,12 +0,0 @@ - 'FastRoute\\RouteParser\\Std', - 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', - 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', - 'routeCollector' => 'FastRoute\\RouteCollector', - ]; - - /** @var RouteCollector $routeCollector */ - $routeCollector = new $options['routeCollector']( - new $options['routeParser'], new $options['dataGenerator'] - ); - $routeDefinitionCallback($routeCollector); - - return new $options['dispatcher']($routeCollector->getData()); - } - - /** - * @param callable $routeDefinitionCallback - * @param array $options - * - * @return Dispatcher - */ - function cachedDispatcher(callable $routeDefinitionCallback, array $options = []) - { - $options += [ - 'routeParser' => 'FastRoute\\RouteParser\\Std', - 'dataGenerator' => 'FastRoute\\DataGenerator\\GroupCountBased', - 'dispatcher' => 'FastRoute\\Dispatcher\\GroupCountBased', - 'routeCollector' => 'FastRoute\\RouteCollector', - 'cacheDisabled' => false, - ]; - - if (!isset($options['cacheFile'])) { - throw new \LogicException('Must specify "cacheFile" option'); - } - - if (!$options['cacheDisabled'] && file_exists($options['cacheFile'])) { - $dispatchData = require $options['cacheFile']; - if (!is_array($dispatchData)) { - throw new \RuntimeException('Invalid cache file "' . $options['cacheFile'] . '"'); - } - return new $options['dispatcher']($dispatchData); - } - - $routeCollector = new $options['routeCollector']( - new $options['routeParser'], new $options['dataGenerator'] - ); - $routeDefinitionCallback($routeCollector); - - /** @var RouteCollector $routeCollector */ - $dispatchData = $routeCollector->getData(); - if (!$options['cacheDisabled']) { - file_put_contents( - $options['cacheFile'], - ' $this->getDataGeneratorClass(), - 'dispatcher' => $this->getDispatcherClass() - ]; - } - - /** - * @dataProvider provideFoundDispatchCases - */ - public function testFoundDispatches($method, $uri, $callback, $handler, $argDict) - { - $dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); - $info = $dispatcher->dispatch($method, $uri); - $this->assertSame($dispatcher::FOUND, $info[0]); - $this->assertSame($handler, $info[1]); - $this->assertSame($argDict, $info[2]); - } - - /** - * @dataProvider provideNotFoundDispatchCases - */ - public function testNotFoundDispatches($method, $uri, $callback) - { - $dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); - $routeInfo = $dispatcher->dispatch($method, $uri); - $this->assertArrayNotHasKey(1, $routeInfo, - 'NOT_FOUND result must only contain a single element in the returned info array' - ); - $this->assertSame($dispatcher::NOT_FOUND, $routeInfo[0]); - } - - /** - * @dataProvider provideMethodNotAllowedDispatchCases - */ - public function testMethodNotAllowedDispatches($method, $uri, $callback, $availableMethods) - { - $dispatcher = \FastRoute\simpleDispatcher($callback, $this->generateDispatcherOptions()); - $routeInfo = $dispatcher->dispatch($method, $uri); - $this->assertArrayHasKey(1, $routeInfo, - 'METHOD_NOT_ALLOWED result must return an array of allowed methods at index 1' - ); - - list($routedStatus, $methodArray) = $dispatcher->dispatch($method, $uri); - $this->assertSame($dispatcher::METHOD_NOT_ALLOWED, $routedStatus); - $this->assertSame($availableMethods, $methodArray); - } - - /** - * @expectedException \FastRoute\BadRouteException - * @expectedExceptionMessage Cannot use the same placeholder "test" twice - */ - public function testDuplicateVariableNameError() - { - \FastRoute\simpleDispatcher(function (RouteCollector $r) { - $r->addRoute('GET', '/foo/{test}/{test:\d+}', 'handler0'); - }, $this->generateDispatcherOptions()); - } - - /** - * @expectedException \FastRoute\BadRouteException - * @expectedExceptionMessage Cannot register two routes matching "/user/([^/]+)" for method "GET" - */ - public function testDuplicateVariableRoute() - { - \FastRoute\simpleDispatcher(function (RouteCollector $r) { - $r->addRoute('GET', '/user/{id}', 'handler0'); // oops, forgot \d+ restriction ;) - $r->addRoute('GET', '/user/{name}', 'handler1'); - }, $this->generateDispatcherOptions()); - } - - /** - * @expectedException \FastRoute\BadRouteException - * @expectedExceptionMessage Cannot register two routes matching "/user" for method "GET" - */ - public function testDuplicateStaticRoute() - { - \FastRoute\simpleDispatcher(function (RouteCollector $r) { - $r->addRoute('GET', '/user', 'handler0'); - $r->addRoute('GET', '/user', 'handler1'); - }, $this->generateDispatcherOptions()); - } - - /** - * @expectedException \FastRoute\BadRouteException - * @expectedExceptionMessage Static route "/user/nikic" is shadowed by previously defined variable route "/user/([^/]+)" for method "GET" - */ - public function testShadowedStaticRoute() - { - \FastRoute\simpleDispatcher(function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}', 'handler0'); - $r->addRoute('GET', '/user/nikic', 'handler1'); - }, $this->generateDispatcherOptions()); - } - - /** - * @expectedException \FastRoute\BadRouteException - * @expectedExceptionMessage Regex "(en|de)" for parameter "lang" contains a capturing group - */ - public function testCapturing() - { - \FastRoute\simpleDispatcher(function (RouteCollector $r) { - $r->addRoute('GET', '/{lang:(en|de)}', 'handler0'); - }, $this->generateDispatcherOptions()); - } - - public function provideFoundDispatchCases() - { - $cases = []; - - // 0 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/resource/123/456', 'handler0'); - }; - - $method = 'GET'; - $uri = '/resource/123/456'; - $handler = 'handler0'; - $argDict = []; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 1 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/handler0', 'handler0'); - $r->addRoute('GET', '/handler1', 'handler1'); - $r->addRoute('GET', '/handler2', 'handler2'); - }; - - $method = 'GET'; - $uri = '/handler2'; - $handler = 'handler2'; - $argDict = []; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 2 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); - $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); - $r->addRoute('GET', '/user/{name}', 'handler2'); - }; - - $method = 'GET'; - $uri = '/user/rdlowrey'; - $handler = 'handler2'; - $argDict = ['name' => 'rdlowrey']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 3 --------------------------------------------------------------------------------------> - - // reuse $callback from #2 - - $method = 'GET'; - $uri = '/user/12345'; - $handler = 'handler1'; - $argDict = ['id' => '12345']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 4 --------------------------------------------------------------------------------------> - - // reuse $callback from #3 - - $method = 'GET'; - $uri = '/user/NaN'; - $handler = 'handler2'; - $argDict = ['name' => 'NaN']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 5 --------------------------------------------------------------------------------------> - - // reuse $callback from #4 - - $method = 'GET'; - $uri = '/user/rdlowrey/12345'; - $handler = 'handler0'; - $argDict = ['name' => 'rdlowrey', 'id' => '12345']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 6 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler0'); - $r->addRoute('GET', '/user/12345/extension', 'handler1'); - $r->addRoute('GET', '/user/{id:[0-9]+}.{extension}', 'handler2'); - }; - - $method = 'GET'; - $uri = '/user/12345.svg'; - $handler = 'handler2'; - $argDict = ['id' => '12345', 'extension' => 'svg']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 7 ----- Test GET method fallback on HEAD route miss ------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}', 'handler0'); - $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler1'); - $r->addRoute('GET', '/static0', 'handler2'); - $r->addRoute('GET', '/static1', 'handler3'); - $r->addRoute('HEAD', '/static1', 'handler4'); - }; - - $method = 'HEAD'; - $uri = '/user/rdlowrey'; - $handler = 'handler0'; - $argDict = ['name' => 'rdlowrey']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 8 ----- Test GET method fallback on HEAD route miss ------------------------------------> - - // reuse $callback from #7 - - $method = 'HEAD'; - $uri = '/user/rdlowrey/1234'; - $handler = 'handler1'; - $argDict = ['name' => 'rdlowrey', 'id' => '1234']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 9 ----- Test GET method fallback on HEAD route miss ------------------------------------> - - // reuse $callback from #8 - - $method = 'HEAD'; - $uri = '/static0'; - $handler = 'handler2'; - $argDict = []; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 10 ---- Test existing HEAD route used if available (no fallback) -----------------------> - - // reuse $callback from #9 - - $method = 'HEAD'; - $uri = '/static1'; - $handler = 'handler4'; - $argDict = []; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 11 ---- More specified routes are not shadowed by less specific of another method ------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}', 'handler0'); - $r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1'); - }; - - $method = 'POST'; - $uri = '/user/rdlowrey'; - $handler = 'handler1'; - $argDict = ['name' => 'rdlowrey']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 12 ---- Handler of more specific routes is used, if it occurs first --------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}', 'handler0'); - $r->addRoute('POST', '/user/{name:[a-z]+}', 'handler1'); - $r->addRoute('POST', '/user/{name}', 'handler2'); - }; - - $method = 'POST'; - $uri = '/user/rdlowrey'; - $handler = 'handler1'; - $argDict = ['name' => 'rdlowrey']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 13 ---- Route with constant suffix -----------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}', 'handler0'); - $r->addRoute('GET', '/user/{name}/edit', 'handler1'); - }; - - $method = 'GET'; - $uri = '/user/rdlowrey/edit'; - $handler = 'handler1'; - $argDict = ['name' => 'rdlowrey']; - - $cases[] = [$method, $uri, $callback, $handler, $argDict]; - - // 14 ---- Handle multiple methods with the same handler ----------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost'); - $r->addRoute(['DELETE'], '/user', 'handlerDelete'); - $r->addRoute([], '/user', 'handlerNone'); - }; - - $argDict = []; - $cases[] = ['GET', '/user', $callback, 'handlerGetPost', $argDict]; - $cases[] = ['POST', '/user', $callback, 'handlerGetPost', $argDict]; - $cases[] = ['DELETE', '/user', $callback, 'handlerDelete', $argDict]; - - // 17 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('POST', '/user.json', 'handler0'); - $r->addRoute('GET', '/{entity}.json', 'handler1'); - }; - - $cases[] = ['GET', '/user.json', $callback, 'handler1', ['entity' => 'user']]; - - // 18 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '', 'handler0'); - }; - - $cases[] = ['GET', '', $callback, 'handler0', []]; - - // 19 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('HEAD', '/a/{foo}', 'handler0'); - $r->addRoute('GET', '/b/{foo}', 'handler1'); - }; - - $cases[] = ['HEAD', '/b/bar', $callback, 'handler1', ['foo' => 'bar']]; - - // 20 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('HEAD', '/a', 'handler0'); - $r->addRoute('GET', '/b', 'handler1'); - }; - - $cases[] = ['HEAD', '/b', $callback, 'handler1', []]; - - // 21 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/foo', 'handler0'); - $r->addRoute('HEAD', '/{bar}', 'handler1'); - }; - - $cases[] = ['HEAD', '/foo', $callback, 'handler1', ['bar' => 'foo']]; - - // 22 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('*', '/user', 'handler0'); - $r->addRoute('*', '/{user}', 'handler1'); - $r->addRoute('GET', '/user', 'handler2'); - }; - - $cases[] = ['GET', '/user', $callback, 'handler2', []]; - - // 23 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('*', '/user', 'handler0'); - $r->addRoute('GET', '/user', 'handler1'); - }; - - $cases[] = ['POST', '/user', $callback, 'handler0', []]; - - // 24 ---- - - $cases[] = ['HEAD', '/user', $callback, 'handler1', []]; - - // 25 ---- - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/{bar}', 'handler0'); - $r->addRoute('*', '/foo', 'handler1'); - }; - - $cases[] = ['GET', '/foo', $callback, 'handler0', ['bar' => 'foo']]; - - // 26 ---- - - $callback = function(RouteCollector $r) { - $r->addRoute('GET', '/user', 'handler0'); - $r->addRoute('*', '/{foo:.*}', 'handler1'); - }; - - $cases[] = ['POST', '/bar', $callback, 'handler1', ['foo' => 'bar']]; - - // x --------------------------------------------------------------------------------------> - - return $cases; - } - - public function provideNotFoundDispatchCases() - { - $cases = []; - - // 0 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/resource/123/456', 'handler0'); - }; - - $method = 'GET'; - $uri = '/not-found'; - - $cases[] = [$method, $uri, $callback]; - - // 1 --------------------------------------------------------------------------------------> - - // reuse callback from #0 - $method = 'POST'; - $uri = '/not-found'; - - $cases[] = [$method, $uri, $callback]; - - // 2 --------------------------------------------------------------------------------------> - - // reuse callback from #1 - $method = 'PUT'; - $uri = '/not-found'; - - $cases[] = [$method, $uri, $callback]; - - // 3 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/handler0', 'handler0'); - $r->addRoute('GET', '/handler1', 'handler1'); - $r->addRoute('GET', '/handler2', 'handler2'); - }; - - $method = 'GET'; - $uri = '/not-found'; - - $cases[] = [$method, $uri, $callback]; - - // 4 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); - $r->addRoute('GET', '/user/{id:[0-9]+}', 'handler1'); - $r->addRoute('GET', '/user/{name}', 'handler2'); - }; - - $method = 'GET'; - $uri = '/not-found'; - - $cases[] = [$method, $uri, $callback]; - - // 5 --------------------------------------------------------------------------------------> - - // reuse callback from #4 - $method = 'GET'; - $uri = '/user/rdlowrey/12345/not-found'; - - $cases[] = [$method, $uri, $callback]; - - // 6 --------------------------------------------------------------------------------------> - - // reuse callback from #5 - $method = 'HEAD'; - - $cases[] = [$method, $uri, $callback]; - - // x --------------------------------------------------------------------------------------> - - return $cases; - } - - public function provideMethodNotAllowedDispatchCases() - { - $cases = []; - - // 0 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/resource/123/456', 'handler0'); - }; - - $method = 'POST'; - $uri = '/resource/123/456'; - $allowedMethods = ['GET']; - - $cases[] = [$method, $uri, $callback, $allowedMethods]; - - // 1 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/resource/123/456', 'handler0'); - $r->addRoute('POST', '/resource/123/456', 'handler1'); - $r->addRoute('PUT', '/resource/123/456', 'handler2'); - $r->addRoute('*', '/', 'handler3'); - }; - - $method = 'DELETE'; - $uri = '/resource/123/456'; - $allowedMethods = ['GET', 'POST', 'PUT']; - - $cases[] = [$method, $uri, $callback, $allowedMethods]; - - // 2 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0'); - $r->addRoute('POST', '/user/{name}/{id:[0-9]+}', 'handler1'); - $r->addRoute('PUT', '/user/{name}/{id:[0-9]+}', 'handler2'); - $r->addRoute('PATCH', '/user/{name}/{id:[0-9]+}', 'handler3'); - }; - - $method = 'DELETE'; - $uri = '/user/rdlowrey/42'; - $allowedMethods = ['GET', 'POST', 'PUT', 'PATCH']; - - $cases[] = [$method, $uri, $callback, $allowedMethods]; - - // 3 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute('POST', '/user/{name}', 'handler1'); - $r->addRoute('PUT', '/user/{name:[a-z]+}', 'handler2'); - $r->addRoute('PATCH', '/user/{name:[a-z]+}', 'handler3'); - }; - - $method = 'GET'; - $uri = '/user/rdlowrey'; - $allowedMethods = ['POST', 'PUT', 'PATCH']; - - $cases[] = [$method, $uri, $callback, $allowedMethods]; - - // 4 --------------------------------------------------------------------------------------> - - $callback = function (RouteCollector $r) { - $r->addRoute(['GET', 'POST'], '/user', 'handlerGetPost'); - $r->addRoute(['DELETE'], '/user', 'handlerDelete'); - $r->addRoute([], '/user', 'handlerNone'); - }; - - $cases[] = ['PUT', '/user', $callback, ['GET', 'POST', 'DELETE']]; - - // 5 - - $callback = function (RouteCollector $r) { - $r->addRoute('POST', '/user.json', 'handler0'); - $r->addRoute('GET', '/{entity}.json', 'handler1'); - }; - - $cases[] = ['PUT', '/user.json', $callback, ['POST', 'GET']]; - - // x --------------------------------------------------------------------------------------> - - return $cases; - } -} diff --git a/vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php b/vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php deleted file mode 100644 index f821ef5..0000000 --- a/vendor/nikic/fast-route/test/Dispatcher/GroupCountBasedTest.php +++ /dev/null @@ -1,16 +0,0 @@ -markTestSkipped('PHP 5.6 required for MARK support'); - } - } - - protected function getDispatcherClass() - { - return 'FastRoute\\Dispatcher\\MarkBased'; - } - - protected function getDataGeneratorClass() - { - return 'FastRoute\\DataGenerator\\MarkBased'; - } -} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php b/vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php deleted file mode 100644 index b6fc53f..0000000 --- a/vendor/nikic/fast-route/test/HackTypechecker/HackTypecheckerTest.php +++ /dev/null @@ -1,44 +0,0 @@ -markTestSkipped('HHVM only'); - } - if (!version_compare(HHVM_VERSION, '3.9.0', '>=')) { - $this->markTestSkipped('classname requires HHVM 3.9+'); - } - - // The typechecker recurses the whole tree, so it makes sure - // that everything in fixtures/ is valid when this runs. - - $output = []; - $exit_code = null; - exec( - 'hh_server --check ' . escapeshellarg(__DIR__ . '/../../') . ' 2>&1', - $output, - $exit_code - ); - if ($exit_code === self::SERVER_ALREADY_RUNNING_CODE) { - $this->assertTrue( - $recurse, - 'Typechecker still running after running hh_client stop' - ); - // Server already running - 3.10 => 3.11 regression: - // https://github.com/facebook/hhvm/issues/6646 - exec('hh_client stop 2>/dev/null'); - $this->testTypechecks(/* recurse = */ false); - return; - - } - $this->assertSame(0, $exit_code, implode("\n", $output)); - } -} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php deleted file mode 100644 index 05a9af2..0000000 --- a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/all_options.php +++ /dev/null @@ -1,29 +0,0 @@ - {}, - shape( - 'routeParser' => \FastRoute\RouteParser\Std::class, - 'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class, - 'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class, - 'routeCollector' => \FastRoute\RouteCollector::class, - ), - ); -} - -function all_options_cached(): \FastRoute\Dispatcher { - return \FastRoute\cachedDispatcher( - $collector ==> {}, - shape( - 'routeParser' => \FastRoute\RouteParser\Std::class, - 'dataGenerator' => \FastRoute\DataGenerator\GroupCountBased::class, - 'dispatcher' => \FastRoute\Dispatcher\GroupCountBased::class, - 'routeCollector' => \FastRoute\RouteCollector::class, - 'cacheFile' => '/dev/null', - 'cacheDisabled' => false, - ), - ); -} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php deleted file mode 100644 index 61eb541..0000000 --- a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/empty_options.php +++ /dev/null @@ -1,11 +0,0 @@ - {}, shape()); -} - -function empty_options_cached(): \FastRoute\Dispatcher { - return \FastRoute\cachedDispatcher($collector ==> {}, shape()); -} diff --git a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php b/vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php deleted file mode 100644 index 44b5422..0000000 --- a/vendor/nikic/fast-route/test/HackTypechecker/fixtures/no_options.php +++ /dev/null @@ -1,11 +0,0 @@ - {}); -} - -function no_options_cached(): \FastRoute\Dispatcher { - return \FastRoute\cachedDispatcher($collector ==> {}); -} diff --git a/vendor/nikic/fast-route/test/RouteCollectorTest.php b/vendor/nikic/fast-route/test/RouteCollectorTest.php deleted file mode 100644 index cc54407..0000000 --- a/vendor/nikic/fast-route/test/RouteCollectorTest.php +++ /dev/null @@ -1,108 +0,0 @@ -delete('/delete', 'delete'); - $r->get('/get', 'get'); - $r->head('/head', 'head'); - $r->patch('/patch', 'patch'); - $r->post('/post', 'post'); - $r->put('/put', 'put'); - - $expected = [ - ['DELETE', '/delete', 'delete'], - ['GET', '/get', 'get'], - ['HEAD', '/head', 'head'], - ['PATCH', '/patch', 'patch'], - ['POST', '/post', 'post'], - ['PUT', '/put', 'put'], - ]; - - $this->assertSame($expected, $r->routes); - } - - public function testGroups() - { - $r = new DummyRouteCollector(); - - $r->delete('/delete', 'delete'); - $r->get('/get', 'get'); - $r->head('/head', 'head'); - $r->patch('/patch', 'patch'); - $r->post('/post', 'post'); - $r->put('/put', 'put'); - - $r->addGroup('/group-one', function (DummyRouteCollector $r) { - $r->delete('/delete', 'delete'); - $r->get('/get', 'get'); - $r->head('/head', 'head'); - $r->patch('/patch', 'patch'); - $r->post('/post', 'post'); - $r->put('/put', 'put'); - - $r->addGroup('/group-two', function (DummyRouteCollector $r) { - $r->delete('/delete', 'delete'); - $r->get('/get', 'get'); - $r->head('/head', 'head'); - $r->patch('/patch', 'patch'); - $r->post('/post', 'post'); - $r->put('/put', 'put'); - }); - }); - - $r->addGroup('/admin', function (DummyRouteCollector $r) { - $r->get('-some-info', 'admin-some-info'); - }); - $r->addGroup('/admin-', function (DummyRouteCollector $r) { - $r->get('more-info', 'admin-more-info'); - }); - - $expected = [ - ['DELETE', '/delete', 'delete'], - ['GET', '/get', 'get'], - ['HEAD', '/head', 'head'], - ['PATCH', '/patch', 'patch'], - ['POST', '/post', 'post'], - ['PUT', '/put', 'put'], - ['DELETE', '/group-one/delete', 'delete'], - ['GET', '/group-one/get', 'get'], - ['HEAD', '/group-one/head', 'head'], - ['PATCH', '/group-one/patch', 'patch'], - ['POST', '/group-one/post', 'post'], - ['PUT', '/group-one/put', 'put'], - ['DELETE', '/group-one/group-two/delete', 'delete'], - ['GET', '/group-one/group-two/get', 'get'], - ['HEAD', '/group-one/group-two/head', 'head'], - ['PATCH', '/group-one/group-two/patch', 'patch'], - ['POST', '/group-one/group-two/post', 'post'], - ['PUT', '/group-one/group-two/put', 'put'], - ['GET', '/admin-some-info', 'admin-some-info'], - ['GET', '/admin-more-info', 'admin-more-info'], - ]; - - $this->assertSame($expected, $r->routes); - } -} - -class DummyRouteCollector extends RouteCollector -{ - public $routes = []; - - public function __construct() - { - } - - public function addRoute($method, $route, $handler) - { - $route = $this->currentGroupPrefix . $route; - $this->routes[] = [$method, $route, $handler]; - } -} diff --git a/vendor/nikic/fast-route/test/RouteParser/StdTest.php b/vendor/nikic/fast-route/test/RouteParser/StdTest.php deleted file mode 100644 index e13e4de..0000000 --- a/vendor/nikic/fast-route/test/RouteParser/StdTest.php +++ /dev/null @@ -1,154 +0,0 @@ -parse($routeString); - $this->assertSame($expectedRouteDatas, $routeDatas); - } - - /** @dataProvider provideTestParseError */ - public function testParseError($routeString, $expectedExceptionMessage) - { - $parser = new Std(); - $this->setExpectedException('FastRoute\\BadRouteException', $expectedExceptionMessage); - $parser->parse($routeString); - } - - public function provideTestParse() - { - return [ - [ - '/test', - [ - ['/test'], - ] - ], - [ - '/test/{param}', - [ - ['/test/', ['param', '[^/]+']], - ] - ], - [ - '/te{ param }st', - [ - ['/te', ['param', '[^/]+'], 'st'] - ] - ], - [ - '/test/{param1}/test2/{param2}', - [ - ['/test/', ['param1', '[^/]+'], '/test2/', ['param2', '[^/]+']] - ] - ], - [ - '/test/{param:\d+}', - [ - ['/test/', ['param', '\d+']] - ] - ], - [ - '/test/{ param : \d{1,9} }', - [ - ['/test/', ['param', '\d{1,9}']] - ] - ], - [ - '/test[opt]', - [ - ['/test'], - ['/testopt'], - ] - ], - [ - '/test[/{param}]', - [ - ['/test'], - ['/test/', ['param', '[^/]+']], - ] - ], - [ - '/{param}[opt]', - [ - ['/', ['param', '[^/]+']], - ['/', ['param', '[^/]+'], 'opt'] - ] - ], - [ - '/test[/{name}[/{id:[0-9]+}]]', - [ - ['/test'], - ['/test/', ['name', '[^/]+']], - ['/test/', ['name', '[^/]+'], '/', ['id', '[0-9]+']], - ] - ], - [ - '', - [ - [''], - ] - ], - [ - '[test]', - [ - [''], - ['test'], - ] - ], - [ - '/{foo-bar}', - [ - ['/', ['foo-bar', '[^/]+']] - ] - ], - [ - '/{_foo:.*}', - [ - ['/', ['_foo', '.*']] - ] - ], - ]; - } - - public function provideTestParseError() - { - return [ - [ - '/test[opt', - "Number of opening '[' and closing ']' does not match" - ], - [ - '/test[opt[opt2]', - "Number of opening '[' and closing ']' does not match" - ], - [ - '/testopt]', - "Number of opening '[' and closing ']' does not match" - ], - [ - '/test[]', - 'Empty optional part' - ], - [ - '/test[[opt]]', - 'Empty optional part' - ], - [ - '[[test]]', - 'Empty optional part' - ], - [ - '/test[/opt]/required', - 'Optional segments can only occur at the end of a route' - ], - ]; - } -} diff --git a/vendor/nikic/fast-route/test/bootstrap.php b/vendor/nikic/fast-route/test/bootstrap.php deleted file mode 100644 index 3023f41..0000000 --- a/vendor/nikic/fast-route/test/bootstrap.php +++ /dev/null @@ -1,11 +0,0 @@ - `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`. -> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered. - diff --git a/vendor/psr/http-message/docs/PSR7-Usage.md b/vendor/psr/http-message/docs/PSR7-Usage.md deleted file mode 100644 index b6d048a..0000000 --- a/vendor/psr/http-message/docs/PSR7-Usage.md +++ /dev/null @@ -1,159 +0,0 @@ -### PSR-7 Usage - -All PSR-7 applications comply with these interfaces -They were created to establish a standard between middleware implementations. - -> `RequestInterface`, `ServerRequestInterface`, `ResponseInterface` extend `MessageInterface` because the `Request` and the `Response` are `HTTP Messages`. -> When using `ServerRequestInterface`, both `RequestInterface` and `Psr\Http\Message\MessageInterface` methods are considered. - - -The following examples will illustrate how basic operations are done in PSR-7. - -##### Examples - - -For this examples to work (at least) a PSR-7 implementation package is required. (eg: zendframework/zend-diactoros, guzzlehttp/psr7, slim/slim, etc) -All PSR-7 implementations should have the same behaviour. - -The following will be assumed: -`$request` is an object of `Psr\Http\Message\RequestInterface` and - -`$response` is an object implementing `Psr\Http\Message\RequestInterface` - - -### Working with HTTP Headers - -#### Adding headers to response: - -```php -$response->withHeader('My-Custom-Header', 'My Custom Message'); -``` - -#### Appending values to headers - -```php -$response->withAddedHeader('My-Custom-Header', 'The second message'); -``` - -#### Checking if header exists: - -```php -$request->hasHeader('My-Custom-Header'); // will return false -$response->hasHeader('My-Custom-Header'); // will return true -``` - -> Note: My-Custom-Header was only added in the Response - -#### Getting comma-separated values from a header (also applies to request) - -```php -// getting value from request headers -$request->getHeaderLine('Content-Type'); // will return: "text/html; charset=UTF-8" -// getting value from response headers -$response->getHeaderLine('My-Custom-Header'); // will return: "My Custom Message; The second message" -``` - -#### Getting array of value from a header (also applies to request) -```php -// getting value from request headers -$request->getHeader('Content-Type'); // will return: ["text/html", "charset=UTF-8"] -// getting value from response headers -$response->getHeader('My-Custom-Header'); // will return: ["My Custom Message", "The second message"] -``` - -#### Removing headers from HTTP Messages -```php -// removing a header from Request, removing deprecated "Content-MD5" header -$request->withoutHeader('Content-MD5'); - -// removing a header from Response -// effect: the browser won't know the size of the stream -// the browser will download the stream till it ends -$response->withoutHeader('Content-Length'); -``` - -### Working with HTTP Message Body - -When working with the PSR-7 there are two methods of implementation: -#### 1. Getting the body separately - -> This method makes the body handling easier to understand and is useful when repeatedly calling body methods. (You only call `getBody()` once). Using this method mistakes like `$response->write()` are also prevented. - -```php -$body = $response->getBody(); -// operations on body, eg. read, write, seek -// ... -// replacing the old body -$response->withBody($body); -// this last statement is optional as we working with objects -// in this case the "new" body is same with the "old" one -// the $body variable has the same value as the one in $request, only the reference is passed -``` - -#### 2. Working directly on response - -> This method is useful when only performing few operations as the `$request->getBody()` statement fragment is required - -```php -$response->getBody()->write('hello'); -``` - -### Getting the body contents - -The following snippet gets the contents of a stream contents. -> Note: Streams must be rewinded, if content was written into streams, it will be ignored when calling `getContents()` because the stream pointer is set to the last character, which is `\0` - meaning end of stream. -```php -$body = $response->getBody(); -$body->rewind(); // or $body->seek(0); -$bodyText = $body->getContents(); -``` -> Note: If `$body->seek(1)` is called before `$body->getContents()`, the first character will be ommited as the starting pointer is set to `1`, not `0`. This is why using `$body->rewind()` is recommended. - -### Append to body - -```php -$response->getBody()->write('Hello'); // writing directly -$body = $request->getBody(); // which is a `StreamInterface` -$body->write('xxxxx'); -``` - -### Prepend to body -Prepending is different when it comes to streams. The content must be copied before writing the content to be prepended. -The following example will explain the behaviour of streams. - -```php -// assuming our response is initially empty -$body = $repsonse->getBody(); -// writing the string "abcd" -$body->write('abcd'); - -// seeking to start of stream -$body->seek(0); -// writing 'ef' -$body->write('ef'); // at this point the stream contains "efcd" -``` - -#### Prepending by rewriting separately - -```php -// assuming our response body stream only contains: "abcd" -$body = $response->getBody(); -$body->rewind(); -$contents = $body->getContents(); // abcd -// seeking the stream to beginning -$body->rewind(); -$body->write('ef'); // stream contains "efcd" -$body->write($contents); // stream contains "efabcd" -``` - -> Note: `getContents()` seeks the stream while reading it, therefore if the second `rewind()` method call was not present the stream would have resulted in `abcdefabcd` because the `write()` method appends to stream if not preceeded by `rewind()` or `seek(0)`. - -#### Prepending by using contents as a string -```php -$body = $response->getBody(); -$body->rewind(); -$contents = $body->getContents(); // efabcd -$contents = 'ef'.$contents; -$body->rewind(); -$body->write($contents); -``` diff --git a/vendor/psr/http-message/src/MessageInterface.php b/vendor/psr/http-message/src/MessageInterface.php deleted file mode 100644 index 8cdb4ed..0000000 --- a/vendor/psr/http-message/src/MessageInterface.php +++ /dev/null @@ -1,189 +0,0 @@ -getHeaders() as $name => $values) { - * echo $name . ": " . implode(", ", $values); - * } - * - * // Emit headers iteratively: - * foreach ($message->getHeaders() as $name => $values) { - * foreach ($values as $value) { - * header(sprintf('%s: %s', $name, $value), false); - * } - * } - * - * While header names are not case-sensitive, getHeaders() will preserve the - * exact case in which headers were originally specified. - * - * @return string[][] Returns an associative array of the message's headers. Each - * key MUST be a header name, and each value MUST be an array of strings - * for that header. - */ - public function getHeaders(); - - /** - * Checks if a header exists by the given case-insensitive name. - * - * @param string $name Case-insensitive header field name. - * @return bool Returns true if any header names match the given header - * name using a case-insensitive string comparison. Returns false if - * no matching header name is found in the message. - */ - public function hasHeader(string $name); - - /** - * Retrieves a message header value by the given case-insensitive name. - * - * This method returns an array of all the header values of the given - * case-insensitive header name. - * - * If the header does not appear in the message, this method MUST return an - * empty array. - * - * @param string $name Case-insensitive header field name. - * @return string[] An array of string values as provided for the given - * header. If the header does not appear in the message, this method MUST - * return an empty array. - */ - public function getHeader(string $name); - - /** - * Retrieves a comma-separated string of the values for a single header. - * - * This method returns all of the header values of the given - * case-insensitive header name as a string concatenated together using - * a comma. - * - * NOTE: Not all header values may be appropriately represented using - * comma concatenation. For such headers, use getHeader() instead - * and supply your own delimiter when concatenating. - * - * If the header does not appear in the message, this method MUST return - * an empty string. - * - * @param string $name Case-insensitive header field name. - * @return string A string of values as provided for the given header - * concatenated together using a comma. If the header does not appear in - * the message, this method MUST return an empty string. - */ - public function getHeaderLine(string $name); - - /** - * Return an instance with the provided value replacing the specified header. - * - * While header names are case-insensitive, the casing of the header will - * be preserved by this function, and returned from getHeaders(). - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * new and/or updated header and value. - * - * @param string $name Case-insensitive header field name. - * @param string|string[] $value Header value(s). - * @return static - * @throws \InvalidArgumentException for invalid header names or values. - */ - public function withHeader(string $name, $value); - - /** - * Return an instance with the specified header appended with the given value. - * - * Existing values for the specified header will be maintained. The new - * value(s) will be appended to the existing list. If the header did not - * exist previously, it will be added. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * new header and/or value. - * - * @param string $name Case-insensitive header field name to add. - * @param string|string[] $value Header value(s). - * @return static - * @throws \InvalidArgumentException for invalid header names or values. - */ - public function withAddedHeader(string $name, $value); - - /** - * Return an instance without the specified header. - * - * Header resolution MUST be done without case-sensitivity. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that removes - * the named header. - * - * @param string $name Case-insensitive header field name to remove. - * @return static - */ - public function withoutHeader(string $name); - - /** - * Gets the body of the message. - * - * @return StreamInterface Returns the body as a stream. - */ - public function getBody(); - - /** - * Return an instance with the specified message body. - * - * The body MUST be a StreamInterface object. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return a new instance that has the - * new body stream. - * - * @param StreamInterface $body Body. - * @return static - * @throws \InvalidArgumentException When the body is not valid. - */ - public function withBody(StreamInterface $body); -} diff --git a/vendor/psr/http-message/src/RequestInterface.php b/vendor/psr/http-message/src/RequestInterface.php deleted file mode 100644 index 38066df..0000000 --- a/vendor/psr/http-message/src/RequestInterface.php +++ /dev/null @@ -1,131 +0,0 @@ -getQuery()` - * or from the `QUERY_STRING` server param. - * - * @return array - */ - public function getQueryParams(); - - /** - * Return an instance with the specified query string arguments. - * - * These values SHOULD remain immutable over the course of the incoming - * request. They MAY be injected during instantiation, such as from PHP's - * $_GET superglobal, or MAY be derived from some other value such as the - * URI. In cases where the arguments are parsed from the URI, the data - * MUST be compatible with what PHP's parse_str() would return for - * purposes of how duplicate query parameters are handled, and how nested - * sets are handled. - * - * Setting query string arguments MUST NOT change the URI stored by the - * request, nor the values in the server params. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * updated query string arguments. - * - * @param array $query Array of query string arguments, typically from - * $_GET. - * @return static - */ - public function withQueryParams(array $query); - - /** - * Retrieve normalized file upload data. - * - * This method returns upload metadata in a normalized tree, with each leaf - * an instance of Psr\Http\Message\UploadedFileInterface. - * - * These values MAY be prepared from $_FILES or the message body during - * instantiation, or MAY be injected via withUploadedFiles(). - * - * @return array An array tree of UploadedFileInterface instances; an empty - * array MUST be returned if no data is present. - */ - public function getUploadedFiles(); - - /** - * Create a new instance with the specified uploaded files. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * updated body parameters. - * - * @param array $uploadedFiles An array tree of UploadedFileInterface instances. - * @return static - * @throws \InvalidArgumentException if an invalid structure is provided. - */ - public function withUploadedFiles(array $uploadedFiles); - - /** - * Retrieve any parameters provided in the request body. - * - * If the request Content-Type is either application/x-www-form-urlencoded - * or multipart/form-data, and the request method is POST, this method MUST - * return the contents of $_POST. - * - * Otherwise, this method may return any results of deserializing - * the request body content; as parsing returns structured content, the - * potential types MUST be arrays or objects only. A null value indicates - * the absence of body content. - * - * @return null|array|object The deserialized body parameters, if any. - * These will typically be an array or object. - */ - public function getParsedBody(); - - /** - * Return an instance with the specified body parameters. - * - * These MAY be injected during instantiation. - * - * If the request Content-Type is either application/x-www-form-urlencoded - * or multipart/form-data, and the request method is POST, use this method - * ONLY to inject the contents of $_POST. - * - * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of - * deserializing the request body content. Deserialization/parsing returns - * structured data, and, as such, this method ONLY accepts arrays or objects, - * or a null value if nothing was available to parse. - * - * As an example, if content negotiation determines that the request data - * is a JSON payload, this method could be used to create a request - * instance with the deserialized parameters. - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * updated body parameters. - * - * @param null|array|object $data The deserialized body data. This will - * typically be in an array or object. - * @return static - * @throws \InvalidArgumentException if an unsupported argument type is - * provided. - */ - public function withParsedBody($data); - - /** - * Retrieve attributes derived from the request. - * - * The request "attributes" may be used to allow injection of any - * parameters derived from the request: e.g., the results of path - * match operations; the results of decrypting cookies; the results of - * deserializing non-form-encoded message bodies; etc. Attributes - * will be application and request specific, and CAN be mutable. - * - * @return array Attributes derived from the request. - */ - public function getAttributes(); - - /** - * Retrieve a single derived request attribute. - * - * Retrieves a single derived request attribute as described in - * getAttributes(). If the attribute has not been previously set, returns - * the default value as provided. - * - * This method obviates the need for a hasAttribute() method, as it allows - * specifying a default value to return if the attribute is not found. - * - * @see getAttributes() - * @param string $name The attribute name. - * @param mixed $default Default value to return if the attribute does not exist. - * @return mixed - */ - public function getAttribute(string $name, $default = null); - - /** - * Return an instance with the specified derived request attribute. - * - * This method allows setting a single derived request attribute as - * described in getAttributes(). - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that has the - * updated attribute. - * - * @see getAttributes() - * @param string $name The attribute name. - * @param mixed $value The value of the attribute. - * @return static - */ - public function withAttribute(string $name, $value); - - /** - * Return an instance that removes the specified derived request attribute. - * - * This method allows removing a single derived request attribute as - * described in getAttributes(). - * - * This method MUST be implemented in such a way as to retain the - * immutability of the message, and MUST return an instance that removes - * the attribute. - * - * @see getAttributes() - * @param string $name The attribute name. - * @return static - */ - public function withoutAttribute(string $name); -} diff --git a/vendor/psr/http-message/src/StreamInterface.php b/vendor/psr/http-message/src/StreamInterface.php deleted file mode 100644 index 5924663..0000000 --- a/vendor/psr/http-message/src/StreamInterface.php +++ /dev/null @@ -1,160 +0,0 @@ - - * [user-info@]host[:port] - * - * - * If the port component is not set or is the standard port for the current - * scheme, it SHOULD NOT be included. - * - * @see https://tools.ietf.org/html/rfc3986#section-3.2 - * @return string The URI authority, in "[user-info@]host[:port]" format. - */ - public function getAuthority(); - - /** - * Retrieve the user information component of the URI. - * - * If no user information is present, this method MUST return an empty - * string. - * - * If a user is present in the URI, this will return that value; - * additionally, if the password is also present, it will be appended to the - * user value, with a colon (":") separating the values. - * - * The trailing "@" character is not part of the user information and MUST - * NOT be added. - * - * @return string The URI user information, in "username[:password]" format. - */ - public function getUserInfo(); - - /** - * Retrieve the host component of the URI. - * - * If no host is present, this method MUST return an empty string. - * - * The value returned MUST be normalized to lowercase, per RFC 3986 - * Section 3.2.2. - * - * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 - * @return string The URI host. - */ - public function getHost(); - - /** - * Retrieve the port component of the URI. - * - * If a port is present, and it is non-standard for the current scheme, - * this method MUST return it as an integer. If the port is the standard port - * used with the current scheme, this method SHOULD return null. - * - * If no port is present, and no scheme is present, this method MUST return - * a null value. - * - * If no port is present, but a scheme is present, this method MAY return - * the standard port for that scheme, but SHOULD return null. - * - * @return null|int The URI port. - */ - public function getPort(); - - /** - * Retrieve the path component of the URI. - * - * The path can either be empty or absolute (starting with a slash) or - * rootless (not starting with a slash). Implementations MUST support all - * three syntaxes. - * - * Normally, the empty path "" and absolute path "/" are considered equal as - * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically - * do this normalization because in contexts with a trimmed base path, e.g. - * the front controller, this difference becomes significant. It's the task - * of the user to handle both "" and "/". - * - * The value returned MUST be percent-encoded, but MUST NOT double-encode - * any characters. To determine what characters to encode, please refer to - * RFC 3986, Sections 2 and 3.3. - * - * As an example, if the value should include a slash ("/") not intended as - * delimiter between path segments, that value MUST be passed in encoded - * form (e.g., "%2F") to the instance. - * - * @see https://tools.ietf.org/html/rfc3986#section-2 - * @see https://tools.ietf.org/html/rfc3986#section-3.3 - * @return string The URI path. - */ - public function getPath(); - - /** - * Retrieve the query string of the URI. - * - * If no query string is present, this method MUST return an empty string. - * - * The leading "?" character is not part of the query and MUST NOT be - * added. - * - * The value returned MUST be percent-encoded, but MUST NOT double-encode - * any characters. To determine what characters to encode, please refer to - * RFC 3986, Sections 2 and 3.4. - * - * As an example, if a value in a key/value pair of the query string should - * include an ampersand ("&") not intended as a delimiter between values, - * that value MUST be passed in encoded form (e.g., "%26") to the instance. - * - * @see https://tools.ietf.org/html/rfc3986#section-2 - * @see https://tools.ietf.org/html/rfc3986#section-3.4 - * @return string The URI query string. - */ - public function getQuery(); - - /** - * Retrieve the fragment component of the URI. - * - * If no fragment is present, this method MUST return an empty string. - * - * The leading "#" character is not part of the fragment and MUST NOT be - * added. - * - * The value returned MUST be percent-encoded, but MUST NOT double-encode - * any characters. To determine what characters to encode, please refer to - * RFC 3986, Sections 2 and 3.5. - * - * @see https://tools.ietf.org/html/rfc3986#section-2 - * @see https://tools.ietf.org/html/rfc3986#section-3.5 - * @return string The URI fragment. - */ - public function getFragment(); - - /** - * Return an instance with the specified scheme. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified scheme. - * - * Implementations MUST support the schemes "http" and "https" case - * insensitively, and MAY accommodate other schemes if required. - * - * An empty scheme is equivalent to removing the scheme. - * - * @param string $scheme The scheme to use with the new instance. - * @return static A new instance with the specified scheme. - * @throws \InvalidArgumentException for invalid or unsupported schemes. - */ - public function withScheme(string $scheme); - - /** - * Return an instance with the specified user information. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified user information. - * - * Password is optional, but the user information MUST include the - * user; an empty string for the user is equivalent to removing user - * information. - * - * @param string $user The user name to use for authority. - * @param null|string $password The password associated with $user. - * @return static A new instance with the specified user information. - */ - public function withUserInfo(string $user, ?string $password = null); - - /** - * Return an instance with the specified host. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified host. - * - * An empty host value is equivalent to removing the host. - * - * @param string $host The hostname to use with the new instance. - * @return static A new instance with the specified host. - * @throws \InvalidArgumentException for invalid hostnames. - */ - public function withHost(string $host); - - /** - * Return an instance with the specified port. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified port. - * - * Implementations MUST raise an exception for ports outside the - * established TCP and UDP port ranges. - * - * A null value provided for the port is equivalent to removing the port - * information. - * - * @param null|int $port The port to use with the new instance; a null value - * removes the port information. - * @return static A new instance with the specified port. - * @throws \InvalidArgumentException for invalid ports. - */ - public function withPort(?int $port); - - /** - * Return an instance with the specified path. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified path. - * - * The path can either be empty or absolute (starting with a slash) or - * rootless (not starting with a slash). Implementations MUST support all - * three syntaxes. - * - * If the path is intended to be domain-relative rather than path relative then - * it must begin with a slash ("/"). Paths not starting with a slash ("/") - * are assumed to be relative to some base path known to the application or - * consumer. - * - * Users can provide both encoded and decoded path characters. - * Implementations ensure the correct encoding as outlined in getPath(). - * - * @param string $path The path to use with the new instance. - * @return static A new instance with the specified path. - * @throws \InvalidArgumentException for invalid paths. - */ - public function withPath(string $path); - - /** - * Return an instance with the specified query string. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified query string. - * - * Users can provide both encoded and decoded query characters. - * Implementations ensure the correct encoding as outlined in getQuery(). - * - * An empty query string value is equivalent to removing the query string. - * - * @param string $query The query string to use with the new instance. - * @return static A new instance with the specified query string. - * @throws \InvalidArgumentException for invalid query strings. - */ - public function withQuery(string $query); - - /** - * Return an instance with the specified URI fragment. - * - * This method MUST retain the state of the current instance, and return - * an instance that contains the specified URI fragment. - * - * Users can provide both encoded and decoded fragment characters. - * Implementations ensure the correct encoding as outlined in getFragment(). - * - * An empty fragment value is equivalent to removing the fragment. - * - * @param string $fragment The fragment to use with the new instance. - * @return static A new instance with the specified fragment. - */ - public function withFragment(string $fragment); - - /** - * Return the string representation as a URI reference. - * - * Depending on which components of the URI are present, the resulting - * string is either a full URI or relative reference according to RFC 3986, - * Section 4.1. The method concatenates the various components of the URI, - * using the appropriate delimiters: - * - * - If a scheme is present, it MUST be suffixed by ":". - * - If an authority is present, it MUST be prefixed by "//". - * - The path can be concatenated without delimiters. But there are two - * cases where the path has to be adjusted to make the URI reference - * valid as PHP does not allow to throw an exception in __toString(): - * - If the path is rootless and an authority is present, the path MUST - * be prefixed by "/". - * - If the path is starting with more than one "/" and no authority is - * present, the starting slashes MUST be reduced to one. - * - If a query is present, it MUST be prefixed by "?". - * - If a fragment is present, it MUST be prefixed by "#". - * - * @see http://tools.ietf.org/html/rfc3986#section-4.1 - * @return string - */ - public function __toString(); -} diff --git a/vendor/react/async/CHANGELOG.md b/vendor/react/async/CHANGELOG.md deleted file mode 100644 index bafac9d..0000000 --- a/vendor/react/async/CHANGELOG.md +++ /dev/null @@ -1,112 +0,0 @@ -# Changelog - -## 4.3.0 (2024-06-04) - -* Feature: Improve performance by avoiding unneeded references in `FiberMap`. - (#88 by @clue) - -* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations. - (#87 by @clue) - -* Improve type safety for test environment. - (#86 by @SimonFrings) - -## 4.2.0 (2023-11-22) - -* Feature: Add Promise v3 template types for all public functions. - (#40 by @WyriHaximus and @clue) - - All our public APIs now use Promise v3 template types to guide IDEs and static - analysis tools (like PHPStan), helping with proper type usage and improving - code quality: - - ```php - assertType('bool', await(resolve(true))); - assertType('PromiseInterface', async(fn(): bool => true)()); - assertType('PromiseInterface', coroutine(fn(): bool => true)); - ``` - -* Feature: Full PHP 8.3 compatibility. - (#81 by @clue) - -* Update test suite to avoid unhandled promise rejections. - (#79 by @clue) - -## 4.1.0 (2023-06-22) - -* Feature: Add new `delay()` function to delay program execution. - (#69 and #78 by @clue) - - ```php - echo 'a'; - Loop::addTimer(1.0, function () { - echo 'b'; - }); - React\Async\delay(3.0); - echo 'c'; - - // prints "a" at t=0.0s - // prints "b" at t=1.0s - // prints "c" at t=3.0s - ``` - -* Update test suite, add PHPStan with `max` level and report failed assertions. - (#66 and #76 by @clue and #61 and #73 by @WyriHaximus) - -## 4.0.0 (2022-07-11) - -A major new feature release, see [**release announcement**](https://clue.engineering/2022/announcing-reactphp-async). - -* We'd like to emphasize that this component is production ready and battle-tested. - We plan to support all long-term support (LTS) releases for at least 24 months, - so you have a rock-solid foundation to build on top of. - -* The v4 release will be the way forward for this package. However, we will still - actively support v3 and v2 to provide a smooth upgrade path for those not yet - on PHP 8.1+. If you're using an older PHP version, you may use either version - which all provide a compatible API but may not take advantage of newer language - features. You may target multiple versions at the same time to support a wider range of - PHP versions: - - * [`4.x` branch](https://github.com/reactphp/async/tree/4.x) (PHP 8.1+) - * [`3.x` branch](https://github.com/reactphp/async/tree/3.x) (PHP 7.1+) - * [`2.x` branch](https://github.com/reactphp/async/tree/2.x) (PHP 5.3+) - -This update involves some major new features and a minor BC break over the -`v3.0.0` release. We've tried hard to avoid BC breaks where possible and -minimize impact otherwise. We expect that most consumers of this package will be -affected by BC breaks, but updating should take no longer than a few minutes. -See below for more details: - -* Feature / BC break: Require PHP 8.1+ and add `mixed` type declarations. - (#14 by @clue) - -* Feature: Add Fiber-based `async()` and `await()` functions. - (#15, #18, #19 and #20 by @WyriHaximus and #26, #28, #30, #32, #34, #55 and #57 by @clue) - -* Project maintenance, rename `main` branch to `4.x` and update installation instructions. - (#29 by @clue) - -The following changes had to be ported to this release due to our branching -strategy, but also appeared in the `v3.0.0` release: - -* Feature: Support iterable type for `parallel()` + `series()` + `waterfall()`. - (#49 by @clue) - -* Feature: Forward compatibility with upcoming Promise v3. - (#48 by @clue) - -* Minor documentation improvements. - (#36 by @SimonFrings and #51 by @nhedger) - -## 3.0.0 (2022-07-11) - -See [`3.x` CHANGELOG](https://github.com/reactphp/async/blob/3.x/CHANGELOG.md) for more details. - -## 2.0.0 (2022-07-11) - -See [`2.x` CHANGELOG](https://github.com/reactphp/async/blob/2.x/CHANGELOG.md) for more details. - -## 1.0.0 (2013-02-07) - -* First tagged release diff --git a/vendor/react/async/LICENSE b/vendor/react/async/LICENSE deleted file mode 100644 index 44acaef..0000000 --- a/vendor/react/async/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -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/async/README.md b/vendor/react/async/README.md deleted file mode 100644 index 9a49cde..0000000 --- a/vendor/react/async/README.md +++ /dev/null @@ -1,672 +0,0 @@ -# Async Utilities - -[![CI status](https://github.com/reactphp/async/workflows/CI/badge.svg)](https://github.com/reactphp/async/actions) -[![installs on Packagist](https://img.shields.io/packagist/dt/react/async?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/async) - -Async utilities and fibers for [ReactPHP](https://reactphp.org/). - -This library allows you to manage async control flow. It provides a number of -combinators for [Promise](https://github.com/reactphp/promise)-based APIs. -Instead of nesting or chaining promise callbacks, you can declare them as a -list, which is resolved sequentially in an async manner. -React/Async will not automagically change blocking code to be async. You need -to have an actual event loop and non-blocking libraries interacting with that -event loop for it to work. As long as you have a Promise-based API that runs in -an event loop, it can be used with this library. - -**Table of Contents** - -* [Usage](#usage) - * [async()](#async) - * [await()](#await) - * [coroutine()](#coroutine) - * [delay()](#delay) - * [parallel()](#parallel) - * [series()](#series) - * [waterfall()](#waterfall) -* [Todo](#todo) -* [Install](#install) -* [Tests](#tests) -* [License](#license) - -## Usage - -This lightweight library consists only of a few simple functions. -All functions reside under the `React\Async` namespace. - -The below examples refer to all functions with their fully-qualified names like this: - -```php -React\Async\await(…); -``` - -As of PHP 5.6+ you can also import each required function into your code like this: - -```php -use function React\Async\await; - -await(…); -``` - -Alternatively, you can also use an import statement similar to this: - -```php -use React\Async; - -Async\await(…); -``` - -### async() - -The `async(callable():(PromiseInterface|T) $function): (callable():PromiseInterface)` function can be used to -return an async function for a function that uses [`await()`](#await) internally. - -This function is specifically designed to complement the [`await()` function](#await). -The [`await()` function](#await) can be considered *blocking* from the -perspective of the calling code. You can avoid this blocking behavior by -wrapping it in an `async()` function call. Everything inside this function -will still be blocked, but everything outside this function can be executed -asynchronously without blocking: - -```php -Loop::addTimer(0.5, React\Async\async(function () { - echo 'a'; - React\Async\await(React\Promise\Timer\sleep(1.0)); - echo 'c'; -})); - -Loop::addTimer(1.0, function () { - echo 'b'; -}); - -// prints "a" at t=0.5s -// prints "b" at t=1.0s -// prints "c" at t=1.5s -``` - -See also the [`await()` function](#await) for more details. - -Note that this function only works in tandem with the [`await()` function](#await). -In particular, this function does not "magically" make any blocking function -non-blocking: - -```php -Loop::addTimer(0.5, React\Async\async(function () { - echo 'a'; - sleep(1); // broken: using PHP's blocking sleep() for demonstration purposes - echo 'c'; -})); - -Loop::addTimer(1.0, function () { - echo 'b'; -}); - -// prints "a" at t=0.5s -// prints "c" at t=1.5s: Correct timing, but wrong order -// prints "b" at t=1.5s: Triggered too late because it was blocked -``` - -As an alternative, you should always make sure to use this function in tandem -with the [`await()` function](#await) and an async API returning a promise -as shown in the previous example. - -The `async()` function is specifically designed for cases where it is used -as a callback (such as an event loop timer, event listener, or promise -callback). For this reason, it returns a new function wrapping the given -`$function` instead of directly invoking it and returning its value. - -```php -use function React\Async\async; - -Loop::addTimer(1.0, async(function () { … })); -$connection->on('close', async(function () { … })); -$stream->on('data', async(function ($data) { … })); -$promise->then(async(function (int $result) { … })); -``` - -You can invoke this wrapping function to invoke the given `$function` with -any arguments given as-is. The function will always return a Promise which -will be fulfilled with whatever your `$function` returns. Likewise, it will -return a promise that will be rejected if you throw an `Exception` or -`Throwable` from your `$function`. This allows you to easily create -Promise-based functions: - -```php -$promise = React\Async\async(function (): int { - $browser = new React\Http\Browser(); - $urls = [ - 'https://example.com/alice', - 'https://example.com/bob' - ]; - - $bytes = 0; - foreach ($urls as $url) { - $response = React\Async\await($browser->get($url)); - assert($response instanceof Psr\Http\Message\ResponseInterface); - $bytes += $response->getBody()->getSize(); - } - return $bytes; -})(); - -$promise->then(function (int $bytes) { - echo 'Total size: ' . $bytes . PHP_EOL; -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -The previous example uses [`await()`](#await) inside a loop to highlight how -this vastly simplifies consuming asynchronous operations. At the same time, -this naive example does not leverage concurrent execution, as it will -essentially "await" between each operation. In order to take advantage of -concurrent execution within the given `$function`, you can "await" multiple -promises by using a single [`await()`](#await) together with Promise-based -primitives like this: - -```php -$promise = React\Async\async(function (): int { - $browser = new React\Http\Browser(); - $urls = [ - 'https://example.com/alice', - 'https://example.com/bob' - ]; - - $promises = []; - foreach ($urls as $url) { - $promises[] = $browser->get($url); - } - - try { - $responses = React\Async\await(React\Promise\all($promises)); - } catch (Exception $e) { - foreach ($promises as $promise) { - $promise->cancel(); - } - throw $e; - } - - $bytes = 0; - foreach ($responses as $response) { - assert($response instanceof Psr\Http\Message\ResponseInterface); - $bytes += $response->getBody()->getSize(); - } - return $bytes; -})(); - -$promise->then(function (int $bytes) { - echo 'Total size: ' . $bytes . PHP_EOL; -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -The returned promise is implemented in such a way that it can be cancelled -when it is still pending. Cancelling a pending promise will cancel any awaited -promises inside that fiber or any nested fibers. As such, the following example -will only output `ab` and cancel the pending [`delay()`](#delay). -The [`await()`](#await) calls in this example would throw a `RuntimeException` -from the cancelled [`delay()`](#delay) call that bubbles up through the fibers. - -```php -$promise = async(static function (): int { - echo 'a'; - await(async(static function (): void { - echo 'b'; - delay(2); - echo 'c'; - })()); - echo 'd'; - - return time(); -})(); - -$promise->cancel(); -await($promise); -``` - -### await() - -The `await(PromiseInterface $promise): T` function can be used to -block waiting for the given `$promise` to be fulfilled. - -```php -$result = React\Async\await($promise); -``` - -This function will only return after the given `$promise` has settled, i.e. -either fulfilled or rejected. While the promise is pending, this function -can be considered *blocking* from the perspective of the calling code. -You can avoid this blocking behavior by wrapping it in an [`async()` function](#async) -call. Everything inside this function will still be blocked, but everything -outside this function can be executed asynchronously without blocking: - -```php -Loop::addTimer(0.5, React\Async\async(function () { - echo 'a'; - React\Async\await(React\Promise\Timer\sleep(1.0)); - echo 'c'; -})); - -Loop::addTimer(1.0, function () { - echo 'b'; -}); - -// prints "a" at t=0.5s -// prints "b" at t=1.0s -// prints "c" at t=1.5s -``` - -See also the [`async()` function](#async) for more details. - -Once the promise is fulfilled, this function will return whatever the promise -resolved to. - -Once the promise is rejected, this will throw whatever the promise rejected -with. If the promise did not reject with an `Exception` or `Throwable`, then -this function will throw an `UnexpectedValueException` instead. - -```php -try { - $result = React\Async\await($promise); - // promise successfully fulfilled with $result - echo 'Result: ' . $result; -} catch (Throwable $e) { - // promise rejected with $e - echo 'Error: ' . $e->getMessage(); -} -``` - -### coroutine() - -The `coroutine(callable(mixed ...$args):(\Generator|PromiseInterface|T) $function, mixed ...$args): PromiseInterface` function can be used to -execute a Generator-based coroutine to "await" promises. - -```php -React\Async\coroutine(function () { - $browser = new React\Http\Browser(); - - try { - $response = yield $browser->get('https://example.com/'); - assert($response instanceof Psr\Http\Message\ResponseInterface); - echo $response->getBody(); - } catch (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; - } -}); -``` - -Using Generator-based coroutines is an alternative to directly using the -underlying promise APIs. For many use cases, this makes using promise-based -APIs much simpler, as it resembles a synchronous code flow more closely. -The above example performs the equivalent of directly using the promise APIs: - -```php -$browser = new React\Http\Browser(); - -$browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { - echo $response->getBody(); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -The `yield` keyword can be used to "await" a promise resolution. Internally, -it will turn the entire given `$function` into a [`Generator`](https://www.php.net/manual/en/class.generator.php). -This allows the execution to be interrupted and resumed at the same place -when the promise is fulfilled. The `yield` statement returns whatever the -promise is fulfilled with. If the promise is rejected, it will throw an -`Exception` or `Throwable`. - -The `coroutine()` function will always return a Promise which will be -fulfilled with whatever your `$function` returns. Likewise, it will return -a promise that will be rejected if you throw an `Exception` or `Throwable` -from your `$function`. This allows you to easily create Promise-based -functions: - -```php -$promise = React\Async\coroutine(function () { - $browser = new React\Http\Browser(); - $urls = [ - 'https://example.com/alice', - 'https://example.com/bob' - ]; - - $bytes = 0; - foreach ($urls as $url) { - $response = yield $browser->get($url); - assert($response instanceof Psr\Http\Message\ResponseInterface); - $bytes += $response->getBody()->getSize(); - } - return $bytes; -}); - -$promise->then(function (int $bytes) { - echo 'Total size: ' . $bytes . PHP_EOL; -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -The previous example uses a `yield` statement inside a loop to highlight how -this vastly simplifies consuming asynchronous operations. At the same time, -this naive example does not leverage concurrent execution, as it will -essentially "await" between each operation. In order to take advantage of -concurrent execution within the given `$function`, you can "await" multiple -promises by using a single `yield` together with Promise-based primitives -like this: - -```php -$promise = React\Async\coroutine(function () { - $browser = new React\Http\Browser(); - $urls = [ - 'https://example.com/alice', - 'https://example.com/bob' - ]; - - $promises = []; - foreach ($urls as $url) { - $promises[] = $browser->get($url); - } - - try { - $responses = yield React\Promise\all($promises); - } catch (Exception $e) { - foreach ($promises as $promise) { - $promise->cancel(); - } - throw $e; - } - - $bytes = 0; - foreach ($responses as $response) { - assert($response instanceof Psr\Http\Message\ResponseInterface); - $bytes += $response->getBody()->getSize(); - } - return $bytes; -}); - -$promise->then(function (int $bytes) { - echo 'Total size: ' . $bytes . PHP_EOL; -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -### delay() - -The `delay(float $seconds): void` function can be used to -delay program execution for duration given in `$seconds`. - -```php -React\Async\delay($seconds); -``` - -This function will only return after the given number of `$seconds` have -elapsed. If there are no other events attached to this loop, it will behave -similar to PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php). - -```php -echo 'a'; -React\Async\delay(1.0); -echo 'b'; - -// prints "a" at t=0.0s -// prints "b" at t=1.0s -``` - -Unlike PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php), -this function may not necessarily halt execution of the entire process thread. -Instead, it allows the event loop to run any other events attached to the -same loop until the delay returns: - -```php -echo 'a'; -Loop::addTimer(1.0, function (): void { - echo 'b'; -}); -React\Async\delay(3.0); -echo 'c'; - -// prints "a" at t=0.0s -// prints "b" at t=1.0s -// prints "c" at t=3.0s -``` - -This behavior is especially useful if you want to delay the program execution -of a particular routine, such as when building a simple polling or retry -mechanism: - -```php -try { - something(); -} catch (Throwable) { - // in case of error, retry after a short delay - React\Async\delay(1.0); - something(); -} -``` - -Because this function only returns after some time has passed, it can be -considered *blocking* from the perspective of the calling code. You can avoid -this blocking behavior by wrapping it in an [`async()` function](#async) call. -Everything inside this function will still be blocked, but everything outside -this function can be executed asynchronously without blocking: - -```php -Loop::addTimer(0.5, React\Async\async(function (): void { - echo 'a'; - React\Async\delay(1.0); - echo 'c'; -})); - -Loop::addTimer(1.0, function (): void { - echo 'b'; -}); - -// prints "a" at t=0.5s -// prints "b" at t=1.0s -// prints "c" at t=1.5s -``` - -See also the [`async()` function](#async) for more details. - -Internally, the `$seconds` argument will be used as a timer for the loop so that -it keeps running until this timer triggers. This implies that if you pass a -really small (or negative) value, it will still start a timer and will thus -trigger at the earliest possible time in the future. - -The function is implemented in such a way that it can be cancelled when it is -running inside an [`async()` function](#async). Cancelling the resulting -promise will clean up any pending timers and throw a `RuntimeException` from -the pending delay which in turn would reject the resulting promise. - -```php -$promise = async(function (): void { - echo 'a'; - delay(3.0); - echo 'b'; -})(); - -Loop::addTimer(2.0, function () use ($promise): void { - $promise->cancel(); -}); - -// prints "a" at t=0.0s -// rejects $promise at t=2.0 -// never prints "b" -``` - -### parallel() - -The `parallel(iterable> $tasks): PromiseInterface>` function can be used -like this: - -```php -then(function (array $results) { - foreach ($results as $result) { - var_dump($result); - } -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -### series() - -The `series(iterable> $tasks): PromiseInterface>` function can be used -like this: - -```php -then(function (array $results) { - foreach ($results as $result) { - var_dump($result); - } -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -### waterfall() - -The `waterfall(iterable> $tasks): PromiseInterface` function can be used -like this: - -```php -then(function ($prev) { - echo "Final result is $prev\n"; -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -## Todo - - * Implement queue() - -## Install - -The recommended way to install this library is [through Composer](https://getcomposer.org/). -[New to Composer?](https://getcomposer.org/doc/00-intro.md) - -This project follows [SemVer](https://semver.org/). -This will install the latest supported version from this branch: - -```bash -composer require react/async:^4.3 -``` - -See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. - -This project aims to run on any platform and thus does not require any PHP -extensions and supports running on PHP 8.1+. -It's *highly recommended to use the latest supported PHP version* for this project. - -We're committed to providing long-term support (LTS) options and to provide a -smooth upgrade path. If you're using an older PHP version, you may use the -[`3.x` branch](https://github.com/reactphp/async/tree/3.x) (PHP 7.1+) or -[`2.x` branch](https://github.com/reactphp/async/tree/2.x) (PHP 5.3+) which both -provide a compatible API but do not take advantage of newer language features. -You may target multiple versions at the same time to support a wider range of -PHP versions like this: - -```bash -composer require "react/async:^4 || ^3 || ^2" -``` - -## Tests - -To run the test suite, you first need to clone this repo and then install all -dependencies [through Composer](https://getcomposer.org/): - -```bash -composer install -``` - -To run the test suite, go to the project root and run: - -```bash -vendor/bin/phpunit -``` - -On top of this, we use PHPStan on max level to ensure type safety across the project: - -```bash -vendor/bin/phpstan -``` - -## License - -MIT, see [LICENSE file](LICENSE). - -This project is heavily influenced by [async.js](https://github.com/caolan/async). diff --git a/vendor/react/async/composer.json b/vendor/react/async/composer.json deleted file mode 100644 index 5d4082b..0000000 --- a/vendor/react/async/composer.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "react/async", - "description": "Async utilities and fibers for ReactPHP", - "keywords": ["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": ">=8.1", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.8 || ^1.2.1" - }, - "require-dev": { - "phpstan/phpstan": "1.10.39", - "phpunit/phpunit": "^9.6" - }, - "autoload": { - "psr-4": { - "React\\Async\\": "src/" - }, - "files": [ - "src/functions_include.php" - ] - }, - "autoload-dev": { - "psr-4": { - "React\\Tests\\Async\\": "tests/" - } - } -} diff --git a/vendor/react/async/src/FiberFactory.php b/vendor/react/async/src/FiberFactory.php deleted file mode 100644 index ee90818..0000000 --- a/vendor/react/async/src/FiberFactory.php +++ /dev/null @@ -1,33 +0,0 @@ - new SimpleFiber(); - } -} diff --git a/vendor/react/async/src/FiberInterface.php b/vendor/react/async/src/FiberInterface.php deleted file mode 100644 index e40304e..0000000 --- a/vendor/react/async/src/FiberInterface.php +++ /dev/null @@ -1,23 +0,0 @@ -> */ - private static array $map = []; - - /** - * @param \Fiber $fiber - * @param PromiseInterface $promise - */ - public static function setPromise(\Fiber $fiber, PromiseInterface $promise): void - { - self::$map[\spl_object_id($fiber)] = $promise; - } - - /** - * @param \Fiber $fiber - */ - public static function unsetPromise(\Fiber $fiber): void - { - unset(self::$map[\spl_object_id($fiber)]); - } - - /** - * @param \Fiber $fiber - * @return ?PromiseInterface - */ - public static function getPromise(\Fiber $fiber): ?PromiseInterface - { - return self::$map[\spl_object_id($fiber)] ?? null; - } -} diff --git a/vendor/react/async/src/SimpleFiber.php b/vendor/react/async/src/SimpleFiber.php deleted file mode 100644 index 8c5460a..0000000 --- a/vendor/react/async/src/SimpleFiber.php +++ /dev/null @@ -1,79 +0,0 @@ - */ - private static ?\Fiber $scheduler = null; - - private static ?\Closure $suspend = null; - - /** @var ?\Fiber */ - private ?\Fiber $fiber = null; - - public function __construct() - { - $this->fiber = \Fiber::getCurrent(); - } - - public function resume(mixed $value): void - { - if ($this->fiber !== null) { - $this->fiber->resume($value); - } else { - self::$suspend = static fn() => $value; - } - - if (self::$suspend !== null && \Fiber::getCurrent() === self::$scheduler) { - $suspend = self::$suspend; - self::$suspend = null; - - \Fiber::suspend($suspend); - } - } - - public function throw(\Throwable $throwable): void - { - if ($this->fiber !== null) { - $this->fiber->throw($throwable); - } else { - self::$suspend = static fn() => throw $throwable; - } - - if (self::$suspend !== null && \Fiber::getCurrent() === self::$scheduler) { - $suspend = self::$suspend; - self::$suspend = null; - - \Fiber::suspend($suspend); - } - } - - public function suspend(): mixed - { - if ($this->fiber === null) { - if (self::$scheduler === null || self::$scheduler->isTerminated()) { - self::$scheduler = new \Fiber(static fn() => Loop::run()); - // Run event loop to completion on shutdown. - \register_shutdown_function(static function (): void { - assert(self::$scheduler instanceof \Fiber); - if (self::$scheduler->isSuspended()) { - self::$scheduler->resume(); - } - }); - } - - $ret = (self::$scheduler->isStarted() ? self::$scheduler->resume() : self::$scheduler->start()); - assert(\is_callable($ret)); - - return $ret(); - } - - return \Fiber::suspend(); - } -} diff --git a/vendor/react/async/src/functions.php b/vendor/react/async/src/functions.php deleted file mode 100644 index bcf40c1..0000000 --- a/vendor/react/async/src/functions.php +++ /dev/null @@ -1,846 +0,0 @@ -on('close', async(function () { … })); - * $stream->on('data', async(function ($data) { … })); - * $promise->then(async(function (int $result) { … })); - * ``` - * - * You can invoke this wrapping function to invoke the given `$function` with - * any arguments given as-is. The function will always return a Promise which - * will be fulfilled with whatever your `$function` returns. Likewise, it will - * return a promise that will be rejected if you throw an `Exception` or - * `Throwable` from your `$function`. This allows you to easily create - * Promise-based functions: - * - * ```php - * $promise = React\Async\async(function (): int { - * $browser = new React\Http\Browser(); - * $urls = [ - * 'https://example.com/alice', - * 'https://example.com/bob' - * ]; - * - * $bytes = 0; - * foreach ($urls as $url) { - * $response = React\Async\await($browser->get($url)); - * assert($response instanceof Psr\Http\Message\ResponseInterface); - * $bytes += $response->getBody()->getSize(); - * } - * return $bytes; - * })(); - * - * $promise->then(function (int $bytes) { - * echo 'Total size: ' . $bytes . PHP_EOL; - * }, function (Exception $e) { - * echo 'Error: ' . $e->getMessage() . PHP_EOL; - * }); - * ``` - * - * The previous example uses [`await()`](#await) inside a loop to highlight how - * this vastly simplifies consuming asynchronous operations. At the same time, - * this naive example does not leverage concurrent execution, as it will - * essentially "await" between each operation. In order to take advantage of - * concurrent execution within the given `$function`, you can "await" multiple - * promises by using a single [`await()`](#await) together with Promise-based - * primitives like this: - * - * ```php - * $promise = React\Async\async(function (): int { - * $browser = new React\Http\Browser(); - * $urls = [ - * 'https://example.com/alice', - * 'https://example.com/bob' - * ]; - * - * $promises = []; - * foreach ($urls as $url) { - * $promises[] = $browser->get($url); - * } - * - * try { - * $responses = React\Async\await(React\Promise\all($promises)); - * } catch (Exception $e) { - * foreach ($promises as $promise) { - * $promise->cancel(); - * } - * throw $e; - * } - * - * $bytes = 0; - * foreach ($responses as $response) { - * assert($response instanceof Psr\Http\Message\ResponseInterface); - * $bytes += $response->getBody()->getSize(); - * } - * return $bytes; - * })(); - * - * $promise->then(function (int $bytes) { - * echo 'Total size: ' . $bytes . PHP_EOL; - * }, function (Exception $e) { - * echo 'Error: ' . $e->getMessage() . PHP_EOL; - * }); - * ``` - * - * The returned promise is implemented in such a way that it can be cancelled - * when it is still pending. Cancelling a pending promise will cancel any awaited - * promises inside that fiber or any nested fibers. As such, the following example - * will only output `ab` and cancel the pending [`delay()`](#delay). - * The [`await()`](#await) calls in this example would throw a `RuntimeException` - * from the cancelled [`delay()`](#delay) call that bubbles up through the fibers. - * - * ```php - * $promise = async(static function (): int { - * echo 'a'; - * await(async(static function (): void { - * echo 'b'; - * delay(2); - * echo 'c'; - * })()); - * echo 'd'; - * - * return time(); - * })(); - * - * $promise->cancel(); - * await($promise); - * ``` - * - * @template T - * @template A1 (any number of function arguments, see https://github.com/phpstan/phpstan/issues/8214) - * @template A2 - * @template A3 - * @template A4 - * @template A5 - * @param callable(A1,A2,A3,A4,A5): (PromiseInterface|T) $function - * @return callable(A1=,A2=,A3=,A4=,A5=): PromiseInterface - * @since 4.0.0 - * @see coroutine() - */ -function async(callable $function): callable -{ - return static function (mixed ...$args) use ($function): PromiseInterface { - $fiber = null; - /** @var PromiseInterface $promise*/ - $promise = new Promise(function (callable $resolve, callable $reject) use ($function, $args, &$fiber): void { - $fiber = new \Fiber(function () use ($resolve, $reject, $function, $args, &$fiber): void { - try { - $resolve($function(...$args)); - } catch (\Throwable $exception) { - $reject($exception); - } finally { - assert($fiber instanceof \Fiber); - FiberMap::unsetPromise($fiber); - } - }); - - $fiber->start(); - }, function () use (&$fiber): void { - assert($fiber instanceof \Fiber); - $promise = FiberMap::getPromise($fiber); - if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { - $promise->cancel(); - } - }); - - $lowLevelFiber = \Fiber::getCurrent(); - if ($lowLevelFiber !== null) { - FiberMap::setPromise($lowLevelFiber, $promise); - } - - return $promise; - }; -} - -/** - * Block waiting for the given `$promise` to be fulfilled. - * - * ```php - * $result = React\Async\await($promise); - * ``` - * - * This function will only return after the given `$promise` has settled, i.e. - * either fulfilled or rejected. While the promise is pending, this function - * can be considered *blocking* from the perspective of the calling code. - * You can avoid this blocking behavior by wrapping it in an [`async()` function](#async) - * call. Everything inside this function will still be blocked, but everything - * outside this function can be executed asynchronously without blocking: - * - * ```php - * Loop::addTimer(0.5, React\Async\async(function () { - * echo 'a'; - * React\Async\await(React\Promise\Timer\sleep(1.0)); - * echo 'c'; - * })); - * - * Loop::addTimer(1.0, function () { - * echo 'b'; - * }); - * - * // prints "a" at t=0.5s - * // prints "b" at t=1.0s - * // prints "c" at t=1.5s - * ``` - * - * See also the [`async()` function](#async) for more details. - * - * Once the promise is fulfilled, this function will return whatever the promise - * resolved to. - * - * Once the promise is rejected, this will throw whatever the promise rejected - * with. If the promise did not reject with an `Exception` or `Throwable`, then - * this function will throw an `UnexpectedValueException` instead. - * - * ```php - * try { - * $result = React\Async\await($promise); - * // promise successfully fulfilled with $result - * echo 'Result: ' . $result; - * } catch (Throwable $e) { - * // promise rejected with $e - * echo 'Error: ' . $e->getMessage(); - * } - * ``` - * - * @template T - * @param PromiseInterface $promise - * @return T returns whatever the promise resolves to - * @throws \Exception when the promise is rejected with an `Exception` - * @throws \Throwable when the promise is rejected with a `Throwable` - * @throws \UnexpectedValueException when the promise is rejected with an unexpected value (Promise API v1 or v2 only) - */ -function await(PromiseInterface $promise): mixed -{ - $fiber = null; - $resolved = false; - $rejected = false; - - /** @var T $resolvedValue */ - $resolvedValue = null; - $rejectedThrowable = null; - $lowLevelFiber = \Fiber::getCurrent(); - - $promise->then( - function (mixed $value) use (&$resolved, &$resolvedValue, &$fiber, $lowLevelFiber): void { - if ($lowLevelFiber !== null) { - FiberMap::unsetPromise($lowLevelFiber); - } - - /** @var ?\Fiber $fiber */ - if ($fiber === null) { - $resolved = true; - /** @var T $resolvedValue */ - $resolvedValue = $value; - return; - } - - $fiber->resume($value); - }, - function (mixed $throwable) use (&$rejected, &$rejectedThrowable, &$fiber, $lowLevelFiber): void { - if ($lowLevelFiber !== null) { - FiberMap::unsetPromise($lowLevelFiber); - } - - if (!$throwable instanceof \Throwable) { - $throwable = new \UnexpectedValueException( - 'Promise rejected with unexpected value of type ' . (is_object($throwable) ? get_class($throwable) : gettype($throwable)) /** @phpstan-ignore-line */ - ); - - // avoid garbage references by replacing all closures in call stack. - // what a lovely piece of code! - $r = new \ReflectionProperty('Exception', 'trace'); - $trace = $r->getValue($throwable); - assert(\is_array($trace)); - - // Exception trace arguments only available when zend.exception_ignore_args is not set - // @codeCoverageIgnoreStart - foreach ($trace as $ti => $one) { - if (isset($one['args'])) { - foreach ($one['args'] as $ai => $arg) { - if ($arg instanceof \Closure) { - $trace[$ti]['args'][$ai] = 'Object(' . \get_class($arg) . ')'; - } - } - } - } - // @codeCoverageIgnoreEnd - $r->setValue($throwable, $trace); - } - - if ($fiber === null) { - $rejected = true; - $rejectedThrowable = $throwable; - return; - } - - $fiber->throw($throwable); - } - ); - - if ($resolved) { - return $resolvedValue; - } - - if ($rejected) { - assert($rejectedThrowable instanceof \Throwable); - throw $rejectedThrowable; - } - - if ($lowLevelFiber !== null) { - FiberMap::setPromise($lowLevelFiber, $promise); - } - - $fiber = FiberFactory::create(); - - return $fiber->suspend(); -} - -/** - * Delay program execution for duration given in `$seconds`. - * - * ```php - * React\Async\delay($seconds); - * ``` - * - * This function will only return after the given number of `$seconds` have - * elapsed. If there are no other events attached to this loop, it will behave - * similar to PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php). - * - * ```php - * echo 'a'; - * React\Async\delay(1.0); - * echo 'b'; - * - * // prints "a" at t=0.0s - * // prints "b" at t=1.0s - * ``` - * - * Unlike PHP's [`sleep()` function](https://www.php.net/manual/en/function.sleep.php), - * this function may not necessarily halt execution of the entire process thread. - * Instead, it allows the event loop to run any other events attached to the - * same loop until the delay returns: - * - * ```php - * echo 'a'; - * Loop::addTimer(1.0, function (): void { - * echo 'b'; - * }); - * React\Async\delay(3.0); - * echo 'c'; - * - * // prints "a" at t=0.0s - * // prints "b" at t=1.0s - * // prints "c" at t=3.0s - * ``` - * - * This behavior is especially useful if you want to delay the program execution - * of a particular routine, such as when building a simple polling or retry - * mechanism: - * - * ```php - * try { - * something(); - * } catch (Throwable) { - * // in case of error, retry after a short delay - * React\Async\delay(1.0); - * something(); - * } - * ``` - * - * Because this function only returns after some time has passed, it can be - * considered *blocking* from the perspective of the calling code. You can avoid - * this blocking behavior by wrapping it in an [`async()` function](#async) call. - * Everything inside this function will still be blocked, but everything outside - * this function can be executed asynchronously without blocking: - * - * ```php - * Loop::addTimer(0.5, React\Async\async(function (): void { - * echo 'a'; - * React\Async\delay(1.0); - * echo 'c'; - * })); - * - * Loop::addTimer(1.0, function (): void { - * echo 'b'; - * }); - * - * // prints "a" at t=0.5s - * // prints "b" at t=1.0s - * // prints "c" at t=1.5s - * ``` - * - * See also the [`async()` function](#async) for more details. - * - * Internally, the `$seconds` argument will be used as a timer for the loop so that - * it keeps running until this timer triggers. This implies that if you pass a - * really small (or negative) value, it will still start a timer and will thus - * trigger at the earliest possible time in the future. - * - * The function is implemented in such a way that it can be cancelled when it is - * running inside an [`async()` function](#async). Cancelling the resulting - * promise will clean up any pending timers and throw a `RuntimeException` from - * the pending delay which in turn would reject the resulting promise. - * - * ```php - * $promise = async(function (): void { - * echo 'a'; - * delay(3.0); - * echo 'b'; - * })(); - * - * Loop::addTimer(2.0, function () use ($promise): void { - * $promise->cancel(); - * }); - * - * // prints "a" at t=0.0s - * // rejects $promise at t=2.0 - * // never prints "b" - * ``` - * - * @return void - * @throws \RuntimeException when the function is cancelled inside an `async()` function - * @see async() - * @uses await() - */ -function delay(float $seconds): void -{ - /** @var ?TimerInterface $timer */ - $timer = null; - - await(new Promise(function (callable $resolve) use ($seconds, &$timer): void { - $timer = Loop::addTimer($seconds, fn() => $resolve(null)); - }, function () use (&$timer): void { - assert($timer instanceof TimerInterface); - Loop::cancelTimer($timer); - throw new \RuntimeException('Delay cancelled'); - })); -} - -/** - * Execute a Generator-based coroutine to "await" promises. - * - * ```php - * React\Async\coroutine(function () { - * $browser = new React\Http\Browser(); - * - * try { - * $response = yield $browser->get('https://example.com/'); - * assert($response instanceof Psr\Http\Message\ResponseInterface); - * echo $response->getBody(); - * } catch (Exception $e) { - * echo 'Error: ' . $e->getMessage() . PHP_EOL; - * } - * }); - * ``` - * - * Using Generator-based coroutines is an alternative to directly using the - * underlying promise APIs. For many use cases, this makes using promise-based - * APIs much simpler, as it resembles a synchronous code flow more closely. - * The above example performs the equivalent of directly using the promise APIs: - * - * ```php - * $browser = new React\Http\Browser(); - * - * $browser->get('https://example.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { - * echo $response->getBody(); - * }, function (Exception $e) { - * echo 'Error: ' . $e->getMessage() . PHP_EOL; - * }); - * ``` - * - * The `yield` keyword can be used to "await" a promise resolution. Internally, - * it will turn the entire given `$function` into a [`Generator`](https://www.php.net/manual/en/class.generator.php). - * This allows the execution to be interrupted and resumed at the same place - * when the promise is fulfilled. The `yield` statement returns whatever the - * promise is fulfilled with. If the promise is rejected, it will throw an - * `Exception` or `Throwable`. - * - * The `coroutine()` function will always return a Promise which will be - * fulfilled with whatever your `$function` returns. Likewise, it will return - * a promise that will be rejected if you throw an `Exception` or `Throwable` - * from your `$function`. This allows you to easily create Promise-based - * functions: - * - * ```php - * $promise = React\Async\coroutine(function () { - * $browser = new React\Http\Browser(); - * $urls = [ - * 'https://example.com/alice', - * 'https://example.com/bob' - * ]; - * - * $bytes = 0; - * foreach ($urls as $url) { - * $response = yield $browser->get($url); - * assert($response instanceof Psr\Http\Message\ResponseInterface); - * $bytes += $response->getBody()->getSize(); - * } - * return $bytes; - * }); - * - * $promise->then(function (int $bytes) { - * echo 'Total size: ' . $bytes . PHP_EOL; - * }, function (Exception $e) { - * echo 'Error: ' . $e->getMessage() . PHP_EOL; - * }); - * ``` - * - * The previous example uses a `yield` statement inside a loop to highlight how - * this vastly simplifies consuming asynchronous operations. At the same time, - * this naive example does not leverage concurrent execution, as it will - * essentially "await" between each operation. In order to take advantage of - * concurrent execution within the given `$function`, you can "await" multiple - * promises by using a single `yield` together with Promise-based primitives - * like this: - * - * ```php - * $promise = React\Async\coroutine(function () { - * $browser = new React\Http\Browser(); - * $urls = [ - * 'https://example.com/alice', - * 'https://example.com/bob' - * ]; - * - * $promises = []; - * foreach ($urls as $url) { - * $promises[] = $browser->get($url); - * } - * - * try { - * $responses = yield React\Promise\all($promises); - * } catch (Exception $e) { - * foreach ($promises as $promise) { - * $promise->cancel(); - * } - * throw $e; - * } - * - * $bytes = 0; - * foreach ($responses as $response) { - * assert($response instanceof Psr\Http\Message\ResponseInterface); - * $bytes += $response->getBody()->getSize(); - * } - * return $bytes; - * }); - * - * $promise->then(function (int $bytes) { - * echo 'Total size: ' . $bytes . PHP_EOL; - * }, function (Exception $e) { - * echo 'Error: ' . $e->getMessage() . PHP_EOL; - * }); - * ``` - * - * @template T - * @template TYield - * @template A1 (any number of function arguments, see https://github.com/phpstan/phpstan/issues/8214) - * @template A2 - * @template A3 - * @template A4 - * @template A5 - * @param callable(A1, A2, A3, A4, A5):(\Generator, TYield, PromiseInterface|T>|PromiseInterface|T) $function - * @param mixed ...$args Optional list of additional arguments that will be passed to the given `$function` as is - * @return PromiseInterface - * @since 3.0.0 - */ -function coroutine(callable $function, mixed ...$args): PromiseInterface -{ - try { - $generator = $function(...$args); - } catch (\Throwable $e) { - return reject($e); - } - - if (!$generator instanceof \Generator) { - return resolve($generator); - } - - $promise = null; - /** @var Deferred $deferred*/ - $deferred = new Deferred(function () use (&$promise) { - /** @var ?PromiseInterface $promise */ - if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { - $promise->cancel(); - } - $promise = null; - }); - - /** @var callable $next */ - $next = function () use ($deferred, $generator, &$next, &$promise) { - try { - if (!$generator->valid()) { - $next = null; - $deferred->resolve($generator->getReturn()); - return; - } - } catch (\Throwable $e) { - $next = null; - $deferred->reject($e); - return; - } - - $promise = $generator->current(); - if (!$promise instanceof PromiseInterface) { - $next = null; - $deferred->reject(new \UnexpectedValueException( - 'Expected coroutine to yield ' . PromiseInterface::class . ', but got ' . (is_object($promise) ? get_class($promise) : gettype($promise)) - )); - return; - } - - /** @var PromiseInterface $promise */ - assert($next instanceof \Closure); - $promise->then(function ($value) use ($generator, $next) { - $generator->send($value); - $next(); - }, function (\Throwable $reason) use ($generator, $next) { - $generator->throw($reason); - $next(); - })->then(null, function (\Throwable $reason) use ($deferred, &$next) { - $next = null; - $deferred->reject($reason); - }); - }; - $next(); - - return $deferred->promise(); -} - -/** - * @template T - * @param iterable|T)> $tasks - * @return PromiseInterface> - */ -function parallel(iterable $tasks): PromiseInterface -{ - /** @var array> $pending */ - $pending = []; - /** @var Deferred> $deferred */ - $deferred = new Deferred(function () use (&$pending) { - foreach ($pending as $promise) { - if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { - $promise->cancel(); - } - } - $pending = []; - }); - $results = []; - $continue = true; - - $taskErrback = function ($error) use (&$pending, $deferred, &$continue) { - $continue = false; - $deferred->reject($error); - - foreach ($pending as $promise) { - if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { - $promise->cancel(); - } - } - $pending = []; - }; - - foreach ($tasks as $i => $task) { - $taskCallback = function ($result) use (&$results, &$pending, &$continue, $i, $deferred) { - $results[$i] = $result; - unset($pending[$i]); - - if (!$pending && !$continue) { - $deferred->resolve($results); - } - }; - - $promise = \call_user_func($task); - assert($promise instanceof PromiseInterface); - $pending[$i] = $promise; - - $promise->then($taskCallback, $taskErrback); - - if (!$continue) { - break; - } - } - - $continue = false; - if (!$pending) { - $deferred->resolve($results); - } - - /** @var PromiseInterface> Remove once defining `Deferred()` above is supported by PHPStan, see https://github.com/phpstan/phpstan/issues/11032 */ - return $deferred->promise(); -} - -/** - * @template T - * @param iterable|T)> $tasks - * @return PromiseInterface> - */ -function series(iterable $tasks): PromiseInterface -{ - $pending = null; - /** @var Deferred> $deferred */ - $deferred = new Deferred(function () use (&$pending) { - /** @var ?PromiseInterface $pending */ - if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) { - $pending->cancel(); - } - $pending = null; - }); - $results = []; - - if ($tasks instanceof \IteratorAggregate) { - $tasks = $tasks->getIterator(); - assert($tasks instanceof \Iterator); - } - - $taskCallback = function ($result) use (&$results, &$next) { - $results[] = $result; - /** @var \Closure $next */ - $next(); - }; - - $next = function () use (&$tasks, $taskCallback, $deferred, &$results, &$pending) { - if ($tasks instanceof \Iterator ? !$tasks->valid() : !$tasks) { - $deferred->resolve($results); - return; - } - - if ($tasks instanceof \Iterator) { - $task = $tasks->current(); - $tasks->next(); - } else { - assert(\is_array($tasks)); - $task = \array_shift($tasks); - } - - assert(\is_callable($task)); - $promise = \call_user_func($task); - assert($promise instanceof PromiseInterface); - $pending = $promise; - - $promise->then($taskCallback, array($deferred, 'reject')); - }; - - $next(); - - /** @var PromiseInterface> Remove once defining `Deferred()` above is supported by PHPStan, see https://github.com/phpstan/phpstan/issues/11032 */ - return $deferred->promise(); -} - -/** - * @template T - * @param iterable<(callable():(PromiseInterface|T))|(callable(mixed):(PromiseInterface|T))> $tasks - * @return PromiseInterface<($tasks is non-empty-array|\Traversable ? T : null)> - */ -function waterfall(iterable $tasks): PromiseInterface -{ - $pending = null; - /** @var Deferred $deferred*/ - $deferred = new Deferred(function () use (&$pending) { - /** @var ?PromiseInterface $pending */ - if ($pending instanceof PromiseInterface && \method_exists($pending, 'cancel')) { - $pending->cancel(); - } - $pending = null; - }); - - if ($tasks instanceof \IteratorAggregate) { - $tasks = $tasks->getIterator(); - assert($tasks instanceof \Iterator); - } - - /** @var callable $next */ - $next = function ($value = null) use (&$tasks, &$next, $deferred, &$pending) { - if ($tasks instanceof \Iterator ? !$tasks->valid() : !$tasks) { - $deferred->resolve($value); - return; - } - - if ($tasks instanceof \Iterator) { - $task = $tasks->current(); - $tasks->next(); - } else { - assert(\is_array($tasks)); - $task = \array_shift($tasks); - } - - assert(\is_callable($task)); - $promise = \call_user_func_array($task, func_get_args()); - assert($promise instanceof PromiseInterface); - $pending = $promise; - - $promise->then($next, array($deferred, 'reject')); - }; - - $next(); - - return $deferred->promise(); -} diff --git a/vendor/react/async/src/functions_include.php b/vendor/react/async/src/functions_include.php deleted file mode 100644 index 05c78fa..0000000 --- a/vendor/react/async/src/functions_include.php +++ /dev/null @@ -1,9 +0,0 @@ - Contains no other changes, so it's actually fully compatible with the v0.6.0 release. - -## 0.6.0 (2019-07-04) - -* Feature / BC break: Add support for `getMultiple()`, `setMultiple()`, `deleteMultiple()`, `clear()` and `has()` - supporting multiple cache items (inspired by PSR-16). - (#32 by @krlv and #37 by @clue) - -* Documentation for TTL precision with millisecond accuracy or below and - use high-resolution timer for cache TTL on PHP 7.3+. - (#35 and #38 by @clue) - -* Improve API documentation and allow legacy HHVM to fail in Travis CI config. - (#34 and #36 by @clue) - -* Prefix all global functions calls with \ to skip the look up and resolve process and go straight to the global function. - (#31 by @WyriHaximus) - -## 0.5.0 (2018-06-25) - -* Improve documentation by describing what is expected of a class implementing `CacheInterface`. - (#21, #22, #23, #27 by @WyriHaximus) - -* Implemented (optional) Least Recently Used (LRU) cache algorithm for `ArrayCache`. - (#26 by @clue) - -* Added support for cache expiration (TTL). - (#29 by @clue and @WyriHaximus) - -* Renamed `remove` to `delete` making it more in line with `PSR-16`. - (#30 by @clue) - -## 0.4.2 (2017-12-20) - -* Improve documentation with usage and installation instructions - (#10 by @clue) - -* Improve test suite by adding PHPUnit to `require-dev` and - add forward compatibility with PHPUnit 5 and PHPUnit 6 and - sanitize Composer autoload paths - (#14 by @shaunbramley and #12 and #18 by @clue) - -## 0.4.1 (2016-02-25) - -* Repository maintenance, split off from main repo, improve test suite and documentation -* First class support for PHP7 and HHVM (#9 by @clue) -* Adjust compatibility to 5.3 (#7 by @clue) - -## 0.4.0 (2014-02-02) - -* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks -* BC break: Update to React/Promise 2.0 -* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0 - -## 0.3.2 (2013-05-10) - -* Version bump - -## 0.3.0 (2013-04-14) - -* Version bump - -## 0.2.6 (2012-12-26) - -* Feature: New cache component, used by DNS diff --git a/vendor/react/cache/LICENSE b/vendor/react/cache/LICENSE deleted file mode 100644 index d6f8901..0000000 --- a/vendor/react/cache/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -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/cache/README.md b/vendor/react/cache/README.md deleted file mode 100644 index 7a86be9..0000000 --- a/vendor/react/cache/README.md +++ /dev/null @@ -1,367 +0,0 @@ -# Cache - -[![CI status](https://github.com/reactphp/cache/actions/workflows/ci.yml/badge.svg)](https://github.com/reactphp/cache/actions) -[![installs on Packagist](https://img.shields.io/packagist/dt/react/cache?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/cache) - -Async, [Promise](https://github.com/reactphp/promise)-based cache interface -for [ReactPHP](https://reactphp.org/). - -The cache component provides a -[Promise](https://github.com/reactphp/promise)-based -[`CacheInterface`](#cacheinterface) and an in-memory [`ArrayCache`](#arraycache) -implementation of that. -This allows consumers to type hint against the interface and third parties to -provide alternate implementations. -This project is heavily inspired by -[PSR-16: Common Interface for Caching Libraries](https://www.php-fig.org/psr/psr-16/), -but uses an interface more suited for async, non-blocking applications. - -**Table of Contents** - -* [Usage](#usage) - * [CacheInterface](#cacheinterface) - * [get()](#get) - * [set()](#set) - * [delete()](#delete) - * [getMultiple()](#getmultiple) - * [setMultiple()](#setmultiple) - * [deleteMultiple()](#deletemultiple) - * [clear()](#clear) - * [has()](#has) - * [ArrayCache](#arraycache) -* [Common usage](#common-usage) - * [Fallback get](#fallback-get) - * [Fallback-get-and-set](#fallback-get-and-set) -* [Install](#install) -* [Tests](#tests) -* [License](#license) - -## Usage - -### CacheInterface - -The `CacheInterface` describes the main interface of this component. -This allows consumers to type hint against the interface and third parties to -provide alternate implementations. - -#### get() - -The `get(string $key, mixed $default = null): PromiseInterface` method can be used to -retrieve an item from the cache. - -This method will resolve with the cached value on success or with the -given `$default` value when no item can be found or when an error occurs. -Similarly, an expired cache item (once the time-to-live is expired) is -considered a cache miss. - -```php -$cache - ->get('foo') - ->then('var_dump'); -``` - -This example fetches the value of the key `foo` and passes it to the -`var_dump` function. You can use any of the composition provided by -[promises](https://github.com/reactphp/promise). - -#### set() - -The `set(string $key, mixed $value, ?float $ttl = null): PromiseInterface` method can be used to -store an item in the cache. - -This method will resolve with `true` on success or `false` when an error -occurs. If the cache implementation has to go over the network to store -it, it may take a while. - -The optional `$ttl` parameter sets the maximum time-to-live in seconds -for this cache item. If this parameter is omitted (or `null`), the item -will stay in the cache for as long as the underlying implementation -supports. Trying to access an expired cache item results in a cache miss, -see also [`get()`](#get). - -```php -$cache->set('foo', 'bar', 60); -``` - -This example eventually sets the value of the key `foo` to `bar`. If it -already exists, it is overridden. - -This interface does not enforce any particular TTL resolution, so special -care may have to be taken if you rely on very high precision with -millisecond accuracy or below. Cache implementations SHOULD work on a -best effort basis and SHOULD provide at least second accuracy unless -otherwise noted. Many existing cache implementations are known to provide -microsecond or millisecond accuracy, but it's generally not recommended -to rely on this high precision. - -This interface suggests that cache implementations SHOULD use a monotonic -time source if available. Given that a monotonic time source is only -available as of PHP 7.3 by default, cache implementations MAY fall back -to using wall-clock time. -While this does not affect many common use cases, this is an important -distinction for programs that rely on a high time precision or on systems -that are subject to discontinuous time adjustments (time jumps). -This means that if you store a cache item with a TTL of 30s and then -adjust your system time forward by 20s, the cache item SHOULD still -expire in 30s. - -#### delete() - -The `delete(string $key): PromiseInterface` method can be used to -delete an item from the cache. - -This method will resolve with `true` on success or `false` when an error -occurs. When no item for `$key` is found in the cache, it also resolves -to `true`. If the cache implementation has to go over the network to -delete it, it may take a while. - -```php -$cache->delete('foo'); -``` - -This example eventually deletes the key `foo` from the cache. As with -`set()`, this may not happen instantly and a promise is returned to -provide guarantees whether or not the item has been removed from cache. - -#### getMultiple() - -The `getMultiple(string[] $keys, mixed $default = null): PromiseInterface` method can be used to -retrieve multiple cache items by their unique keys. - -This method will resolve with an array of cached values on success or with the -given `$default` value when an item can not be found or when an error occurs. -Similarly, an expired cache item (once the time-to-live is expired) is -considered a cache miss. - -```php -$cache->getMultiple(array('name', 'age'))->then(function (array $values) { - $name = $values['name'] ?? 'User'; - $age = $values['age'] ?? 'n/a'; - - echo $name . ' is ' . $age . PHP_EOL; -}); -``` - -This example fetches the cache items for the `name` and `age` keys and -prints some example output. You can use any of the composition provided -by [promises](https://github.com/reactphp/promise). - -#### setMultiple() - -The `setMultiple(array $values, ?float $ttl = null): PromiseInterface` method can be used to -persist a set of key => value pairs in the cache, with an optional TTL. - -This method will resolve with `true` on success or `false` when an error -occurs. If the cache implementation has to go over the network to store -it, it may take a while. - -The optional `$ttl` parameter sets the maximum time-to-live in seconds -for these cache items. If this parameter is omitted (or `null`), these items -will stay in the cache for as long as the underlying implementation -supports. Trying to access an expired cache items results in a cache miss, -see also [`getMultiple()`](#getmultiple). - -```php -$cache->setMultiple(array('foo' => 1, 'bar' => 2), 60); -``` - -This example eventually sets the list of values - the key `foo` to `1` value -and the key `bar` to `2`. If some of the keys already exist, they are overridden. - -#### deleteMultiple() - -The `setMultiple(string[] $keys): PromiseInterface` method can be used to -delete multiple cache items in a single operation. - -This method will resolve with `true` on success or `false` when an error -occurs. When no items for `$keys` are found in the cache, it also resolves -to `true`. If the cache implementation has to go over the network to -delete it, it may take a while. - -```php -$cache->deleteMultiple(array('foo', 'bar, 'baz')); -``` - -This example eventually deletes keys `foo`, `bar` and `baz` from the cache. -As with `setMultiple()`, this may not happen instantly and a promise is returned to -provide guarantees whether or not the item has been removed from cache. - -#### clear() - -The `clear(): PromiseInterface` method can be used to -wipe clean the entire cache. - -This method will resolve with `true` on success or `false` when an error -occurs. If the cache implementation has to go over the network to -delete it, it may take a while. - -```php -$cache->clear(); -``` - -This example eventually deletes all keys from the cache. As with `deleteMultiple()`, -this may not happen instantly and a promise is returned to provide guarantees -whether or not all the items have been removed from cache. - -#### has() - -The `has(string $key): PromiseInterface` method can be used to -determine whether an item is present in the cache. - -This method will resolve with `true` on success or `false` when no item can be found -or when an error occurs. Similarly, an expired cache item (once the time-to-live -is expired) is considered a cache miss. - -```php -$cache - ->has('foo') - ->then('var_dump'); -``` - -This example checks if the value of the key `foo` is set in the cache and passes -the result to the `var_dump` function. You can use any of the composition provided by -[promises](https://github.com/reactphp/promise). - -NOTE: It is recommended that has() is only to be used for cache warming type purposes -and not to be used within your live applications operations for get/set, as this method -is subject to a race condition where your has() will return true and immediately after, -another script can remove it making the state of your app out of date. - -### ArrayCache - -The `ArrayCache` provides an in-memory implementation of the [`CacheInterface`](#cacheinterface). - -```php -$cache = new ArrayCache(); - -$cache->set('foo', 'bar'); -``` - -Its constructor accepts an optional `?int $limit` parameter to limit the -maximum number of entries to store in the LRU cache. If you add more -entries to this instance, it will automatically take care of removing -the one that was least recently used (LRU). - -For example, this snippet will overwrite the first value and only store -the last two entries: - -```php -$cache = new ArrayCache(2); - -$cache->set('foo', '1'); -$cache->set('bar', '2'); -$cache->set('baz', '3'); -``` - -This cache implementation is known to rely on wall-clock time to schedule -future cache expiration times when using any version before PHP 7.3, -because a monotonic time source is only available as of PHP 7.3 (`hrtime()`). -While this does not affect many common use cases, this is an important -distinction for programs that rely on a high time precision or on systems -that are subject to discontinuous time adjustments (time jumps). -This means that if you store a cache item with a TTL of 30s on PHP < 7.3 -and then adjust your system time forward by 20s, the cache item may -expire in 10s. See also [`set()`](#set) for more details. - -## Common usage - -### Fallback get - -A common use case of caches is to attempt fetching a cached value and as a -fallback retrieve it from the original data source if not found. Here is an -example of that: - -```php -$cache - ->get('foo') - ->then(function ($result) { - if ($result === null) { - return getFooFromDb(); - } - - return $result; - }) - ->then('var_dump'); -``` - -First an attempt is made to retrieve the value of `foo`. A callback function is -registered that will call `getFooFromDb` when the resulting value is null. -`getFooFromDb` is a function (can be any PHP callable) that will be called if the -key does not exist in the cache. - -`getFooFromDb` can handle the missing key by returning a promise for the -actual value from the database (or any other data source). As a result, this -chain will correctly fall back, and provide the value in both cases. - -### Fallback get and set - -To expand on the fallback get example, often you want to set the value on the -cache after fetching it from the data source. - -```php -$cache - ->get('foo') - ->then(function ($result) { - if ($result === null) { - return $this->getAndCacheFooFromDb(); - } - - return $result; - }) - ->then('var_dump'); - -public function getAndCacheFooFromDb() -{ - return $this->db - ->get('foo') - ->then(array($this, 'cacheFooFromDb')); -} - -public function cacheFooFromDb($foo) -{ - $this->cache->set('foo', $foo); - - return $foo; -} -``` - -By using chaining you can easily conditionally cache the value if it is -fetched from the database. - -## Install - -The recommended way to install this library is [through Composer](https://getcomposer.org). -[New to Composer?](https://getcomposer.org/doc/00-intro.md) - -This project follows [SemVer](https://semver.org/). -This will install the latest supported version: - -```bash -composer require react/cache:^1.2 -``` - -See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. - -This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.3 through current PHP 8+ and -HHVM. -It's *highly recommended to use PHP 7+* for this project. - -## Tests - -To run the test suite, you first need to clone this repo and then install all -dependencies [through Composer](https://getcomposer.org): - -```bash -composer install -``` - -To run the test suite, go to the project root and run: - -```bash -vendor/bin/phpunit -``` - -## License - -MIT, see [LICENSE file](LICENSE). diff --git a/vendor/react/cache/composer.json b/vendor/react/cache/composer.json deleted file mode 100644 index 153439a..0000000 --- a/vendor/react/cache/composer.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "react/cache", - "description": "Async, Promise-based cache interface for ReactPHP", - "keywords": ["cache", "caching", "promise", "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", - "react/promise": "^3.0 || ^2.0 || ^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" - }, - "autoload": { - "psr-4": { - "React\\Cache\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "React\\Tests\\Cache\\": "tests/" - } - } -} diff --git a/vendor/react/cache/src/ArrayCache.php b/vendor/react/cache/src/ArrayCache.php deleted file mode 100644 index 81f25ef..0000000 --- a/vendor/react/cache/src/ArrayCache.php +++ /dev/null @@ -1,181 +0,0 @@ -set('foo', 'bar'); - * ``` - * - * Its constructor accepts an optional `?int $limit` parameter to limit the - * maximum number of entries to store in the LRU cache. If you add more - * entries to this instance, it will automatically take care of removing - * the one that was least recently used (LRU). - * - * For example, this snippet will overwrite the first value and only store - * the last two entries: - * - * ```php - * $cache = new ArrayCache(2); - * - * $cache->set('foo', '1'); - * $cache->set('bar', '2'); - * $cache->set('baz', '3'); - * ``` - * - * This cache implementation is known to rely on wall-clock time to schedule - * future cache expiration times when using any version before PHP 7.3, - * because a monotonic time source is only available as of PHP 7.3 (`hrtime()`). - * While this does not affect many common use cases, this is an important - * distinction for programs that rely on a high time precision or on systems - * that are subject to discontinuous time adjustments (time jumps). - * This means that if you store a cache item with a TTL of 30s on PHP < 7.3 - * and then adjust your system time forward by 20s, the cache item may - * expire in 10s. See also [`set()`](#set) for more details. - * - * @param int|null $limit maximum number of entries to store in the LRU cache - */ - public function __construct($limit = null) - { - $this->limit = $limit; - - // prefer high-resolution timer, available as of PHP 7.3+ - $this->supportsHighResolution = \function_exists('hrtime'); - } - - public function get($key, $default = null) - { - // delete key if it is already expired => below will detect this as a cache miss - if (isset($this->expires[$key]) && $this->now() - $this->expires[$key] > 0) { - unset($this->data[$key], $this->expires[$key]); - } - - if (!\array_key_exists($key, $this->data)) { - return Promise\resolve($default); - } - - // remove and append to end of array to keep track of LRU info - $value = $this->data[$key]; - unset($this->data[$key]); - $this->data[$key] = $value; - - return Promise\resolve($value); - } - - public function set($key, $value, $ttl = null) - { - // unset before setting to ensure this entry will be added to end of array (LRU info) - unset($this->data[$key]); - $this->data[$key] = $value; - - // sort expiration times if TTL is given (first will expire first) - unset($this->expires[$key]); - if ($ttl !== null) { - $this->expires[$key] = $this->now() + $ttl; - \asort($this->expires); - } - - // ensure size limit is not exceeded or remove first entry from array - if ($this->limit !== null && \count($this->data) > $this->limit) { - // first try to check if there's any expired entry - // expiration times are sorted, so we can simply look at the first one - \reset($this->expires); - $key = \key($this->expires); - - // check to see if the first in the list of expiring keys is already expired - // if the first key is not expired, we have to overwrite by using LRU info - if ($key === null || $this->now() - $this->expires[$key] < 0) { - \reset($this->data); - $key = \key($this->data); - } - unset($this->data[$key], $this->expires[$key]); - } - - return Promise\resolve(true); - } - - public function delete($key) - { - unset($this->data[$key], $this->expires[$key]); - - return Promise\resolve(true); - } - - public function getMultiple(array $keys, $default = null) - { - $values = array(); - - foreach ($keys as $key) { - $values[$key] = $this->get($key, $default); - } - - return Promise\all($values); - } - - public function setMultiple(array $values, $ttl = null) - { - foreach ($values as $key => $value) { - $this->set($key, $value, $ttl); - } - - return Promise\resolve(true); - } - - public function deleteMultiple(array $keys) - { - foreach ($keys as $key) { - unset($this->data[$key], $this->expires[$key]); - } - - return Promise\resolve(true); - } - - public function clear() - { - $this->data = array(); - $this->expires = array(); - - return Promise\resolve(true); - } - - public function has($key) - { - // delete key if it is already expired - if (isset($this->expires[$key]) && $this->now() - $this->expires[$key] > 0) { - unset($this->data[$key], $this->expires[$key]); - } - - if (!\array_key_exists($key, $this->data)) { - return Promise\resolve(false); - } - - // remove and append to end of array to keep track of LRU info - $value = $this->data[$key]; - unset($this->data[$key]); - $this->data[$key] = $value; - - return Promise\resolve(true); - } - - /** - * @return float - */ - private function now() - { - return $this->supportsHighResolution ? \hrtime(true) * 1e-9 : \microtime(true); - } -} diff --git a/vendor/react/cache/src/CacheInterface.php b/vendor/react/cache/src/CacheInterface.php deleted file mode 100644 index 8e51c19..0000000 --- a/vendor/react/cache/src/CacheInterface.php +++ /dev/null @@ -1,194 +0,0 @@ -get('foo') - * ->then('var_dump'); - * ``` - * - * This example fetches the value of the key `foo` and passes it to the - * `var_dump` function. You can use any of the composition provided by - * [promises](https://github.com/reactphp/promise). - * - * @param string $key - * @param mixed $default Default value to return for cache miss or null if not given. - * @return PromiseInterface - */ - public function get($key, $default = null); - - /** - * Stores an item in the cache. - * - * This method will resolve with `true` on success or `false` when an error - * occurs. If the cache implementation has to go over the network to store - * it, it may take a while. - * - * The optional `$ttl` parameter sets the maximum time-to-live in seconds - * for this cache item. If this parameter is omitted (or `null`), the item - * will stay in the cache for as long as the underlying implementation - * supports. Trying to access an expired cache item results in a cache miss, - * see also [`get()`](#get). - * - * ```php - * $cache->set('foo', 'bar', 60); - * ``` - * - * This example eventually sets the value of the key `foo` to `bar`. If it - * already exists, it is overridden. - * - * This interface does not enforce any particular TTL resolution, so special - * care may have to be taken if you rely on very high precision with - * millisecond accuracy or below. Cache implementations SHOULD work on a - * best effort basis and SHOULD provide at least second accuracy unless - * otherwise noted. Many existing cache implementations are known to provide - * microsecond or millisecond accuracy, but it's generally not recommended - * to rely on this high precision. - * - * This interface suggests that cache implementations SHOULD use a monotonic - * time source if available. Given that a monotonic time source is only - * available as of PHP 7.3 by default, cache implementations MAY fall back - * to using wall-clock time. - * While this does not affect many common use cases, this is an important - * distinction for programs that rely on a high time precision or on systems - * that are subject to discontinuous time adjustments (time jumps). - * This means that if you store a cache item with a TTL of 30s and then - * adjust your system time forward by 20s, the cache item SHOULD still - * expire in 30s. - * - * @param string $key - * @param mixed $value - * @param ?float $ttl - * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error - */ - public function set($key, $value, $ttl = null); - - /** - * Deletes an item from the cache. - * - * This method will resolve with `true` on success or `false` when an error - * occurs. When no item for `$key` is found in the cache, it also resolves - * to `true`. If the cache implementation has to go over the network to - * delete it, it may take a while. - * - * ```php - * $cache->delete('foo'); - * ``` - * - * This example eventually deletes the key `foo` from the cache. As with - * `set()`, this may not happen instantly and a promise is returned to - * provide guarantees whether or not the item has been removed from cache. - * - * @param string $key - * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error - */ - public function delete($key); - - /** - * Retrieves multiple cache items by their unique keys. - * - * This method will resolve with an array of cached values on success or with the - * given `$default` value when an item can not be found or when an error occurs. - * Similarly, an expired cache item (once the time-to-live is expired) is - * considered a cache miss. - * - * ```php - * $cache->getMultiple(array('name', 'age'))->then(function (array $values) { - * $name = $values['name'] ?? 'User'; - * $age = $values['age'] ?? 'n/a'; - * - * echo $name . ' is ' . $age . PHP_EOL; - * }); - * ``` - * - * This example fetches the cache items for the `name` and `age` keys and - * prints some example output. You can use any of the composition provided - * by [promises](https://github.com/reactphp/promise). - * - * @param string[] $keys A list of keys that can obtained in a single operation. - * @param mixed $default Default value to return for keys that do not exist. - * @return PromiseInterface Returns a promise which resolves to an `array` of cached values - */ - public function getMultiple(array $keys, $default = null); - - /** - * Persists a set of key => value pairs in the cache, with an optional TTL. - * - * This method will resolve with `true` on success or `false` when an error - * occurs. If the cache implementation has to go over the network to store - * it, it may take a while. - * - * The optional `$ttl` parameter sets the maximum time-to-live in seconds - * for these cache items. If this parameter is omitted (or `null`), these items - * will stay in the cache for as long as the underlying implementation - * supports. Trying to access an expired cache items results in a cache miss, - * see also [`get()`](#get). - * - * ```php - * $cache->setMultiple(array('foo' => 1, 'bar' => 2), 60); - * ``` - * - * This example eventually sets the list of values - the key `foo` to 1 value - * and the key `bar` to 2. If some of the keys already exist, they are overridden. - * - * @param array $values A list of key => value pairs for a multiple-set operation. - * @param ?float $ttl Optional. The TTL value of this item. - * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error - */ - public function setMultiple(array $values, $ttl = null); - - /** - * Deletes multiple cache items in a single operation. - * - * @param string[] $keys A list of string-based keys to be deleted. - * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error - */ - public function deleteMultiple(array $keys); - - /** - * Wipes clean the entire cache. - * - * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error - */ - public function clear(); - - /** - * Determines whether an item is present in the cache. - * - * This method will resolve with `true` on success or `false` when no item can be found - * or when an error occurs. Similarly, an expired cache item (once the time-to-live - * is expired) is considered a cache miss. - * - * ```php - * $cache - * ->has('foo') - * ->then('var_dump'); - * ``` - * - * This example checks if the value of the key `foo` is set in the cache and passes - * the result to the `var_dump` function. You can use any of the composition provided by - * [promises](https://github.com/reactphp/promise). - * - * NOTE: It is recommended that has() is only to be used for cache warming type purposes - * and not to be used within your live applications operations for get/set, as this method - * is subject to a race condition where your has() will return true and immediately after, - * another script can remove it making the state of your app out of date. - * - * @param string $key The cache item key. - * @return PromiseInterface Returns a promise which resolves to `true` on success or `false` on error - */ - public function has($key); -} diff --git a/vendor/react/dns/CHANGELOG.md b/vendor/react/dns/CHANGELOG.md deleted file mode 100644 index bc1055f..0000000 --- a/vendor/react/dns/CHANGELOG.md +++ /dev/null @@ -1,452 +0,0 @@ -# Changelog - -## 1.13.0 (2024-06-13) - -* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable type declarations. - (#224 by @WyriHaximus) - -## 1.12.0 (2023-11-29) - -* Feature: Full PHP 8.3 compatibility. - (#217 by @sergiy-petrov) - -* Update test environment and avoid unhandled promise rejections. - (#215, #216 and #218 by @clue) - -## 1.11.0 (2023-06-02) - -* Feature: Include timeout logic to avoid dependency on reactphp/promise-timer. - (#213 by @clue) - -* Improve test suite and project setup and report failed assertions. - (#210 by @clue, #212 by @WyriHaximus and #209 and #211 by @SimonFrings) - -## 1.10.0 (2022-09-08) - -* Feature: Full support for PHP 8.2 release. - (#201 by @clue and #207 by @WyriHaximus) - -* Feature: Optimize forward compatibility with Promise v3, avoid hitting autoloader. - (#202 by @clue) - -* Feature / Fix: Improve error reporting when custom error handler is used. - (#197 by @clue) - -* Fix: Fix invalid references in exception stack trace. - (#191 by @clue) - -* Minor documentation improvements. - (#195 by @SimonFrings and #203 by @nhedger) - -* Improve test suite, update to use default loop and new reactphp/async package. - (#204, #205 and #206 by @clue and #196 by @SimonFrings) - -## 1.9.0 (2021-12-20) - -* Feature: Full support for PHP 8.1 release and prepare PHP 8.2 compatibility - by refactoring `Parser` to avoid assigning dynamic properties. - (#188 and #186 by @clue and #184 by @SimonFrings) - -* Feature: Avoid dependency on `ext-filter`. - (#185 by @clue) - -* Feature / Fix: Skip invalid nameserver entries from `resolv.conf` and ignore IPv6 zone IDs. - (#187 by @clue) - -* Feature / Fix: Reduce socket read chunk size for queries over TCP/IP. - (#189 by @clue) - -## 1.8.0 (2021-07-11) - -A major new feature release, see [**release announcement**](https://clue.engineering/2021/announcing-reactphp-default-loop). - -* Feature: Simplify usage by supporting new [default loop](https://reactphp.org/event-loop/#loop). - (#182 by @clue) - - ```php - // old (still supported) - $factory = new React\Dns\Resolver\Factory(); - $resolver = $factory->create($config, $loop); - - // new (using default loop) - $factory = new React\Dns\Resolver\Factory(); - $resolver = $factory->create($config); - ``` - -## 1.7.0 (2021-06-25) - -* Feature: Update DNS `Factory` to accept complete `Config` object. - Add new `FallbackExecutor` and use fallback DNS servers when `Config` lists multiple servers. - (#179 and #180 by @clue) - - ```php - // old (still supported) - $config = React\Dns\Config\Config::loadSystemConfigBlocking(); - $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8'; - $resolver = $factory->create($server, $loop); - - // new - $config = React\Dns\Config\Config::loadSystemConfigBlocking(); - if (!$config->nameservers) { - $config->nameservers[] = '8.8.8.8'; - } - $resolver = $factory->create($config, $loop); - ``` - -## 1.6.0 (2021-06-21) - -* Feature: Add support for legacy `SPF` record type. - (#178 by @akondas and @clue) - -* Fix: Fix integer overflow for TCP/IP chunk size on 32 bit platforms. - (#177 by @clue) - -## 1.5.0 (2021-03-05) - -* Feature: Improve error reporting when query fails, include domain and query type and DNS server address where applicable. - (#174 by @clue) - -* Feature: Improve error handling when sending data to DNS server fails (macOS). - (#171 and #172 by @clue) - -* Fix: Improve DNS response parser to limit recursion for compressed labels. - (#169 by @clue) - -* Improve test suite, use GitHub actions for continuous integration (CI). - (#170 by @SimonFrings) - -## 1.4.0 (2020-09-18) - -* Feature: Support upcoming PHP 8. - (#168 by @clue) - -* Improve test suite and update to PHPUnit 9.3. - (#164 by @clue, #165 and #166 by @SimonFrings and #167 by @WyriHaximus) - -## 1.3.0 (2020-07-10) - -* Feature: Forward compatibility with react/promise v3. - (#153 by @WyriHaximus) - -* Feature: Support parsing `OPT` records (EDNS0). - (#157 by @clue) - -* Fix: Avoid PHP warnings due to lack of args in exception trace on PHP 7.4. - (#160 by @clue) - -* Improve test suite and add `.gitattributes` to exclude dev files from exports. - Run tests on PHPUnit 9 and PHP 7.4 and clean up test suite. - (#154 by @reedy, #156 by @clue and #163 by @SimonFrings) - -## 1.2.0 (2019-08-15) - -* Feature: Add `TcpTransportExecutor` to send DNS queries over TCP/IP connection, - add `SelectiveTransportExecutor` to retry with TCP if UDP is truncated and - automatically select transport protocol when no explicit `udp://` or `tcp://` scheme is given in `Factory`. - (#145, #146, #147 and #148 by @clue) - -* Feature: Support escaping literal dots and special characters in domain names. - (#144 by @clue) - -## 1.1.0 (2019-07-18) - -* Feature: Support parsing `CAA` and `SSHFP` records. - (#141 and #142 by @clue) - -* Feature: Add `ResolverInterface` as common interface for `Resolver` class. - (#139 by @clue) - -* Fix: Add missing private property definitions and - remove unneeded dependency on `react/stream`. - (#140 and #143 by @clue) - -## 1.0.0 (2019-07-11) - -* First stable LTS release, now following [SemVer](https://semver.org/). - We'd like to emphasize that this component is production ready and battle-tested. - We plan to support all long-term support (LTS) releases for at least 24 months, - so you have a rock-solid foundation to build on top of. - -This update involves a number of BC breaks due to dropped support for -deprecated functionality and some internal API cleanup. We've tried hard to -avoid BC breaks where possible and minimize impact otherwise. We expect that -most consumers of this package will actually not be affected by any BC -breaks, see below for more details: - -* BC break: Delete all deprecated APIs, use `Query` objects for `Message` questions - instead of nested arrays and increase code coverage to 100%. - (#130 by @clue) - -* BC break: Move `$nameserver` from `ExecutorInterface` to `UdpTransportExecutor`, - remove advanced/internal `UdpTransportExecutor` args for `Parser`/`BinaryDumper` and - add API documentation for `ExecutorInterface`. - (#135, #137 and #138 by @clue) - -* BC break: Replace `HeaderBag` attributes with simple `Message` properties. - (#132 by @clue) - -* BC break: Mark all `Record` attributes as required, add documentation vs `Query`. - (#136 by @clue) - -* BC break: Mark all classes as final to discourage inheritance - (#134 by @WyriHaximus) - -## 0.4.19 (2019-07-10) - -* Feature: Avoid garbage references when DNS resolution rejects on legacy PHP <= 5.6. - (#133 by @clue) - -## 0.4.18 (2019-09-07) - -* Feature / Fix: Implement `CachingExecutor` using cache TTL, deprecate old `CachedExecutor`, - respect TTL from response records when caching and do not cache truncated responses. - (#129 by @clue) - -* Feature: Limit cache size to 256 last responses by default. - (#127 by @clue) - -* Feature: Cooperatively resolve hosts to avoid running same query concurrently. - (#125 by @clue) - -## 0.4.17 (2019-04-01) - -* Feature: Support parsing `authority` and `additional` records from DNS response. - (#123 by @clue) - -* Feature: Support dumping records as part of outgoing binary DNS message. - (#124 by @clue) - -* Feature: Forward compatibility with upcoming Cache v0.6 and Cache v1.0 - (#121 by @clue) - -* Improve test suite to add forward compatibility with PHPUnit 7, - test against PHP 7.3 and use legacy PHPUnit 5 on legacy HHVM. - (#122 by @clue) - -## 0.4.16 (2018-11-11) - -* Feature: Improve promise cancellation for DNS lookup retries and clean up any garbage references. - (#118 by @clue) - -* Fix: Reject parsing malformed DNS response messages such as incomplete DNS response messages, - malformed record data or malformed compressed domain name labels. - (#115 and #117 by @clue) - -* Fix: Fix interpretation of TTL as UINT32 with most significant bit unset. - (#116 by @clue) - -* Fix: Fix caching advanced MX/SRV/TXT/SOA structures. - (#112 by @clue) - -## 0.4.15 (2018-07-02) - -* Feature: Add `resolveAll()` method to support custom query types in `Resolver`. - (#110 by @clue and @WyriHaximus) - - ```php - $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) { - echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; - }); - ``` - -* Feature: Support parsing `NS`, `TXT`, `MX`, `SOA` and `SRV` records. - (#104, #105, #106, #107 and #108 by @clue) - -* Feature: Add support for `Message::TYPE_ANY` and parse unknown types as binary data. - (#104 by @clue) - -* Feature: Improve error messages for failed queries and improve documentation. - (#109 by @clue) - -* Feature: Add reverse DNS lookup example. - (#111 by @clue) - -## 0.4.14 (2018-06-26) - -* Feature: Add `UdpTransportExecutor`, validate incoming DNS response messages - to avoid cache poisoning attacks and deprecate legacy `Executor`. - (#101 and #103 by @clue) - -* Feature: Forward compatibility with Cache 0.5 - (#102 by @clue) - -* Deprecate legacy `Query::$currentTime` and binary parser data attributes to clean up and simplify API. - (#99 by @clue) - -## 0.4.13 (2018-02-27) - -* Add `Config::loadSystemConfigBlocking()` to load default system config - and support parsing DNS config on all supported platforms - (`/etc/resolv.conf` on Unix/Linux/Mac and WMIC on Windows) - (#92, #93, #94 and #95 by @clue) - - ```php - $config = Config::loadSystemConfigBlocking(); - $server = $config->nameservers ? reset($config->nameservers) : '8.8.8.8'; - ``` - -* Remove unneeded cyclic dependency on react/socket - (#96 by @clue) - -## 0.4.12 (2018-01-14) - -* Improve test suite by adding forward compatibility with PHPUnit 6, - test against PHP 7.2, fix forward compatibility with upcoming EventLoop releases, - add test group to skip integration tests relying on internet connection - and add minor documentation improvements. - (#85 and #87 by @carusogabriel, #88 and #89 by @clue and #83 by @jsor) - -## 0.4.11 (2017-08-25) - -* Feature: Support resolving from default hosts file - (#75, #76 and #77 by @clue) - - This means that resolving hosts such as `localhost` will now work as - expected across all platforms with no changes required: - - ```php - $resolver->resolve('localhost')->then(function ($ip) { - echo 'IP: ' . $ip; - }); - ``` - - The new `HostsExecutor` exists for advanced usage and is otherwise used - internally for this feature. - -## 0.4.10 (2017-08-10) - -* Feature: Forward compatibility with EventLoop v1.0 and v0.5 and - lock minimum dependencies and work around circular dependency for tests - (#70 and #71 by @clue) - -* Fix: Work around DNS timeout issues for Windows users - (#74 by @clue) - -* Documentation and examples for advanced usage - (#66 by @WyriHaximus) - -* Remove broken TCP code, do not retry with invalid TCP query - (#73 by @clue) - -* Improve test suite by fixing HHVM build for now again and ignore future HHVM build errors and - lock Travis distro so new defaults will not break the build and - fix failing tests for PHP 7.1 - (#68 by @WyriHaximus and #69 and #72 by @clue) - -## 0.4.9 (2017-05-01) - -* Feature: Forward compatibility with upcoming Socket v1.0 and v0.8 - (#61 by @clue) - -## 0.4.8 (2017-04-16) - -* Feature: Add support for the AAAA record type to the protocol parser - (#58 by @othillo) - -* Feature: Add support for the PTR record type to the protocol parser - (#59 by @othillo) - -## 0.4.7 (2017-03-31) - -* Feature: Forward compatibility with upcoming Socket v0.6 and v0.7 component - (#57 by @clue) - -## 0.4.6 (2017-03-11) - -* Fix: Fix DNS timeout issues for Windows users and add forward compatibility - with Stream v0.5 and upcoming v0.6 - (#53 by @clue) - -* Improve test suite by adding PHPUnit to `require-dev` - (#54 by @clue) - -## 0.4.5 (2017-03-02) - -* Fix: Ensure we ignore the case of the answer - (#51 by @WyriHaximus) - -* Feature: Add `TimeoutExecutor` and simplify internal APIs to allow internal - code re-use for upcoming versions. - (#48 and #49 by @clue) - -## 0.4.4 (2017-02-13) - -* Fix: Fix handling connection and stream errors - (#45 by @clue) - -* Feature: Add examples and forward compatibility with upcoming Socket v0.5 component - (#46 and #47 by @clue) - -## 0.4.3 (2016-07-31) - -* Feature: Allow for cache adapter injection (#38 by @WyriHaximus) - - ```php - $factory = new React\Dns\Resolver\Factory(); - - $cache = new MyCustomCacheInstance(); - $resolver = $factory->createCached('8.8.8.8', $loop, $cache); - ``` - -* Feature: Support Promise cancellation (#35 by @clue) - - ```php - $promise = $resolver->resolve('reactphp.org'); - - $promise->cancel(); - ``` - -## 0.4.2 (2016-02-24) - -* Repository maintenance, split off from main repo, improve test suite and documentation -* First class support for PHP7 and HHVM (#34 by @clue) -* Adjust compatibility to 5.3 (#30 by @clue) - -## 0.4.1 (2014-04-13) - -* Bug fix: Fixed PSR-4 autoload path (@marcj/WyriHaximus) - -## 0.4.0 (2014-02-02) - -* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks -* BC break: Update to React/Promise 2.0 -* Bug fix: Properly resolve CNAME aliases -* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0 -* Bump React dependencies to v0.4 - -## 0.3.2 (2013-05-10) - -* Feature: Support default port for IPv6 addresses (@clue) - -## 0.3.0 (2013-04-14) - -* Bump React dependencies to v0.3 - -## 0.2.6 (2012-12-26) - -* Feature: New cache component, used by DNS - -## 0.2.5 (2012-11-26) - -* Version bump - -## 0.2.4 (2012-11-18) - -* Feature: Change to promise-based API (@jsor) - -## 0.2.3 (2012-11-14) - -* Version bump - -## 0.2.2 (2012-10-28) - -* Feature: DNS executor timeout handling (@arnaud-lb) -* Feature: DNS retry executor (@arnaud-lb) - -## 0.2.1 (2012-10-14) - -* Minor adjustments to DNS parser - -## 0.2.0 (2012-09-10) - -* Feature: DNS resolver diff --git a/vendor/react/dns/LICENSE b/vendor/react/dns/LICENSE deleted file mode 100644 index d6f8901..0000000 --- a/vendor/react/dns/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -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/dns/README.md b/vendor/react/dns/README.md deleted file mode 100644 index 9f83a94..0000000 --- a/vendor/react/dns/README.md +++ /dev/null @@ -1,453 +0,0 @@ -# DNS - -[![CI status](https://github.com/reactphp/dns/actions/workflows/ci.yml/badge.svg)](https://github.com/reactphp/dns/actions) -[![installs on Packagist](https://img.shields.io/packagist/dt/react/dns?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/dns) - -Async DNS resolver for [ReactPHP](https://reactphp.org/). - -The main point of the DNS component is to provide async DNS resolution. -However, it is really a toolkit for working with DNS messages, and could -easily be used to create a DNS server. - -**Table of contents** - -* [Basic usage](#basic-usage) -* [Caching](#caching) - * [Custom cache adapter](#custom-cache-adapter) -* [ResolverInterface](#resolverinterface) - * [resolve()](#resolve) - * [resolveAll()](#resolveall) -* [Advanced usage](#advanced-usage) - * [UdpTransportExecutor](#udptransportexecutor) - * [TcpTransportExecutor](#tcptransportexecutor) - * [SelectiveTransportExecutor](#selectivetransportexecutor) - * [HostsFileExecutor](#hostsfileexecutor) -* [Install](#install) -* [Tests](#tests) -* [License](#license) -* [References](#references) - -## Basic usage - -The most basic usage is to just create a resolver through the resolver -factory. All you need to give it is a nameserver, then you can start resolving -names, baby! - -```php -$config = React\Dns\Config\Config::loadSystemConfigBlocking(); -if (!$config->nameservers) { - $config->nameservers[] = '8.8.8.8'; -} - -$factory = new React\Dns\Resolver\Factory(); -$dns = $factory->create($config); - -$dns->resolve('igor.io')->then(function ($ip) { - echo "Host: $ip\n"; -}); -``` - -See also the [first example](examples). - -The `Config` class can be used to load the system default config. This is an -operation that may access the filesystem and block. Ideally, this method should -thus be executed only once before the loop starts and not repeatedly while it is -running. -Note that this class may return an *empty* configuration if the system config -can not be loaded. As such, you'll likely want to apply a default nameserver -as above if none can be found. - -> Note that the factory loads the hosts file from the filesystem once when - creating the resolver instance. - Ideally, this method should thus be executed only once before the loop starts - and not repeatedly while it is running. - -But there's more. - -## Caching - -You can cache results by configuring the resolver to use a `CachedExecutor`: - -```php -$config = React\Dns\Config\Config::loadSystemConfigBlocking(); -if (!$config->nameservers) { - $config->nameservers[] = '8.8.8.8'; -} - -$factory = new React\Dns\Resolver\Factory(); -$dns = $factory->createCached($config); - -$dns->resolve('igor.io')->then(function ($ip) { - echo "Host: $ip\n"; -}); - -... - -$dns->resolve('igor.io')->then(function ($ip) { - echo "Host: $ip\n"; -}); -``` - -If the first call returns before the second, only one query will be executed. -The second result will be served from an in memory cache. -This is particularly useful for long running scripts where the same hostnames -have to be looked up multiple times. - -See also the [third example](examples). - -### Custom cache adapter - -By default, the above will use an in memory cache. - -You can also specify a custom cache implementing [`CacheInterface`](https://github.com/reactphp/cache) to handle the record cache instead: - -```php -$cache = new React\Cache\ArrayCache(); -$factory = new React\Dns\Resolver\Factory(); -$dns = $factory->createCached('8.8.8.8', null, $cache); -``` - -See also the wiki for possible [cache implementations](https://github.com/reactphp/react/wiki/Users#cache-implementations). - -## ResolverInterface - - - -### resolve() - -The `resolve(string $domain): PromiseInterface` method can be used to -resolve the given $domain name to a single IPv4 address (type `A` query). - -```php -$resolver->resolve('reactphp.org')->then(function ($ip) { - echo 'IP for reactphp.org is ' . $ip . PHP_EOL; -}); -``` - -This is one of the main methods in this package. It sends a DNS query -for the given $domain name to your DNS server and returns a single IP -address on success. - -If the DNS server sends a DNS response message that contains more than -one IP address for this query, it will randomly pick one of the IP -addresses from the response. If you want the full list of IP addresses -or want to send a different type of query, you should use the -[`resolveAll()`](#resolveall) method instead. - -If the DNS server sends a DNS response message that indicates an error -code, this method will reject with a `RecordNotFoundException`. Its -message and code can be used to check for the response code. - -If the DNS communication fails and the server does not respond with a -valid response message, this message will reject with an `Exception`. - -Pending DNS queries can be cancelled by cancelling its pending promise like so: - -```php -$promise = $resolver->resolve('reactphp.org'); - -$promise->cancel(); -``` - -### resolveAll() - -The `resolveAll(string $host, int $type): PromiseInterface` method can be used to -resolve all record values for the given $domain name and query $type. - -```php -$resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) { - echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; -}); - -$resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) { - echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; -}); -``` - -This is one of the main methods in this package. It sends a DNS query -for the given $domain name to your DNS server and returns a list with all -record values on success. - -If the DNS server sends a DNS response message that contains one or more -records for this query, it will return a list with all record values -from the response. You can use the `Message::TYPE_*` constants to control -which type of query will be sent. Note that this method always returns a -list of record values, but each record value type depends on the query -type. For example, it returns the IPv4 addresses for type `A` queries, -the IPv6 addresses for type `AAAA` queries, the hostname for type `NS`, -`CNAME` and `PTR` queries and structured data for other queries. See also -the `Record` documentation for more details. - -If the DNS server sends a DNS response message that indicates an error -code, this method will reject with a `RecordNotFoundException`. Its -message and code can be used to check for the response code. - -If the DNS communication fails and the server does not respond with a -valid response message, this message will reject with an `Exception`. - -Pending DNS queries can be cancelled by cancelling its pending promise like so: - -```php -$promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA); - -$promise->cancel(); -``` - -## Advanced Usage - -### UdpTransportExecutor - -The `UdpTransportExecutor` can be used to -send DNS queries over a UDP transport. - -This is the main class that sends a DNS query to your DNS server and is used -internally by the `Resolver` for the actual message transport. - -For more advanced usages one can utilize this class directly. -The following example looks up the `IPv6` address for `igor.io`. - -```php -$executor = new UdpTransportExecutor('8.8.8.8:53'); - -$executor->query( - new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) -)->then(function (Message $message) { - foreach ($message->answers as $answer) { - echo 'IPv6: ' . $answer->data . PHP_EOL; - } -}, 'printf'); -``` - -See also the [fourth example](examples). - -Note that this executor does not implement a timeout, so you will very likely -want to use this in combination with a `TimeoutExecutor` like this: - -```php -$executor = new TimeoutExecutor( - new UdpTransportExecutor($nameserver), - 3.0 -); -``` - -Also note that this executor uses an unreliable UDP transport and that it -does not implement any retry logic, so you will likely want to use this in -combination with a `RetryExecutor` like this: - -```php -$executor = new RetryExecutor( - new TimeoutExecutor( - new UdpTransportExecutor($nameserver), - 3.0 - ) -); -``` - -Note that this executor is entirely async and as such allows you to execute -any number of queries concurrently. You should probably limit the number of -concurrent queries in your application or you're very likely going to face -rate limitations and bans on the resolver end. For many common applications, -you may want to avoid sending the same query multiple times when the first -one is still pending, so you will likely want to use this in combination with -a `CoopExecutor` like this: - -```php -$executor = new CoopExecutor( - new RetryExecutor( - new TimeoutExecutor( - new UdpTransportExecutor($nameserver), - 3.0 - ) - ) -); -``` - -> Internally, this class uses PHP's UDP sockets and does not take advantage - of [react/datagram](https://github.com/reactphp/datagram) purely for - organizational reasons to avoid a cyclic dependency between the two - packages. Higher-level components should take advantage of the Datagram - component instead of reimplementing this socket logic from scratch. - -### TcpTransportExecutor - -The `TcpTransportExecutor` class can be used to -send DNS queries over a TCP/IP stream transport. - -This is one of the main classes that send a DNS query to your DNS server. - -For more advanced usages one can utilize this class directly. -The following example looks up the `IPv6` address for `reactphp.org`. - -```php -$executor = new TcpTransportExecutor('8.8.8.8:53'); - -$executor->query( - new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) -)->then(function (Message $message) { - foreach ($message->answers as $answer) { - echo 'IPv6: ' . $answer->data . PHP_EOL; - } -}, 'printf'); -``` - -See also [example #92](examples). - -Note that this executor does not implement a timeout, so you will very likely -want to use this in combination with a `TimeoutExecutor` like this: - -```php -$executor = new TimeoutExecutor( - new TcpTransportExecutor($nameserver), - 3.0 -); -``` - -Unlike the `UdpTransportExecutor`, this class uses a reliable TCP/IP -transport, so you do not necessarily have to implement any retry logic. - -Note that this executor is entirely async and as such allows you to execute -queries concurrently. The first query will establish a TCP/IP socket -connection to the DNS server which will be kept open for a short period. -Additional queries will automatically reuse this existing socket connection -to the DNS server, will pipeline multiple requests over this single -connection and will keep an idle connection open for a short period. The -initial TCP/IP connection overhead may incur a slight delay if you only send -occasional queries – when sending a larger number of concurrent queries over -an existing connection, it becomes increasingly more efficient and avoids -creating many concurrent sockets like the UDP-based executor. You may still -want to limit the number of (concurrent) queries in your application or you -may be facing rate limitations and bans on the resolver end. For many common -applications, you may want to avoid sending the same query multiple times -when the first one is still pending, so you will likely want to use this in -combination with a `CoopExecutor` like this: - -```php -$executor = new CoopExecutor( - new TimeoutExecutor( - new TcpTransportExecutor($nameserver), - 3.0 - ) -); -``` - -> Internally, this class uses PHP's TCP/IP sockets and does not take advantage - of [react/socket](https://github.com/reactphp/socket) purely for - organizational reasons to avoid a cyclic dependency between the two - packages. Higher-level components should take advantage of the Socket - component instead of reimplementing this socket logic from scratch. - -### SelectiveTransportExecutor - -The `SelectiveTransportExecutor` class can be used to -Send DNS queries over a UDP or TCP/IP stream transport. - -This class will automatically choose the correct transport protocol to send -a DNS query to your DNS server. It will always try to send it over the more -efficient UDP transport first. If this query yields a size related issue -(truncated messages), it will retry over a streaming TCP/IP transport. - -For more advanced usages one can utilize this class directly. -The following example looks up the `IPv6` address for `reactphp.org`. - -```php -$executor = new SelectiveTransportExecutor($udpExecutor, $tcpExecutor); - -$executor->query( - new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) -)->then(function (Message $message) { - foreach ($message->answers as $answer) { - echo 'IPv6: ' . $answer->data . PHP_EOL; - } -}, 'printf'); -``` - -Note that this executor only implements the logic to select the correct -transport for the given DNS query. Implementing the correct transport logic, -implementing timeouts and any retry logic is left up to the given executors, -see also [`UdpTransportExecutor`](#udptransportexecutor) and -[`TcpTransportExecutor`](#tcptransportexecutor) for more details. - -Note that this executor is entirely async and as such allows you to execute -any number of queries concurrently. You should probably limit the number of -concurrent queries in your application or you're very likely going to face -rate limitations and bans on the resolver end. For many common applications, -you may want to avoid sending the same query multiple times when the first -one is still pending, so you will likely want to use this in combination with -a `CoopExecutor` like this: - -```php -$executor = new CoopExecutor( - new SelectiveTransportExecutor( - $datagramExecutor, - $streamExecutor - ) -); -``` - -### HostsFileExecutor - -Note that the above `UdpTransportExecutor` class always performs an actual DNS query. -If you also want to take entries from your hosts file into account, you may -use this code: - -```php -$hosts = \React\Dns\Config\HostsFile::loadFromPathBlocking(); - -$executor = new UdpTransportExecutor('8.8.8.8:53'); -$executor = new HostsFileExecutor($hosts, $executor); - -$executor->query( - new Query('localhost', Message::TYPE_A, Message::CLASS_IN) -); -``` - -## Install - -The recommended way to install this library is [through Composer](https://getcomposer.org/). -[New to Composer?](https://getcomposer.org/doc/00-intro.md) - -This project follows [SemVer](https://semver.org/). -This will install the latest supported version: - -```bash -composer require react/dns:^1.13 -``` - -See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. - -This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.3 through current PHP 8+ and -HHVM. -It's *highly recommended to use the latest supported PHP version* for this project. - -## Tests - -To run the test suite, you first need to clone this repo and then install all -dependencies [through Composer](https://getcomposer.org/): - -```bash -composer install -``` - -To run the test suite, go to the project root and run: - -```bash -vendor/bin/phpunit -``` - -The test suite also contains a number of functional integration tests that rely -on a stable internet connection. -If you do not want to run these, they can simply be skipped like this: - -```bash -vendor/bin/phpunit --exclude-group internet -``` - -## License - -MIT, see [LICENSE file](LICENSE). - -## References - -* [RFC 1034](https://tools.ietf.org/html/rfc1034) Domain Names - Concepts and Facilities -* [RFC 1035](https://tools.ietf.org/html/rfc1035) Domain Names - Implementation and Specification diff --git a/vendor/react/dns/composer.json b/vendor/react/dns/composer.json deleted file mode 100644 index 4fe5c0d..0000000 --- a/vendor/react/dns/composer.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "react/dns", - "description": "Async DNS resolver for ReactPHP", - "keywords": ["dns", "dns-resolver", "ReactPHP", "async"], - "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", - "react/cache": "^1.0 || ^0.6 || ^0.5", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.7 || ^1.2.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3 || ^2", - "react/promise-timer": "^1.11" - }, - "autoload": { - "psr-4": { - "React\\Dns\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "React\\Tests\\Dns\\": "tests/" - } - } -} diff --git a/vendor/react/dns/src/BadServerException.php b/vendor/react/dns/src/BadServerException.php deleted file mode 100644 index 3d95213..0000000 --- a/vendor/react/dns/src/BadServerException.php +++ /dev/null @@ -1,7 +0,0 @@ - `fe80:1`) - if (strpos($ip, ':') !== false && ($pos = strpos($ip, '%')) !== false) { - $ip = substr($ip, 0, $pos); - } - - if (@inet_pton($ip) !== false) { - $config->nameservers[] = $ip; - } - } - - return $config; - } - - /** - * Loads the DNS configurations from Windows's WMIC (from the given command or default command) - * - * Note that this method blocks while loading the given command and should - * thus be used with care! While this should be relatively fast for normal - * WMIC commands, it remains unknown if this may block under certain - * circumstances. In particular, this method should only be executed before - * the loop starts, not while it is running. - * - * Note that this method will only try to execute the given command try to - * parse its output, irrespective of whether this command exists. In - * particular, this command is only available on Windows. Currently, this - * will only parse valid nameserver entries from the command output and will - * ignore all other output without complaining. - * - * Note that the previous section implies that this may return an empty - * `Config` object if no valid nameserver entries can be found. - * - * @param ?string $command (advanced) should not be given (NULL) unless you know what you're doing - * @return self - * @link https://ss64.com/nt/wmic.html - */ - public static function loadWmicBlocking($command = null) - { - $contents = shell_exec($command === null ? 'wmic NICCONFIG get "DNSServerSearchOrder" /format:CSV' : $command); - preg_match_all('/(?<=[{;,"])([\da-f.:]{4,})(?=[};,"])/i', $contents, $matches); - - $config = new self(); - $config->nameservers = $matches[1]; - - return $config; - } - - public $nameservers = array(); -} diff --git a/vendor/react/dns/src/Config/HostsFile.php b/vendor/react/dns/src/Config/HostsFile.php deleted file mode 100644 index 1060231..0000000 --- a/vendor/react/dns/src/Config/HostsFile.php +++ /dev/null @@ -1,153 +0,0 @@ -contents = $contents; - } - - /** - * Returns all IPs for the given hostname - * - * @param string $name - * @return string[] - */ - public function getIpsForHost($name) - { - $name = strtolower($name); - - $ips = array(); - foreach (preg_split('/\r?\n/', $this->contents) as $line) { - $parts = preg_split('/\s+/', $line); - $ip = array_shift($parts); - if ($parts && array_search($name, $parts) !== false) { - // remove IPv6 zone ID (`fe80::1%lo0` => `fe80:1`) - if (strpos($ip, ':') !== false && ($pos = strpos($ip, '%')) !== false) { - $ip = substr($ip, 0, $pos); - } - - if (@inet_pton($ip) !== false) { - $ips[] = $ip; - } - } - } - - return $ips; - } - - /** - * Returns all hostnames for the given IPv4 or IPv6 address - * - * @param string $ip - * @return string[] - */ - public function getHostsForIp($ip) - { - // check binary representation of IP to avoid string case and short notation - $ip = @inet_pton($ip); - if ($ip === false) { - return array(); - } - - $names = array(); - foreach (preg_split('/\r?\n/', $this->contents) as $line) { - $parts = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY); - $addr = (string) array_shift($parts); - - // remove IPv6 zone ID (`fe80::1%lo0` => `fe80:1`) - if (strpos($addr, ':') !== false && ($pos = strpos($addr, '%')) !== false) { - $addr = substr($addr, 0, $pos); - } - - if (@inet_pton($addr) === $ip) { - foreach ($parts as $part) { - $names[] = $part; - } - } - } - - return $names; - } -} diff --git a/vendor/react/dns/src/Model/Message.php b/vendor/react/dns/src/Model/Message.php deleted file mode 100644 index bac2b10..0000000 --- a/vendor/react/dns/src/Model/Message.php +++ /dev/null @@ -1,230 +0,0 @@ -id = self::generateId(); - $request->rd = true; - $request->questions[] = $query; - - return $request; - } - - /** - * Creates a new response message for the given query with the given answer records - * - * @param Query $query - * @param Record[] $answers - * @return self - */ - public static function createResponseWithAnswersForQuery(Query $query, array $answers) - { - $response = new Message(); - $response->id = self::generateId(); - $response->qr = true; - $response->rd = true; - - $response->questions[] = $query; - - foreach ($answers as $record) { - $response->answers[] = $record; - } - - return $response; - } - - /** - * generates a random 16 bit message ID - * - * This uses a CSPRNG so that an outside attacker that is sending spoofed - * DNS response messages can not guess the message ID to avoid possible - * cache poisoning attacks. - * - * The `random_int()` function is only available on PHP 7+ or when - * https://github.com/paragonie/random_compat is installed. As such, using - * the latest supported PHP version is highly recommended. This currently - * falls back to a less secure random number generator on older PHP versions - * in the hope that this system is properly protected against outside - * attackers, for example by using one of the common local DNS proxy stubs. - * - * @return int - * @see self::getId() - * @codeCoverageIgnore - */ - private static function generateId() - { - if (function_exists('random_int')) { - return random_int(0, 0xffff); - } - return mt_rand(0, 0xffff); - } - - /** - * The 16 bit message ID - * - * The response message ID has to match the request message ID. This allows - * the receiver to verify this is the correct response message. An outside - * attacker may try to inject fake responses by "guessing" the message ID, - * so this should use a proper CSPRNG to avoid possible cache poisoning. - * - * @var int 16 bit message ID - * @see self::generateId() - */ - public $id = 0; - - /** - * @var bool Query/Response flag, query=false or response=true - */ - public $qr = false; - - /** - * @var int specifies the kind of query (4 bit), see self::OPCODE_* constants - * @see self::OPCODE_QUERY - */ - public $opcode = self::OPCODE_QUERY; - - /** - * - * @var bool Authoritative Answer - */ - public $aa = false; - - /** - * @var bool TrunCation - */ - public $tc = false; - - /** - * @var bool Recursion Desired - */ - public $rd = false; - - /** - * @var bool Recursion Available - */ - public $ra = false; - - /** - * @var int response code (4 bit), see self::RCODE_* constants - * @see self::RCODE_OK - */ - public $rcode = Message::RCODE_OK; - - /** - * An array of Query objects - * - * ```php - * $questions = array( - * new Query( - * 'reactphp.org', - * Message::TYPE_A, - * Message::CLASS_IN - * ) - * ); - * ``` - * - * @var Query[] - */ - public $questions = array(); - - /** - * @var Record[] - */ - public $answers = array(); - - /** - * @var Record[] - */ - public $authority = array(); - - /** - * @var Record[] - */ - public $additional = array(); -} diff --git a/vendor/react/dns/src/Model/Record.php b/vendor/react/dns/src/Model/Record.php deleted file mode 100644 index c20403f..0000000 --- a/vendor/react/dns/src/Model/Record.php +++ /dev/null @@ -1,153 +0,0 @@ -name = $name; - $this->type = $type; - $this->class = $class; - $this->ttl = $ttl; - $this->data = $data; - } -} diff --git a/vendor/react/dns/src/Protocol/BinaryDumper.php b/vendor/react/dns/src/Protocol/BinaryDumper.php deleted file mode 100644 index 6f4030f..0000000 --- a/vendor/react/dns/src/Protocol/BinaryDumper.php +++ /dev/null @@ -1,199 +0,0 @@ -headerToBinary($message); - $data .= $this->questionToBinary($message->questions); - $data .= $this->recordsToBinary($message->answers); - $data .= $this->recordsToBinary($message->authority); - $data .= $this->recordsToBinary($message->additional); - - return $data; - } - - /** - * @param Message $message - * @return string - */ - private function headerToBinary(Message $message) - { - $data = ''; - - $data .= pack('n', $message->id); - - $flags = 0x00; - $flags = ($flags << 1) | ($message->qr ? 1 : 0); - $flags = ($flags << 4) | $message->opcode; - $flags = ($flags << 1) | ($message->aa ? 1 : 0); - $flags = ($flags << 1) | ($message->tc ? 1 : 0); - $flags = ($flags << 1) | ($message->rd ? 1 : 0); - $flags = ($flags << 1) | ($message->ra ? 1 : 0); - $flags = ($flags << 3) | 0; // skip unused zero bit - $flags = ($flags << 4) | $message->rcode; - - $data .= pack('n', $flags); - - $data .= pack('n', count($message->questions)); - $data .= pack('n', count($message->answers)); - $data .= pack('n', count($message->authority)); - $data .= pack('n', count($message->additional)); - - return $data; - } - - /** - * @param Query[] $questions - * @return string - */ - private function questionToBinary(array $questions) - { - $data = ''; - - foreach ($questions as $question) { - $data .= $this->domainNameToBinary($question->name); - $data .= pack('n*', $question->type, $question->class); - } - - return $data; - } - - /** - * @param Record[] $records - * @return string - */ - private function recordsToBinary(array $records) - { - $data = ''; - - foreach ($records as $record) { - /* @var $record Record */ - switch ($record->type) { - case Message::TYPE_A: - case Message::TYPE_AAAA: - $binary = \inet_pton($record->data); - break; - case Message::TYPE_CNAME: - case Message::TYPE_NS: - case Message::TYPE_PTR: - $binary = $this->domainNameToBinary($record->data); - break; - case Message::TYPE_TXT: - case Message::TYPE_SPF: - $binary = $this->textsToBinary($record->data); - break; - case Message::TYPE_MX: - $binary = \pack( - 'n', - $record->data['priority'] - ); - $binary .= $this->domainNameToBinary($record->data['target']); - break; - case Message::TYPE_SRV: - $binary = \pack( - 'n*', - $record->data['priority'], - $record->data['weight'], - $record->data['port'] - ); - $binary .= $this->domainNameToBinary($record->data['target']); - break; - case Message::TYPE_SOA: - $binary = $this->domainNameToBinary($record->data['mname']); - $binary .= $this->domainNameToBinary($record->data['rname']); - $binary .= \pack( - 'N*', - $record->data['serial'], - $record->data['refresh'], - $record->data['retry'], - $record->data['expire'], - $record->data['minimum'] - ); - break; - case Message::TYPE_CAA: - $binary = \pack( - 'C*', - $record->data['flag'], - \strlen($record->data['tag']) - ); - $binary .= $record->data['tag']; - $binary .= $record->data['value']; - break; - case Message::TYPE_SSHFP: - $binary = \pack( - 'CCH*', - $record->data['algorithm'], - $record->data['type'], - $record->data['fingerprint'] - ); - break; - case Message::TYPE_OPT: - $binary = ''; - foreach ($record->data as $opt => $value) { - if ($opt === Message::OPT_TCP_KEEPALIVE && $value !== null) { - $value = \pack('n', round($value * 10)); - } - $binary .= \pack('n*', $opt, \strlen((string) $value)) . $value; - } - break; - default: - // RDATA is already stored as binary value for unknown record types - $binary = $record->data; - } - - $data .= $this->domainNameToBinary($record->name); - $data .= \pack('nnNn', $record->type, $record->class, $record->ttl, \strlen($binary)); - $data .= $binary; - } - - return $data; - } - - /** - * @param string[] $texts - * @return string - */ - private function textsToBinary(array $texts) - { - $data = ''; - foreach ($texts as $text) { - $data .= \chr(\strlen($text)) . $text; - } - return $data; - } - - /** - * @param string $host - * @return string - */ - private function domainNameToBinary($host) - { - if ($host === '') { - return "\0"; - } - - // break up domain name at each dot that is not preceeded by a backslash (escaped notation) - return $this->textsToBinary( - \array_map( - 'stripcslashes', - \preg_split( - '/(?parse($data, 0); - if ($message === null) { - throw new InvalidArgumentException('Unable to parse binary message'); - } - - return $message; - } - - /** - * @param string $data - * @param int $consumed - * @return ?Message - */ - private function parse($data, $consumed) - { - if (!isset($data[12 - 1])) { - return null; - } - - list($id, $fields, $qdCount, $anCount, $nsCount, $arCount) = array_values(unpack('n*', substr($data, 0, 12))); - - $message = new Message(); - $message->id = $id; - $message->rcode = $fields & 0xf; - $message->ra = (($fields >> 7) & 1) === 1; - $message->rd = (($fields >> 8) & 1) === 1; - $message->tc = (($fields >> 9) & 1) === 1; - $message->aa = (($fields >> 10) & 1) === 1; - $message->opcode = ($fields >> 11) & 0xf; - $message->qr = (($fields >> 15) & 1) === 1; - $consumed += 12; - - // parse all questions - for ($i = $qdCount; $i > 0; --$i) { - list($question, $consumed) = $this->parseQuestion($data, $consumed); - if ($question === null) { - return null; - } else { - $message->questions[] = $question; - } - } - - // parse all answer records - for ($i = $anCount; $i > 0; --$i) { - list($record, $consumed) = $this->parseRecord($data, $consumed); - if ($record === null) { - return null; - } else { - $message->answers[] = $record; - } - } - - // parse all authority records - for ($i = $nsCount; $i > 0; --$i) { - list($record, $consumed) = $this->parseRecord($data, $consumed); - if ($record === null) { - return null; - } else { - $message->authority[] = $record; - } - } - - // parse all additional records - for ($i = $arCount; $i > 0; --$i) { - list($record, $consumed) = $this->parseRecord($data, $consumed); - if ($record === null) { - return null; - } else { - $message->additional[] = $record; - } - } - - return $message; - } - - /** - * @param string $data - * @param int $consumed - * @return array - */ - private function parseQuestion($data, $consumed) - { - list($labels, $consumed) = $this->readLabels($data, $consumed); - - if ($labels === null || !isset($data[$consumed + 4 - 1])) { - return array(null, null); - } - - list($type, $class) = array_values(unpack('n*', substr($data, $consumed, 4))); - $consumed += 4; - - return array( - new Query( - implode('.', $labels), - $type, - $class - ), - $consumed - ); - } - - /** - * @param string $data - * @param int $consumed - * @return array An array with a parsed Record on success or array with null if data is invalid/incomplete - */ - private function parseRecord($data, $consumed) - { - list($name, $consumed) = $this->readDomain($data, $consumed); - - if ($name === null || !isset($data[$consumed + 10 - 1])) { - return array(null, null); - } - - list($type, $class) = array_values(unpack('n*', substr($data, $consumed, 4))); - $consumed += 4; - - list($ttl) = array_values(unpack('N', substr($data, $consumed, 4))); - $consumed += 4; - - // TTL is a UINT32 that must not have most significant bit set for BC reasons - if ($ttl < 0 || $ttl >= 1 << 31) { - $ttl = 0; - } - - list($rdLength) = array_values(unpack('n', substr($data, $consumed, 2))); - $consumed += 2; - - if (!isset($data[$consumed + $rdLength - 1])) { - return array(null, null); - } - - $rdata = null; - $expected = $consumed + $rdLength; - - if (Message::TYPE_A === $type) { - if ($rdLength === 4) { - $rdata = inet_ntop(substr($data, $consumed, $rdLength)); - $consumed += $rdLength; - } - } elseif (Message::TYPE_AAAA === $type) { - if ($rdLength === 16) { - $rdata = inet_ntop(substr($data, $consumed, $rdLength)); - $consumed += $rdLength; - } - } elseif (Message::TYPE_CNAME === $type || Message::TYPE_PTR === $type || Message::TYPE_NS === $type) { - list($rdata, $consumed) = $this->readDomain($data, $consumed); - } elseif (Message::TYPE_TXT === $type || Message::TYPE_SPF === $type) { - $rdata = array(); - while ($consumed < $expected) { - $len = ord($data[$consumed]); - $rdata[] = (string)substr($data, $consumed + 1, $len); - $consumed += $len + 1; - } - } elseif (Message::TYPE_MX === $type) { - if ($rdLength > 2) { - list($priority) = array_values(unpack('n', substr($data, $consumed, 2))); - list($target, $consumed) = $this->readDomain($data, $consumed + 2); - - $rdata = array( - 'priority' => $priority, - 'target' => $target - ); - } - } elseif (Message::TYPE_SRV === $type) { - if ($rdLength > 6) { - list($priority, $weight, $port) = array_values(unpack('n*', substr($data, $consumed, 6))); - list($target, $consumed) = $this->readDomain($data, $consumed + 6); - - $rdata = array( - 'priority' => $priority, - 'weight' => $weight, - 'port' => $port, - 'target' => $target - ); - } - } elseif (Message::TYPE_SSHFP === $type) { - if ($rdLength > 2) { - list($algorithm, $hash) = \array_values(\unpack('C*', \substr($data, $consumed, 2))); - $fingerprint = \bin2hex(\substr($data, $consumed + 2, $rdLength - 2)); - $consumed += $rdLength; - - $rdata = array( - 'algorithm' => $algorithm, - 'type' => $hash, - 'fingerprint' => $fingerprint - ); - } - } elseif (Message::TYPE_SOA === $type) { - list($mname, $consumed) = $this->readDomain($data, $consumed); - list($rname, $consumed) = $this->readDomain($data, $consumed); - - if ($mname !== null && $rname !== null && isset($data[$consumed + 20 - 1])) { - list($serial, $refresh, $retry, $expire, $minimum) = array_values(unpack('N*', substr($data, $consumed, 20))); - $consumed += 20; - - $rdata = array( - 'mname' => $mname, - 'rname' => $rname, - 'serial' => $serial, - 'refresh' => $refresh, - 'retry' => $retry, - 'expire' => $expire, - 'minimum' => $minimum - ); - } - } elseif (Message::TYPE_OPT === $type) { - $rdata = array(); - while (isset($data[$consumed + 4 - 1])) { - list($code, $length) = array_values(unpack('n*', substr($data, $consumed, 4))); - $value = (string) substr($data, $consumed + 4, $length); - if ($code === Message::OPT_TCP_KEEPALIVE && $value === '') { - $value = null; - } elseif ($code === Message::OPT_TCP_KEEPALIVE && $length === 2) { - list($value) = array_values(unpack('n', $value)); - $value = round($value * 0.1, 1); - } elseif ($code === Message::OPT_TCP_KEEPALIVE) { - break; - } - $rdata[$code] = $value; - $consumed += 4 + $length; - } - } elseif (Message::TYPE_CAA === $type) { - if ($rdLength > 3) { - list($flag, $tagLength) = array_values(unpack('C*', substr($data, $consumed, 2))); - - if ($tagLength > 0 && $rdLength - 2 - $tagLength > 0) { - $tag = substr($data, $consumed + 2, $tagLength); - $value = substr($data, $consumed + 2 + $tagLength, $rdLength - 2 - $tagLength); - $consumed += $rdLength; - - $rdata = array( - 'flag' => $flag, - 'tag' => $tag, - 'value' => $value - ); - } - } - } else { - // unknown types simply parse rdata as an opaque binary string - $rdata = substr($data, $consumed, $rdLength); - $consumed += $rdLength; - } - - // ensure parsing record data consumes expact number of bytes indicated in record length - if ($consumed !== $expected || $rdata === null) { - return array(null, null); - } - - return array( - new Record($name, $type, $class, $ttl, $rdata), - $consumed - ); - } - - private function readDomain($data, $consumed) - { - list ($labels, $consumed) = $this->readLabels($data, $consumed); - - if ($labels === null) { - return array(null, null); - } - - // use escaped notation for each label part, then join using dots - return array( - \implode( - '.', - \array_map( - function ($label) { - return \addcslashes($label, "\0..\40.\177"); - }, - $labels - ) - ), - $consumed - ); - } - - /** - * @param string $data - * @param int $consumed - * @param int $compressionDepth maximum depth for compressed labels to avoid unreasonable recursion - * @return array - */ - private function readLabels($data, $consumed, $compressionDepth = 127) - { - $labels = array(); - - while (true) { - if (!isset($data[$consumed])) { - return array(null, null); - } - - $length = \ord($data[$consumed]); - - // end of labels reached - if ($length === 0) { - $consumed += 1; - break; - } - - // first two bits set? this is a compressed label (14 bit pointer offset) - if (($length & 0xc0) === 0xc0 && isset($data[$consumed + 1]) && $compressionDepth) { - $offset = ($length & ~0xc0) << 8 | \ord($data[$consumed + 1]); - if ($offset >= $consumed) { - return array(null, null); - } - - $consumed += 2; - list($newLabels) = $this->readLabels($data, $offset, $compressionDepth - 1); - - if ($newLabels === null) { - return array(null, null); - } - - $labels = array_merge($labels, $newLabels); - break; - } - - // length MUST be 0-63 (6 bits only) and data has to be large enough - if ($length & 0xc0 || !isset($data[$consumed + $length - 1])) { - return array(null, null); - } - - $labels[] = substr($data, $consumed + 1, $length); - $consumed += $length + 1; - } - - return array($labels, $consumed); - } -} diff --git a/vendor/react/dns/src/Query/CachingExecutor.php b/vendor/react/dns/src/Query/CachingExecutor.php deleted file mode 100644 index e530b24..0000000 --- a/vendor/react/dns/src/Query/CachingExecutor.php +++ /dev/null @@ -1,88 +0,0 @@ -executor = $executor; - $this->cache = $cache; - } - - public function query(Query $query) - { - $id = $query->name . ':' . $query->type . ':' . $query->class; - $cache = $this->cache; - $that = $this; - $executor = $this->executor; - - $pending = $cache->get($id); - return new Promise(function ($resolve, $reject) use ($query, $id, $cache, $executor, &$pending, $that) { - $pending->then( - function ($message) use ($query, $id, $cache, $executor, &$pending, $that) { - // return cached response message on cache hit - if ($message !== null) { - return $message; - } - - // perform DNS lookup if not already cached - return $pending = $executor->query($query)->then( - function (Message $message) use ($cache, $id, $that) { - // DNS response message received => store in cache when not truncated and return - if (!$message->tc) { - $cache->set($id, $message, $that->ttl($message)); - } - - return $message; - } - ); - } - )->then($resolve, function ($e) use ($reject, &$pending) { - $reject($e); - $pending = null; - }); - }, function ($_, $reject) use (&$pending, $query) { - $reject(new \RuntimeException('DNS query for ' . $query->describe() . ' has been cancelled')); - $pending->cancel(); - $pending = null; - }); - } - - /** - * @param Message $message - * @return int - * @internal - */ - public function ttl(Message $message) - { - // select TTL from answers (should all be the same), use smallest value if available - // @link https://tools.ietf.org/html/rfc2181#section-5.2 - $ttl = null; - foreach ($message->answers as $answer) { - if ($ttl === null || $answer->ttl < $ttl) { - $ttl = $answer->ttl; - } - } - - if ($ttl === null) { - $ttl = self::TTL; - } - - return $ttl; - } -} diff --git a/vendor/react/dns/src/Query/CancellationException.php b/vendor/react/dns/src/Query/CancellationException.php deleted file mode 100644 index 5432b36..0000000 --- a/vendor/react/dns/src/Query/CancellationException.php +++ /dev/null @@ -1,7 +0,0 @@ -executor = $base; - } - - public function query(Query $query) - { - $key = $this->serializeQueryToIdentity($query); - if (isset($this->pending[$key])) { - // same query is already pending, so use shared reference to pending query - $promise = $this->pending[$key]; - ++$this->counts[$key]; - } else { - // no such query pending, so start new query and keep reference until it's fulfilled or rejected - $promise = $this->executor->query($query); - $this->pending[$key] = $promise; - $this->counts[$key] = 1; - - $pending =& $this->pending; - $counts =& $this->counts; - $promise->then(function () use ($key, &$pending, &$counts) { - unset($pending[$key], $counts[$key]); - }, function () use ($key, &$pending, &$counts) { - unset($pending[$key], $counts[$key]); - }); - } - - // Return a child promise awaiting the pending query. - // Cancelling this child promise should only cancel the pending query - // when no other child promise is awaiting the same query. - $pending =& $this->pending; - $counts =& $this->counts; - return new Promise(function ($resolve, $reject) use ($promise) { - $promise->then($resolve, $reject); - }, function () use (&$promise, $key, $query, &$pending, &$counts) { - if (--$counts[$key] < 1) { - unset($pending[$key], $counts[$key]); - $promise->cancel(); - $promise = null; - } - throw new \RuntimeException('DNS query for ' . $query->describe() . ' has been cancelled'); - }); - } - - private function serializeQueryToIdentity(Query $query) - { - return sprintf('%s:%s:%s', $query->name, $query->type, $query->class); - } -} diff --git a/vendor/react/dns/src/Query/ExecutorInterface.php b/vendor/react/dns/src/Query/ExecutorInterface.php deleted file mode 100644 index 0bc3945..0000000 --- a/vendor/react/dns/src/Query/ExecutorInterface.php +++ /dev/null @@ -1,43 +0,0 @@ -query($query)->then( - * function (React\Dns\Model\Message $response) { - * // response message successfully received - * var_dump($response->rcode, $response->answers); - * }, - * function (Exception $error) { - * // failed to query due to $error - * } - * ); - * ``` - * - * The returned Promise MUST be implemented in such a way that it can be - * cancelled when it is still pending. Cancelling a pending promise MUST - * reject its value with an Exception. It SHOULD clean up any underlying - * resources and references as applicable. - * - * ```php - * $promise = $executor->query($query); - * - * $promise->cancel(); - * ``` - * - * @param Query $query - * @return \React\Promise\PromiseInterface<\React\Dns\Model\Message> - * resolves with response message on success or rejects with an Exception on error - */ - public function query(Query $query); -} diff --git a/vendor/react/dns/src/Query/FallbackExecutor.php b/vendor/react/dns/src/Query/FallbackExecutor.php deleted file mode 100644 index 83bd360..0000000 --- a/vendor/react/dns/src/Query/FallbackExecutor.php +++ /dev/null @@ -1,49 +0,0 @@ -executor = $executor; - $this->fallback = $fallback; - } - - public function query(Query $query) - { - $cancelled = false; - $fallback = $this->fallback; - $promise = $this->executor->query($query); - - return new Promise(function ($resolve, $reject) use (&$promise, $fallback, $query, &$cancelled) { - $promise->then($resolve, function (\Exception $e1) use ($fallback, $query, $resolve, $reject, &$cancelled, &$promise) { - // reject if primary resolution rejected due to cancellation - if ($cancelled) { - $reject($e1); - return; - } - - // start fallback query if primary query rejected - $promise = $fallback->query($query)->then($resolve, function (\Exception $e2) use ($e1, $reject) { - $append = $e2->getMessage(); - if (($pos = strpos($append, ':')) !== false) { - $append = substr($append, $pos + 2); - } - - // reject with combined error message if both queries fail - $reject(new \RuntimeException($e1->getMessage() . '. ' . $append)); - }); - }); - }, function () use (&$promise, &$cancelled) { - // cancel pending query (primary or fallback) - $cancelled = true; - $promise->cancel(); - }); - } -} diff --git a/vendor/react/dns/src/Query/HostsFileExecutor.php b/vendor/react/dns/src/Query/HostsFileExecutor.php deleted file mode 100644 index d6e2d93..0000000 --- a/vendor/react/dns/src/Query/HostsFileExecutor.php +++ /dev/null @@ -1,89 +0,0 @@ -hosts = $hosts; - $this->fallback = $fallback; - } - - public function query(Query $query) - { - if ($query->class === Message::CLASS_IN && ($query->type === Message::TYPE_A || $query->type === Message::TYPE_AAAA)) { - // forward lookup for type A or AAAA - $records = array(); - $expectsColon = $query->type === Message::TYPE_AAAA; - foreach ($this->hosts->getIpsForHost($query->name) as $ip) { - // ensure this is an IPv4/IPV6 address according to query type - if ((strpos($ip, ':') !== false) === $expectsColon) { - $records[] = new Record($query->name, $query->type, $query->class, 0, $ip); - } - } - - if ($records) { - return Promise\resolve( - Message::createResponseWithAnswersForQuery($query, $records) - ); - } - } elseif ($query->class === Message::CLASS_IN && $query->type === Message::TYPE_PTR) { - // reverse lookup: extract IPv4 or IPv6 from special `.arpa` domain - $ip = $this->getIpFromHost($query->name); - - if ($ip !== null) { - $records = array(); - foreach ($this->hosts->getHostsForIp($ip) as $host) { - $records[] = new Record($query->name, $query->type, $query->class, 0, $host); - } - - if ($records) { - return Promise\resolve( - Message::createResponseWithAnswersForQuery($query, $records) - ); - } - } - } - - return $this->fallback->query($query); - } - - private function getIpFromHost($host) - { - if (substr($host, -13) === '.in-addr.arpa') { - // IPv4: read as IP and reverse bytes - $ip = @inet_pton(substr($host, 0, -13)); - if ($ip === false || isset($ip[4])) { - return null; - } - - return inet_ntop(strrev($ip)); - } elseif (substr($host, -9) === '.ip6.arpa') { - // IPv6: replace dots, reverse nibbles and interpret as hexadecimal string - $ip = @inet_ntop(pack('H*', strrev(str_replace('.', '', substr($host, 0, -9))))); - if ($ip === false) { - return null; - } - - return $ip; - } else { - return null; - } - } -} diff --git a/vendor/react/dns/src/Query/Query.php b/vendor/react/dns/src/Query/Query.php deleted file mode 100644 index a3dcfb5..0000000 --- a/vendor/react/dns/src/Query/Query.php +++ /dev/null @@ -1,69 +0,0 @@ -name = $name; - $this->type = $type; - $this->class = $class; - } - - /** - * Describes the hostname and query type/class for this query - * - * The output format is supposed to be human readable and is subject to change. - * The format is inspired by RFC 3597 when handling unkown types/classes. - * - * @return string "example.com (A)" or "example.com (CLASS0 TYPE1234)" - * @link https://tools.ietf.org/html/rfc3597 - */ - public function describe() - { - $class = $this->class !== Message::CLASS_IN ? 'CLASS' . $this->class . ' ' : ''; - - $type = 'TYPE' . $this->type; - $ref = new \ReflectionClass('React\Dns\Model\Message'); - foreach ($ref->getConstants() as $name => $value) { - if ($value === $this->type && \strpos($name, 'TYPE_') === 0) { - $type = \substr($name, 5); - break; - } - } - - return $this->name . ' (' . $class . $type . ')'; - } -} diff --git a/vendor/react/dns/src/Query/RetryExecutor.php b/vendor/react/dns/src/Query/RetryExecutor.php deleted file mode 100644 index 880609b..0000000 --- a/vendor/react/dns/src/Query/RetryExecutor.php +++ /dev/null @@ -1,85 +0,0 @@ -executor = $executor; - $this->retries = $retries; - } - - public function query(Query $query) - { - return $this->tryQuery($query, $this->retries); - } - - public function tryQuery(Query $query, $retries) - { - $deferred = new Deferred(function () use (&$promise) { - if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { - $promise->cancel(); - } - }); - - $success = function ($value) use ($deferred, &$errorback) { - $errorback = null; - $deferred->resolve($value); - }; - - $executor = $this->executor; - $errorback = function ($e) use ($deferred, &$promise, $query, $success, &$errorback, &$retries, $executor) { - if (!$e instanceof TimeoutException) { - $errorback = null; - $deferred->reject($e); - } elseif ($retries <= 0) { - $errorback = null; - $deferred->reject($e = new \RuntimeException( - 'DNS query for ' . $query->describe() . ' failed: too many retries', - 0, - $e - )); - - // avoid garbage references by replacing all closures in call stack. - // what a lovely piece of code! - $r = new \ReflectionProperty('Exception', 'trace'); - $r->setAccessible(true); - $trace = $r->getValue($e); - - // Exception trace arguments are not available on some PHP 7.4 installs - // @codeCoverageIgnoreStart - foreach ($trace as $ti => $one) { - if (isset($one['args'])) { - foreach ($one['args'] as $ai => $arg) { - if ($arg instanceof \Closure) { - $trace[$ti]['args'][$ai] = 'Object(' . \get_class($arg) . ')'; - } - } - } - } - // @codeCoverageIgnoreEnd - $r->setValue($e, $trace); - } else { - --$retries; - $promise = $executor->query($query)->then( - $success, - $errorback - ); - } - }; - - $promise = $this->executor->query($query)->then( - $success, - $errorback - ); - - return $deferred->promise(); - } -} diff --git a/vendor/react/dns/src/Query/SelectiveTransportExecutor.php b/vendor/react/dns/src/Query/SelectiveTransportExecutor.php deleted file mode 100644 index 0f0ca5d..0000000 --- a/vendor/react/dns/src/Query/SelectiveTransportExecutor.php +++ /dev/null @@ -1,85 +0,0 @@ -query( - * new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) - * )->then(function (Message $message) { - * foreach ($message->answers as $answer) { - * echo 'IPv6: ' . $answer->data . PHP_EOL; - * } - * }, 'printf'); - * ``` - * - * Note that this executor only implements the logic to select the correct - * transport for the given DNS query. Implementing the correct transport logic, - * implementing timeouts and any retry logic is left up to the given executors, - * see also [`UdpTransportExecutor`](#udptransportexecutor) and - * [`TcpTransportExecutor`](#tcptransportexecutor) for more details. - * - * Note that this executor is entirely async and as such allows you to execute - * any number of queries concurrently. You should probably limit the number of - * concurrent queries in your application or you're very likely going to face - * rate limitations and bans on the resolver end. For many common applications, - * you may want to avoid sending the same query multiple times when the first - * one is still pending, so you will likely want to use this in combination with - * a `CoopExecutor` like this: - * - * ```php - * $executor = new CoopExecutor( - * new SelectiveTransportExecutor( - * $datagramExecutor, - * $streamExecutor - * ) - * ); - * ``` - */ -class SelectiveTransportExecutor implements ExecutorInterface -{ - private $datagramExecutor; - private $streamExecutor; - - public function __construct(ExecutorInterface $datagramExecutor, ExecutorInterface $streamExecutor) - { - $this->datagramExecutor = $datagramExecutor; - $this->streamExecutor = $streamExecutor; - } - - public function query(Query $query) - { - $stream = $this->streamExecutor; - $pending = $this->datagramExecutor->query($query); - - return new Promise(function ($resolve, $reject) use (&$pending, $stream, $query) { - $pending->then( - $resolve, - function ($e) use (&$pending, $stream, $query, $resolve, $reject) { - if ($e->getCode() === (\defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90)) { - $pending = $stream->query($query)->then($resolve, $reject); - } else { - $reject($e); - } - } - ); - }, function () use (&$pending) { - $pending->cancel(); - $pending = null; - }); - } -} diff --git a/vendor/react/dns/src/Query/TcpTransportExecutor.php b/vendor/react/dns/src/Query/TcpTransportExecutor.php deleted file mode 100644 index 669fd01..0000000 --- a/vendor/react/dns/src/Query/TcpTransportExecutor.php +++ /dev/null @@ -1,382 +0,0 @@ -query( - * new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) - * )->then(function (Message $message) { - * foreach ($message->answers as $answer) { - * echo 'IPv6: ' . $answer->data . PHP_EOL; - * } - * }, 'printf'); - * ``` - * - * See also [example #92](examples). - * - * Note that this executor does not implement a timeout, so you will very likely - * want to use this in combination with a `TimeoutExecutor` like this: - * - * ```php - * $executor = new TimeoutExecutor( - * new TcpTransportExecutor($nameserver), - * 3.0 - * ); - * ``` - * - * Unlike the `UdpTransportExecutor`, this class uses a reliable TCP/IP - * transport, so you do not necessarily have to implement any retry logic. - * - * Note that this executor is entirely async and as such allows you to execute - * queries concurrently. The first query will establish a TCP/IP socket - * connection to the DNS server which will be kept open for a short period. - * Additional queries will automatically reuse this existing socket connection - * to the DNS server, will pipeline multiple requests over this single - * connection and will keep an idle connection open for a short period. The - * initial TCP/IP connection overhead may incur a slight delay if you only send - * occasional queries – when sending a larger number of concurrent queries over - * an existing connection, it becomes increasingly more efficient and avoids - * creating many concurrent sockets like the UDP-based executor. You may still - * want to limit the number of (concurrent) queries in your application or you - * may be facing rate limitations and bans on the resolver end. For many common - * applications, you may want to avoid sending the same query multiple times - * when the first one is still pending, so you will likely want to use this in - * combination with a `CoopExecutor` like this: - * - * ```php - * $executor = new CoopExecutor( - * new TimeoutExecutor( - * new TcpTransportExecutor($nameserver), - * 3.0 - * ) - * ); - * ``` - * - * > Internally, this class uses PHP's TCP/IP sockets and does not take advantage - * of [react/socket](https://github.com/reactphp/socket) purely for - * organizational reasons to avoid a cyclic dependency between the two - * packages. Higher-level components should take advantage of the Socket - * component instead of reimplementing this socket logic from scratch. - */ -class TcpTransportExecutor implements ExecutorInterface -{ - private $nameserver; - private $loop; - private $parser; - private $dumper; - - /** - * @var ?resource - */ - private $socket; - - /** - * @var Deferred[] - */ - private $pending = array(); - - /** - * @var string[] - */ - private $names = array(); - - /** - * Maximum idle time when socket is current unused (i.e. no pending queries outstanding) - * - * If a new query is to be sent during the idle period, we can reuse the - * existing socket without having to wait for a new socket connection. - * This uses a rather small, hard-coded value to not keep any unneeded - * sockets open and to not keep the loop busy longer than needed. - * - * A future implementation may take advantage of `edns-tcp-keepalive` to keep - * the socket open for longer periods. This will likely require explicit - * configuration because this may consume additional resources and also keep - * the loop busy for longer than expected in some applications. - * - * @var float - * @link https://tools.ietf.org/html/rfc7766#section-6.2.1 - * @link https://tools.ietf.org/html/rfc7828 - */ - private $idlePeriod = 0.001; - - /** - * @var ?\React\EventLoop\TimerInterface - */ - private $idleTimer; - - private $writeBuffer = ''; - private $writePending = false; - - private $readBuffer = ''; - private $readPending = false; - - /** @var string */ - private $readChunk = 0xffff; - - /** - * @param string $nameserver - * @param ?LoopInterface $loop - */ - public function __construct($nameserver, $loop = null) - { - if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) { - // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets - $nameserver = '[' . $nameserver . ']'; - } - - $parts = \parse_url((\strpos($nameserver, '://') === false ? 'tcp://' : '') . $nameserver); - if (!isset($parts['scheme'], $parts['host']) || $parts['scheme'] !== 'tcp' || @\inet_pton(\trim($parts['host'], '[]')) === false) { - throw new \InvalidArgumentException('Invalid nameserver address given'); - } - - if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 - throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); - } - - $this->nameserver = 'tcp://' . $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53); - $this->loop = $loop ?: Loop::get(); - $this->parser = new Parser(); - $this->dumper = new BinaryDumper(); - } - - public function query(Query $query) - { - $request = Message::createRequestForQuery($query); - - // keep shuffing message ID to avoid using the same message ID for two pending queries at the same time - while (isset($this->pending[$request->id])) { - $request->id = \mt_rand(0, 0xffff); // @codeCoverageIgnore - } - - $queryData = $this->dumper->toBinary($request); - $length = \strlen($queryData); - if ($length > 0xffff) { - return \React\Promise\reject(new \RuntimeException( - 'DNS query for ' . $query->describe() . ' failed: Query too large for TCP transport' - )); - } - - $queryData = \pack('n', $length) . $queryData; - - if ($this->socket === null) { - // create async TCP/IP connection (may take a while) - $socket = @\stream_socket_client($this->nameserver, $errno, $errstr, 0, \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT); - if ($socket === false) { - return \React\Promise\reject(new \RuntimeException( - 'DNS query for ' . $query->describe() . ' failed: Unable to connect to DNS server ' . $this->nameserver . ' (' . $errstr . ')', - $errno - )); - } - - // set socket to non-blocking and wait for it to become writable (connection success/rejected) - \stream_set_blocking($socket, false); - if (\function_exists('stream_set_chunk_size')) { - \stream_set_chunk_size($socket, $this->readChunk); // @codeCoverageIgnore - } - $this->socket = $socket; - } - - if ($this->idleTimer !== null) { - $this->loop->cancelTimer($this->idleTimer); - $this->idleTimer = null; - } - - // wait for socket to become writable to actually write out data - $this->writeBuffer .= $queryData; - if (!$this->writePending) { - $this->writePending = true; - $this->loop->addWriteStream($this->socket, array($this, 'handleWritable')); - } - - $names =& $this->names; - $that = $this; - $deferred = new Deferred(function () use ($that, &$names, $request) { - // remove from list of pending names, but remember pending query - $name = $names[$request->id]; - unset($names[$request->id]); - $that->checkIdle(); - - throw new CancellationException('DNS query for ' . $name . ' has been cancelled'); - }); - - $this->pending[$request->id] = $deferred; - $this->names[$request->id] = $query->describe(); - - return $deferred->promise(); - } - - /** - * @internal - */ - public function handleWritable() - { - if ($this->readPending === false) { - $name = @\stream_socket_get_name($this->socket, true); - if ($name === false) { - // Connection failed? Check socket error if available for underlying errno/errstr. - // @codeCoverageIgnoreStart - if (\function_exists('socket_import_stream')) { - $socket = \socket_import_stream($this->socket); - $errno = \socket_get_option($socket, \SOL_SOCKET, \SO_ERROR); - $errstr = \socket_strerror($errno); - } else { - $errno = \defined('SOCKET_ECONNREFUSED') ? \SOCKET_ECONNREFUSED : 111; - $errstr = 'Connection refused'; - } - // @codeCoverageIgnoreEnd - - $this->closeError('Unable to connect to DNS server ' . $this->nameserver . ' (' . $errstr . ')', $errno); - return; - } - - $this->readPending = true; - $this->loop->addReadStream($this->socket, array($this, 'handleRead')); - } - - $errno = 0; - $errstr = ''; - \set_error_handler(function ($_, $error) use (&$errno, &$errstr) { - // Match errstr from PHP's warning message. - // fwrite(): Send of 327712 bytes failed with errno=32 Broken pipe - \preg_match('/errno=(\d+) (.+)/', $error, $m); - $errno = isset($m[1]) ? (int) $m[1] : 0; - $errstr = isset($m[2]) ? $m[2] : $error; - }); - - $written = \fwrite($this->socket, $this->writeBuffer); - - \restore_error_handler(); - - if ($written === false || $written === 0) { - $this->closeError( - 'Unable to send query to DNS server ' . $this->nameserver . ' (' . $errstr . ')', - $errno - ); - return; - } - - if (isset($this->writeBuffer[$written])) { - $this->writeBuffer = \substr($this->writeBuffer, $written); - } else { - $this->loop->removeWriteStream($this->socket); - $this->writePending = false; - $this->writeBuffer = ''; - } - } - - /** - * @internal - */ - public function handleRead() - { - // read one chunk of data from the DNS server - // any error is fatal, this is a stream of TCP/IP data - $chunk = @\fread($this->socket, $this->readChunk); - if ($chunk === false || $chunk === '') { - $this->closeError('Connection to DNS server ' . $this->nameserver . ' lost'); - return; - } - - // reassemble complete message by concatenating all chunks. - $this->readBuffer .= $chunk; - - // response message header contains at least 12 bytes - while (isset($this->readBuffer[11])) { - // read response message length from first 2 bytes and ensure we have length + data in buffer - list(, $length) = \unpack('n', $this->readBuffer); - if (!isset($this->readBuffer[$length + 1])) { - return; - } - - $data = \substr($this->readBuffer, 2, $length); - $this->readBuffer = (string)substr($this->readBuffer, $length + 2); - - try { - $response = $this->parser->parseMessage($data); - } catch (\Exception $e) { - // reject all pending queries if we received an invalid message from remote server - $this->closeError('Invalid message received from DNS server ' . $this->nameserver); - return; - } - - // reject all pending queries if we received an unexpected response ID or truncated response - if (!isset($this->pending[$response->id]) || $response->tc) { - $this->closeError('Invalid response message received from DNS server ' . $this->nameserver); - return; - } - - $deferred = $this->pending[$response->id]; - unset($this->pending[$response->id], $this->names[$response->id]); - - $deferred->resolve($response); - - $this->checkIdle(); - } - } - - /** - * @internal - * @param string $reason - * @param int $code - */ - public function closeError($reason, $code = 0) - { - $this->readBuffer = ''; - if ($this->readPending) { - $this->loop->removeReadStream($this->socket); - $this->readPending = false; - } - - $this->writeBuffer = ''; - if ($this->writePending) { - $this->loop->removeWriteStream($this->socket); - $this->writePending = false; - } - - if ($this->idleTimer !== null) { - $this->loop->cancelTimer($this->idleTimer); - $this->idleTimer = null; - } - - @\fclose($this->socket); - $this->socket = null; - - foreach ($this->names as $id => $name) { - $this->pending[$id]->reject(new \RuntimeException( - 'DNS query for ' . $name . ' failed: ' . $reason, - $code - )); - } - $this->pending = $this->names = array(); - } - - /** - * @internal - */ - public function checkIdle() - { - if ($this->idleTimer === null && !$this->names) { - $that = $this; - $this->idleTimer = $this->loop->addTimer($this->idlePeriod, function () use ($that) { - $that->closeError('Idle timeout'); - }); - } - } -} diff --git a/vendor/react/dns/src/Query/TimeoutException.php b/vendor/react/dns/src/Query/TimeoutException.php deleted file mode 100644 index 109b0a9..0000000 --- a/vendor/react/dns/src/Query/TimeoutException.php +++ /dev/null @@ -1,7 +0,0 @@ -executor = $executor; - $this->loop = $loop ?: Loop::get(); - $this->timeout = $timeout; - } - - public function query(Query $query) - { - $promise = $this->executor->query($query); - - $loop = $this->loop; - $time = $this->timeout; - return new Promise(function ($resolve, $reject) use ($loop, $time, $promise, $query) { - $timer = null; - $promise = $promise->then(function ($v) use (&$timer, $loop, $resolve) { - if ($timer) { - $loop->cancelTimer($timer); - } - $timer = false; - $resolve($v); - }, function ($v) use (&$timer, $loop, $reject) { - if ($timer) { - $loop->cancelTimer($timer); - } - $timer = false; - $reject($v); - }); - - // promise already resolved => no need to start timer - if ($timer === false) { - return; - } - - // start timeout timer which will cancel the pending promise - $timer = $loop->addTimer($time, function () use ($time, &$promise, $reject, $query) { - $reject(new TimeoutException( - 'DNS query for ' . $query->describe() . ' timed out' - )); - - // Cancel pending query to clean up any underlying resources and references. - // Avoid garbage references in call stack by passing pending promise by reference. - assert(\method_exists($promise, 'cancel')); - $promise->cancel(); - $promise = null; - }); - }, function () use (&$promise) { - // Cancelling this promise will cancel the pending query, thus triggering the rejection logic above. - // Avoid garbage references in call stack by passing pending promise by reference. - assert(\method_exists($promise, 'cancel')); - $promise->cancel(); - $promise = null; - }); - } -} diff --git a/vendor/react/dns/src/Query/UdpTransportExecutor.php b/vendor/react/dns/src/Query/UdpTransportExecutor.php deleted file mode 100644 index a8cbfaf..0000000 --- a/vendor/react/dns/src/Query/UdpTransportExecutor.php +++ /dev/null @@ -1,221 +0,0 @@ -query( - * new Query($name, Message::TYPE_AAAA, Message::CLASS_IN) - * )->then(function (Message $message) { - * foreach ($message->answers as $answer) { - * echo 'IPv6: ' . $answer->data . PHP_EOL; - * } - * }, 'printf'); - * ``` - * - * See also the [fourth example](examples). - * - * Note that this executor does not implement a timeout, so you will very likely - * want to use this in combination with a `TimeoutExecutor` like this: - * - * ```php - * $executor = new TimeoutExecutor( - * new UdpTransportExecutor($nameserver), - * 3.0 - * ); - * ``` - * - * Also note that this executor uses an unreliable UDP transport and that it - * does not implement any retry logic, so you will likely want to use this in - * combination with a `RetryExecutor` like this: - * - * ```php - * $executor = new RetryExecutor( - * new TimeoutExecutor( - * new UdpTransportExecutor($nameserver), - * 3.0 - * ) - * ); - * ``` - * - * Note that this executor is entirely async and as such allows you to execute - * any number of queries concurrently. You should probably limit the number of - * concurrent queries in your application or you're very likely going to face - * rate limitations and bans on the resolver end. For many common applications, - * you may want to avoid sending the same query multiple times when the first - * one is still pending, so you will likely want to use this in combination with - * a `CoopExecutor` like this: - * - * ```php - * $executor = new CoopExecutor( - * new RetryExecutor( - * new TimeoutExecutor( - * new UdpTransportExecutor($nameserver), - * 3.0 - * ) - * ) - * ); - * ``` - * - * > Internally, this class uses PHP's UDP sockets and does not take advantage - * of [react/datagram](https://github.com/reactphp/datagram) purely for - * organizational reasons to avoid a cyclic dependency between the two - * packages. Higher-level components should take advantage of the Datagram - * component instead of reimplementing this socket logic from scratch. - */ -final class UdpTransportExecutor implements ExecutorInterface -{ - private $nameserver; - private $loop; - private $parser; - private $dumper; - - /** - * maximum UDP packet size to send and receive - * - * @var int - */ - private $maxPacketSize = 512; - - /** - * @param string $nameserver - * @param ?LoopInterface $loop - */ - public function __construct($nameserver, $loop = null) - { - if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) { - // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets - $nameserver = '[' . $nameserver . ']'; - } - - $parts = \parse_url((\strpos($nameserver, '://') === false ? 'udp://' : '') . $nameserver); - if (!isset($parts['scheme'], $parts['host']) || $parts['scheme'] !== 'udp' || @\inet_pton(\trim($parts['host'], '[]')) === false) { - throw new \InvalidArgumentException('Invalid nameserver address given'); - } - - if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 - throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); - } - - $this->nameserver = 'udp://' . $parts['host'] . ':' . (isset($parts['port']) ? $parts['port'] : 53); - $this->loop = $loop ?: Loop::get(); - $this->parser = new Parser(); - $this->dumper = new BinaryDumper(); - } - - public function query(Query $query) - { - $request = Message::createRequestForQuery($query); - - $queryData = $this->dumper->toBinary($request); - if (isset($queryData[$this->maxPacketSize])) { - return \React\Promise\reject(new \RuntimeException( - 'DNS query for ' . $query->describe() . ' failed: Query too large for UDP transport', - \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90 - )); - } - - // UDP connections are instant, so try connection without a loop or timeout - $errno = 0; - $errstr = ''; - $socket = @\stream_socket_client($this->nameserver, $errno, $errstr, 0); - if ($socket === false) { - return \React\Promise\reject(new \RuntimeException( - 'DNS query for ' . $query->describe() . ' failed: Unable to connect to DNS server ' . $this->nameserver . ' (' . $errstr . ')', - $errno - )); - } - - // set socket to non-blocking and immediately try to send (fill write buffer) - \stream_set_blocking($socket, false); - - \set_error_handler(function ($_, $error) use (&$errno, &$errstr) { - // Write may potentially fail, but most common errors are already caught by connection check above. - // Among others, macOS is known to report here when trying to send to broadcast address. - // This can also be reproduced by writing data exceeding `stream_set_chunk_size()` to a server refusing UDP data. - // fwrite(): send of 8192 bytes failed with errno=111 Connection refused - \preg_match('/errno=(\d+) (.+)/', $error, $m); - $errno = isset($m[1]) ? (int) $m[1] : 0; - $errstr = isset($m[2]) ? $m[2] : $error; - }); - - $written = \fwrite($socket, $queryData); - - \restore_error_handler(); - - if ($written !== \strlen($queryData)) { - return \React\Promise\reject(new \RuntimeException( - 'DNS query for ' . $query->describe() . ' failed: Unable to send query to DNS server ' . $this->nameserver . ' (' . $errstr . ')', - $errno - )); - } - - $loop = $this->loop; - $deferred = new Deferred(function () use ($loop, $socket, $query) { - // cancellation should remove socket from loop and close socket - $loop->removeReadStream($socket); - \fclose($socket); - - throw new CancellationException('DNS query for ' . $query->describe() . ' has been cancelled'); - }); - - $max = $this->maxPacketSize; - $parser = $this->parser; - $nameserver = $this->nameserver; - $loop->addReadStream($socket, function ($socket) use ($loop, $deferred, $query, $parser, $request, $max, $nameserver) { - // try to read a single data packet from the DNS server - // ignoring any errors, this is uses UDP packets and not a stream of data - $data = @\fread($socket, $max); - if ($data === false) { - return; - } - - try { - $response = $parser->parseMessage($data); - } catch (\Exception $e) { - // ignore and await next if we received an invalid message from remote server - // this may as well be a fake response from an attacker (possible DOS) - return; - } - - // ignore and await next if we received an unexpected response ID - // this may as well be a fake response from an attacker (possible cache poisoning) - if ($response->id !== $request->id) { - return; - } - - // we only react to the first valid message, so remove socket from loop and close - $loop->removeReadStream($socket); - \fclose($socket); - - if ($response->tc) { - $deferred->reject(new \RuntimeException( - 'DNS query for ' . $query->describe() . ' failed: The DNS server ' . $nameserver . ' returned a truncated result for a UDP query', - \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90 - )); - return; - } - - $deferred->resolve($response); - }); - - return $deferred->promise(); - } -} diff --git a/vendor/react/dns/src/RecordNotFoundException.php b/vendor/react/dns/src/RecordNotFoundException.php deleted file mode 100644 index 3b70274..0000000 --- a/vendor/react/dns/src/RecordNotFoundException.php +++ /dev/null @@ -1,7 +0,0 @@ -decorateHostsFileExecutor($this->createExecutor($config, $loop ?: Loop::get())); - - return new Resolver($executor); - } - - /** - * Creates a cached DNS resolver instance for the given DNS config and cache - * - * As of v1.7.0 it's recommended to pass a `Config` object instead of a - * single nameserver address. If the given config contains more than one DNS - * nameserver, all DNS nameservers will be used in order. The primary DNS - * server will always be used first before falling back to the secondary or - * tertiary DNS server. - * - * @param Config|string $config DNS Config object (recommended) or single nameserver address - * @param ?LoopInterface $loop - * @param ?CacheInterface $cache - * @return \React\Dns\Resolver\ResolverInterface - * @throws \InvalidArgumentException for invalid DNS server address - * @throws \UnderflowException when given DNS Config object has an empty list of nameservers - */ - public function createCached($config, $loop = null, $cache = null) - { - if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 - throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); - } - - if ($cache !== null && !$cache instanceof CacheInterface) { // manual type check to support legacy PHP < 7.1 - throw new \InvalidArgumentException('Argument #3 ($cache) expected null|React\Cache\CacheInterface'); - } - - // default to keeping maximum of 256 responses in cache unless explicitly given - if (!($cache instanceof CacheInterface)) { - $cache = new ArrayCache(256); - } - - $executor = $this->createExecutor($config, $loop ?: Loop::get()); - $executor = new CachingExecutor($executor, $cache); - $executor = $this->decorateHostsFileExecutor($executor); - - return new Resolver($executor); - } - - /** - * Tries to load the hosts file and decorates the given executor on success - * - * @param ExecutorInterface $executor - * @return ExecutorInterface - * @codeCoverageIgnore - */ - private function decorateHostsFileExecutor(ExecutorInterface $executor) - { - try { - $executor = new HostsFileExecutor( - HostsFile::loadFromPathBlocking(), - $executor - ); - } catch (\RuntimeException $e) { - // ignore this file if it can not be loaded - } - - // Windows does not store localhost in hosts file by default but handles this internally - // To compensate for this, we explicitly use hard-coded defaults for localhost - if (DIRECTORY_SEPARATOR === '\\') { - $executor = new HostsFileExecutor( - new HostsFile("127.0.0.1 localhost\n::1 localhost"), - $executor - ); - } - - return $executor; - } - - /** - * @param Config|string $nameserver - * @param LoopInterface $loop - * @return CoopExecutor - * @throws \InvalidArgumentException for invalid DNS server address - * @throws \UnderflowException when given DNS Config object has an empty list of nameservers - */ - private function createExecutor($nameserver, LoopInterface $loop) - { - if ($nameserver instanceof Config) { - if (!$nameserver->nameservers) { - throw new \UnderflowException('Empty config with no DNS servers'); - } - - // Hard-coded to check up to 3 DNS servers to match default limits in place in most systems (see MAXNS config). - // Note to future self: Recursion isn't too hard, but how deep do we really want to go? - $primary = reset($nameserver->nameservers); - $secondary = next($nameserver->nameservers); - $tertiary = next($nameserver->nameservers); - - if ($tertiary !== false) { - // 3 DNS servers given => nest first with fallback for second and third - return new CoopExecutor( - new RetryExecutor( - new FallbackExecutor( - $this->createSingleExecutor($primary, $loop), - new FallbackExecutor( - $this->createSingleExecutor($secondary, $loop), - $this->createSingleExecutor($tertiary, $loop) - ) - ) - ) - ); - } elseif ($secondary !== false) { - // 2 DNS servers given => fallback from first to second - return new CoopExecutor( - new RetryExecutor( - new FallbackExecutor( - $this->createSingleExecutor($primary, $loop), - $this->createSingleExecutor($secondary, $loop) - ) - ) - ); - } else { - // 1 DNS server given => use single executor - $nameserver = $primary; - } - } - - return new CoopExecutor(new RetryExecutor($this->createSingleExecutor($nameserver, $loop))); - } - - /** - * @param string $nameserver - * @param LoopInterface $loop - * @return ExecutorInterface - * @throws \InvalidArgumentException for invalid DNS server address - */ - private function createSingleExecutor($nameserver, LoopInterface $loop) - { - $parts = \parse_url($nameserver); - - if (isset($parts['scheme']) && $parts['scheme'] === 'tcp') { - $executor = $this->createTcpExecutor($nameserver, $loop); - } elseif (isset($parts['scheme']) && $parts['scheme'] === 'udp') { - $executor = $this->createUdpExecutor($nameserver, $loop); - } else { - $executor = new SelectiveTransportExecutor( - $this->createUdpExecutor($nameserver, $loop), - $this->createTcpExecutor($nameserver, $loop) - ); - } - - return $executor; - } - - /** - * @param string $nameserver - * @param LoopInterface $loop - * @return TimeoutExecutor - * @throws \InvalidArgumentException for invalid DNS server address - */ - private function createTcpExecutor($nameserver, LoopInterface $loop) - { - return new TimeoutExecutor( - new TcpTransportExecutor($nameserver, $loop), - 5.0, - $loop - ); - } - - /** - * @param string $nameserver - * @param LoopInterface $loop - * @return TimeoutExecutor - * @throws \InvalidArgumentException for invalid DNS server address - */ - private function createUdpExecutor($nameserver, LoopInterface $loop) - { - return new TimeoutExecutor( - new UdpTransportExecutor( - $nameserver, - $loop - ), - 5.0, - $loop - ); - } -} diff --git a/vendor/react/dns/src/Resolver/Resolver.php b/vendor/react/dns/src/Resolver/Resolver.php deleted file mode 100644 index 92926f3..0000000 --- a/vendor/react/dns/src/Resolver/Resolver.php +++ /dev/null @@ -1,147 +0,0 @@ -executor = $executor; - } - - public function resolve($domain) - { - return $this->resolveAll($domain, Message::TYPE_A)->then(function (array $ips) { - return $ips[array_rand($ips)]; - }); - } - - public function resolveAll($domain, $type) - { - $query = new Query($domain, $type, Message::CLASS_IN); - $that = $this; - - return $this->executor->query( - $query - )->then(function (Message $response) use ($query, $that) { - return $that->extractValues($query, $response); - }); - } - - /** - * [Internal] extract all resource record values from response for this query - * - * @param Query $query - * @param Message $response - * @return array - * @throws RecordNotFoundException when response indicates an error or contains no data - * @internal - */ - public function extractValues(Query $query, Message $response) - { - // reject if response code indicates this is an error response message - $code = $response->rcode; - if ($code !== Message::RCODE_OK) { - switch ($code) { - case Message::RCODE_FORMAT_ERROR: - $message = 'Format Error'; - break; - case Message::RCODE_SERVER_FAILURE: - $message = 'Server Failure'; - break; - case Message::RCODE_NAME_ERROR: - $message = 'Non-Existent Domain / NXDOMAIN'; - break; - case Message::RCODE_NOT_IMPLEMENTED: - $message = 'Not Implemented'; - break; - case Message::RCODE_REFUSED: - $message = 'Refused'; - break; - default: - $message = 'Unknown error response code ' . $code; - } - throw new RecordNotFoundException( - 'DNS query for ' . $query->describe() . ' returned an error response (' . $message . ')', - $code - ); - } - - $answers = $response->answers; - $addresses = $this->valuesByNameAndType($answers, $query->name, $query->type); - - // reject if we did not receive a valid answer (domain is valid, but no record for this type could be found) - if (0 === count($addresses)) { - throw new RecordNotFoundException( - 'DNS query for ' . $query->describe() . ' did not return a valid answer (NOERROR / NODATA)' - ); - } - - return array_values($addresses); - } - - /** - * @param \React\Dns\Model\Record[] $answers - * @param string $name - * @param int $type - * @return array - */ - private function valuesByNameAndType(array $answers, $name, $type) - { - // return all record values for this name and type (if any) - $named = $this->filterByName($answers, $name); - $records = $this->filterByType($named, $type); - if ($records) { - return $this->mapRecordData($records); - } - - // no matching records found? check if there are any matching CNAMEs instead - $cnameRecords = $this->filterByType($named, Message::TYPE_CNAME); - if ($cnameRecords) { - $cnames = $this->mapRecordData($cnameRecords); - foreach ($cnames as $cname) { - $records = array_merge( - $records, - $this->valuesByNameAndType($answers, $cname, $type) - ); - } - } - - return $records; - } - - private function filterByName(array $answers, $name) - { - return $this->filterByField($answers, 'name', $name); - } - - private function filterByType(array $answers, $type) - { - return $this->filterByField($answers, 'type', $type); - } - - private function filterByField(array $answers, $field, $value) - { - $value = strtolower($value); - return array_filter($answers, function ($answer) use ($field, $value) { - return $value === strtolower($answer->$field); - }); - } - - private function mapRecordData(array $records) - { - return array_map(function ($record) { - return $record->data; - }, $records); - } -} diff --git a/vendor/react/dns/src/Resolver/ResolverInterface.php b/vendor/react/dns/src/Resolver/ResolverInterface.php deleted file mode 100644 index 555a1cb..0000000 --- a/vendor/react/dns/src/Resolver/ResolverInterface.php +++ /dev/null @@ -1,94 +0,0 @@ -resolve('reactphp.org')->then(function ($ip) { - * echo 'IP for reactphp.org is ' . $ip . PHP_EOL; - * }); - * ``` - * - * This is one of the main methods in this package. It sends a DNS query - * for the given $domain name to your DNS server and returns a single IP - * address on success. - * - * If the DNS server sends a DNS response message that contains more than - * one IP address for this query, it will randomly pick one of the IP - * addresses from the response. If you want the full list of IP addresses - * or want to send a different type of query, you should use the - * [`resolveAll()`](#resolveall) method instead. - * - * If the DNS server sends a DNS response message that indicates an error - * code, this method will reject with a `RecordNotFoundException`. Its - * message and code can be used to check for the response code. - * - * If the DNS communication fails and the server does not respond with a - * valid response message, this message will reject with an `Exception`. - * - * Pending DNS queries can be cancelled by cancelling its pending promise like so: - * - * ```php - * $promise = $resolver->resolve('reactphp.org'); - * - * $promise->cancel(); - * ``` - * - * @param string $domain - * @return \React\Promise\PromiseInterface - * resolves with a single IP address on success or rejects with an Exception on error. - */ - public function resolve($domain); - - /** - * Resolves all record values for the given $domain name and query $type. - * - * ```php - * $resolver->resolveAll('reactphp.org', Message::TYPE_A)->then(function ($ips) { - * echo 'IPv4 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; - * }); - * - * $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA)->then(function ($ips) { - * echo 'IPv6 addresses for reactphp.org ' . implode(', ', $ips) . PHP_EOL; - * }); - * ``` - * - * This is one of the main methods in this package. It sends a DNS query - * for the given $domain name to your DNS server and returns a list with all - * record values on success. - * - * If the DNS server sends a DNS response message that contains one or more - * records for this query, it will return a list with all record values - * from the response. You can use the `Message::TYPE_*` constants to control - * which type of query will be sent. Note that this method always returns a - * list of record values, but each record value type depends on the query - * type. For example, it returns the IPv4 addresses for type `A` queries, - * the IPv6 addresses for type `AAAA` queries, the hostname for type `NS`, - * `CNAME` and `PTR` queries and structured data for other queries. See also - * the `Record` documentation for more details. - * - * If the DNS server sends a DNS response message that indicates an error - * code, this method will reject with a `RecordNotFoundException`. Its - * message and code can be used to check for the response code. - * - * If the DNS communication fails and the server does not respond with a - * valid response message, this message will reject with an `Exception`. - * - * Pending DNS queries can be cancelled by cancelling its pending promise like so: - * - * ```php - * $promise = $resolver->resolveAll('reactphp.org', Message::TYPE_AAAA); - * - * $promise->cancel(); - * ``` - * - * @param string $domain - * @return \React\Promise\PromiseInterface - * Resolves with all record values on success or rejects with an Exception on error. - */ - public function resolveAll($domain, $type); -} diff --git a/vendor/react/http/CHANGELOG.md b/vendor/react/http/CHANGELOG.md deleted file mode 100644 index 07a4cc6..0000000 --- a/vendor/react/http/CHANGELOG.md +++ /dev/null @@ -1,920 +0,0 @@ -# Changelog - -## 1.11.0 (2024-11-20) - -* Feature: Improve PHP 8.4+ support by avoiding implicitly nullable types. - (#537 by @clue) - -* Feature: Allow underscore character in Uri host. - (#524 by @lulhum) - -* Improve test suite to fix expected error code when ext-sockets is not enabled. - (#539 by @WyriHaximus) - -## 1.10.0 (2024-03-27) - -* Feature: Add new PSR-7 implementation and remove dated RingCentral PSR-7 dependency. - (#518, #519, #520 and #522 by @clue) - - This changeset allows us to maintain our own PSR-7 implementation and reduce - dependencies on external projects. It also improves performance slightly and - does not otherwise affect our public API. If you want to explicitly install - the old RingCentral PSR-7 dependency, you can still install it like this: - - ```bash - composer require ringcentral/psr7 - ``` - -* Feature: Add new `Uri` class for new PSR-7 implementation. - (#521 by @clue) - -* Feature: Validate outgoing HTTP message headers and reject invalid messages. - (#523 by @clue) - -* Feature: Full PHP 8.3 compatibility. - (#508 by @clue) - -* Fix: Fix HTTP client to omit `Transfer-Encoding: chunked` when streaming empty request body. - (#516 by @clue) - -* Fix: Ensure connection close handler is cleaned up for each request. - (#515 by @WyriHaximus) - -* Update test suite and avoid unhandled promise rejections. - (#501 and #502 by @clue) - -## 1.9.0 (2023-04-26) - -This is a **SECURITY** and feature release for the 1.x series of ReactPHP's HTTP component. - -* Security fix: This release fixes a medium severity security issue in ReactPHP's HTTP server component - that affects all versions between `v0.8.0` and `v1.8.0`. All users are encouraged to upgrade immediately. - (CVE-2023-26044 reported and fixed by @WyriHaximus) - -* Feature: Support HTTP keep-alive for HTTP client (reusing persistent connections). - (#481, #484, #486 and #495 by @clue) - - This feature offers significant performance improvements when sending many - requests to the same host as it avoids recreating the underlying TCP/IP - connection and repeating the TLS handshake for secure HTTPS requests. - - ```php - $browser = new React\Http\Browser(); - - // Up to 300% faster! HTTP keep-alive is enabled by default - $response = React\Async\await($browser->get('https://httpbingo.org/redirect/6')); - assert($response instanceof Psr\Http\Message\ResponseInterface); - ``` - -* Feature: Add `Request` class to represent outgoing HTTP request message. - (#480 by @clue) - -* Feature: Preserve request method and body for `307 Temporary Redirect` and `308 Permanent Redirect`. - (#442 by @dinooo13) - -* Feature: Include buffer logic to avoid dependency on reactphp/promise-stream. - (#482 by @clue) - -* Improve test suite and project setup and report failed assertions. - (#478 by @clue, #487 and #491 by @WyriHaximus and #475 and #479 by @SimonFrings) - -## 1.8.0 (2022-09-29) - -* Feature: Support for default request headers. - (#461 by @51imyy) - - ```php - $browser = new React\Http\Browser(); - $browser = $browser->withHeader('User-Agent', 'ACME'); - - $browser->get($url)->then(…); - ``` - -* Feature: Forward compatibility with upcoming Promise v3. - (#460 by @clue) - -## 1.7.0 (2022-08-23) - -This is a **SECURITY** and feature release for the 1.x series of ReactPHP's HTTP component. - -* Security fix: This release fixes a medium severity security issue in ReactPHP's HTTP server component - that affects all versions between `v0.7.0` and `v1.6.0`. All users are encouraged to upgrade immediately. - Special thanks to Marco Squarcina (TU Wien) for reporting this and working with us to coordinate this release. - (CVE-2022-36032 reported by @lavish and fixed by @clue) - -* Feature: Improve HTTP server performance by ~20%, reuse syscall values for clock time and socket addresses. - (#457 and #467 by @clue) - -* Feature: Full PHP 8.2+ compatibility, refactor internal `Transaction` to avoid assigning dynamic properties. - (#459 by @clue and #466 by @WyriHaximus) - -* Feature / Fix: Allow explicit `Content-Length` response header on `HEAD` requests. - (#444 by @mrsimonbennett) - -* Minor documentation improvements. - (#452 by @clue, #458 by @nhedger, #448 by @jorrit and #446 by @SimonFrings) - -* Improve test suite, update to use new reactphp/async package instead of clue/reactphp-block, - skip memory tests when lowering memory limit fails and fix legacy HHVM build. - (#464 and #440 by @clue and #450 by @SimonFrings) - -## 1.6.0 (2022-02-03) - -* Feature: Add factory methods for common HTML/JSON/plaintext/XML response types. - (#439 by @clue) - - ```php - $response = React\Http\Response\html("

Hello wörld!

\n"); - $response = React\Http\Response\json(['message' => 'Hello wörld!']); - $response = React\Http\Response\plaintext("Hello wörld!\n"); - $response = React\Http\Response\xml("Hello wörld!\n"); - ``` - -* Feature: Expose all status code constants via `Response` class. - (#432 by @clue) - - ```php - $response = new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, // 200 OK - … - ); - $response = new React\Http\Message\Response( - React\Http\Message\Response::STATUS_NOT_FOUND, // 404 Not Found - … - ); - ``` - -* Feature: Full support for PHP 8.1 release. - (#433 by @SimonFrings and #434 by @clue) - -* Feature / Fix: Improve protocol handling for HTTP responses with no body. - (#429 and #430 by @clue) - -* Internal refactoring and internal improvements for handling requests and responses. - (#422 by @WyriHaximus and #431 by @clue) - -* Improve documentation, update proxy examples, include error reporting in examples. - (#420, #424, #426, and #427 by @clue) - -* Update test suite to use default loop. - (#438 by @clue) - -## 1.5.0 (2021-08-04) - -* Feature: Update `Browser` signature to take optional `$connector` as first argument and - to match new Socket API without nullable loop arguments. - (#418 and #419 by @clue) - - ```php - // unchanged - $browser = new React\Http\Browser(); - - // deprecated - $browser = new React\Http\Browser(null, $connector); - $browser = new React\Http\Browser($loop, $connector); - - // new - $browser = new React\Http\Browser($connector); - $browser = new React\Http\Browser($connector, $loop); - ``` - -* Feature: Rename `Server` to `HttpServer` to avoid class name collisions and - to avoid any ambiguities with regards to the new `SocketServer` API. - (#417 and #419 by @clue) - - ```php - // deprecated - $server = new React\Http\Server($handler); - $server->listen(new React\Socket\Server(8080)); - - // new - $http = new React\Http\HttpServer($handler); - $http->listen(new React\Socket\SocketServer('127.0.0.1:8080')); - ``` - -## 1.4.0 (2021-07-11) - -A major new feature release, see [**release announcement**](https://clue.engineering/2021/announcing-reactphp-default-loop). - -* Feature: Simplify usage by supporting new [default loop](https://reactphp.org/event-loop/#loop). - (#410 by @clue) - - ```php - // old (still supported) - $browser = new React\Http\Browser($loop); - $server = new React\Http\Server($loop, $handler); - - // new (using default loop) - $browser = new React\Http\Browser(); - $server = new React\Http\Server($handler); - ``` - -## 1.3.0 (2021-04-11) - -* Feature: Support persistent connections (`Connection: keep-alive`). - (#405 by @clue) - - This shows a noticeable performance improvement especially when benchmarking - using persistent connections (which is the default pretty much everywhere). - Together with other changes in this release, this improves benchmarking - performance by around 100%. - -* Feature: Require `Host` request header for HTTP/1.1 requests. - (#404 by @clue) - -* Minor documentation improvements. - (#398 by @fritz-gerneth and #399 and #400 by @pavog) - -* Improve test suite, use GitHub actions for continuous integration (CI). - (#402 by @SimonFrings) - -## 1.2.0 (2020-12-04) - -* Feature: Keep request body in memory also after consuming request body. - (#395 by @clue) - - This means consumers can now always access the complete request body as - detailed in the documentation. This allows building custom parsers and more - advanced processing models without having to mess with the default parsers. - -## 1.1.0 (2020-09-11) - -* Feature: Support upcoming PHP 8 release, update to reactphp/socket v1.6 and adjust type checks for invalid chunk headers. - (#391 by @clue) - -* Feature: Consistently resolve base URL according to HTTP specs. - (#379 by @clue) - -* Feature / Fix: Expose `Transfer-Encoding: chunked` response header and fix chunked responses for `HEAD` requests. - (#381 by @clue) - -* Internal refactoring to remove unneeded `MessageFactory` and `Response` classes. - (#380 and #389 by @clue) - -* Minor documentation improvements and improve test suite, update to support PHPUnit 9.3. - (#385 by @clue and #393 by @SimonFrings) - -## 1.0.0 (2020-07-11) - -A major new feature release, see [**release announcement**](https://clue.engineering/2020/announcing-reactphp-http). - -* First stable LTS release, now following [SemVer](https://semver.org/). - We'd like to emphasize that this component is production ready and battle-tested. - We plan to support all long-term support (LTS) releases for at least 24 months, - so you have a rock-solid foundation to build on top of. - -This update involves some major new features and a number of BC breaks due to -some necessary API cleanup. We've tried hard to avoid BC breaks where possible -and minimize impact otherwise. We expect that most consumers of this package -will be affected by BC breaks, but updating should take no longer than a few -minutes. See below for more details: - -* Feature: Add async HTTP client implementation. - (#368 by @clue) - - ```php - $browser = new React\Http\Browser($loop); - $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { - echo $response->getBody(); - }); - ``` - - The code has been imported as-is from [clue/reactphp-buzz v2.9.0](https://github.com/clue/reactphp-buzz), - with only minor changes to the namespace and we otherwise leave all the existing APIs unchanged. - Upgrading from [clue/reactphp-buzz v2.9.0](https://github.com/clue/reactphp-buzz) - to this release should be a matter of updating some namespace references only: - - ```php - // old - $browser = new Clue\React\Buzz\Browser($loop); - - // new - $browser = new React\Http\Browser($loop); - ``` - -* Feature / BC break: Add `LoopInterface` as required first constructor argument to `Server` and - change `Server` to accept variadic middleware handlers instead of `array`. - (#361 and #362 by @WyriHaximus) - - ```php - // old - $server = new React\Http\Server($handler); - $server = new React\Http\Server([$middleware, $handler]); - - // new - $server = new React\Http\Server($loop, $handler); - $server = new React\Http\Server($loop, $middleware, $handler); - ``` - -* Feature / BC break: Move `Response` class to `React\Http\Message\Response` and - expose `ServerRequest` class to `React\Http\Message\ServerRequest`. - (#370 by @clue) - - ```php - // old - $response = new React\Http\Response(200, [], 'Hello!'); - - // new - $response = new React\Http\Message\Response(200, [], 'Hello!'); - ``` - -* Feature / BC break: Add `StreamingRequestMiddleware` to stream incoming requests, mark `StreamingServer` as internal. - (#367 by @clue) - - ```php - // old: advanced StreamingServer is now internal only - $server = new React\Http\StreamingServer($handler); - - // new: use StreamingRequestMiddleware instead of StreamingServer - $server = new React\Http\Server( - $loop, - new React\Http\Middleware\StreamingRequestMiddleware(), - $handler - ); - ``` - -* Feature / BC break: Improve default concurrency to 1024 requests and cap default request buffer at 64K. - (#371 by @clue) - - This improves default concurrency to 1024 requests and caps the default request buffer at 64K. - The previous defaults resulted in just 4 concurrent requests with a request buffer of 8M. - See [`Server`](README.md#server) for details on how to override these defaults. - -* Feature: Expose ReactPHP in `User-Agent` client-side request header and in `Server` server-side response header. - (#374 by @clue) - -* Mark all classes as `final` to discourage inheriting from it. - (#373 by @WyriHaximus) - -* Improve documentation and use fully-qualified class names throughout the documentation and - add ReactPHP core team as authors to `composer.json` and license file. - (#366 and #369 by @WyriHaximus and #375 by @clue) - -* Improve test suite and support skipping all online tests with `--exclude-group internet`. - (#372 by @clue) - -## 0.8.7 (2020-07-05) - -* Fix: Fix parsing multipart request body with quoted header parameters (dot net). - (#363 by @ebimmel) - -* Fix: Fix calculating concurrency when `post_max_size` ini is unlimited. - (#365 by @clue) - -* Improve test suite to run tests on PHPUnit 9 and clean up test suite. - (#364 by @SimonFrings) - -## 0.8.6 (2020-01-12) - -* Fix: Fix parsing `Cookie` request header with comma in its values. - (#352 by @fiskie) - -* Fix: Avoid unneeded warning when decoding invalid data on PHP 7.4. - (#357 by @WyriHaximus) - -* Add .gitattributes to exclude dev files from exports. - (#353 by @reedy) - -## 0.8.5 (2019-10-29) - -* Internal refactorings and optimizations to improve request parsing performance. - Benchmarks suggest number of requests/s improved by ~30% for common `GET` requests. - (#345, #346, #349 and #350 by @clue) - -* Add documentation and example for JSON/XML request body and - improve documentation for concurrency and streaming requests and for error handling. - (#341 and #342 by @clue) - -## 0.8.4 (2019-01-16) - -* Improvement: Internal refactoring to simplify response header logic. - (#321 by @clue) - -* Improvement: Assign Content-Length response header automatically only when size is known. - (#329 by @clue) - -* Improvement: Import global functions for better performance. - (#330 by @WyriHaximus) - -## 0.8.3 (2018-04-11) - -* Feature: Do not pause connection stream to detect closed connections immediately. - (#315 by @clue) - -* Feature: Keep incoming `Transfer-Encoding: chunked` request header. - (#316 by @clue) - -* Feature: Reject invalid requests that contain both `Content-Length` and `Transfer-Encoding` request headers. - (#318 by @clue) - -* Minor internal refactoring to simplify connection close logic after sending response. - (#317 by @clue) - -## 0.8.2 (2018-04-06) - -* Fix: Do not pass `$next` handler to final request handler. - (#308 by @clue) - -* Fix: Fix awaiting queued handlers when cancelling a queued handler. - (#313 by @clue) - -* Fix: Fix Server to skip `SERVER_ADDR` params for Unix domain sockets (UDS). - (#307 by @clue) - -* Documentation for PSR-15 middleware and minor documentation improvements. - (#314 by @clue and #297, #298 and #310 by @seregazhuk) - -* Minor code improvements and micro optimizations. - (#301 by @seregazhuk and #305 by @kalessil) - -## 0.8.1 (2018-01-05) - -* Major request handler performance improvement. Benchmarks suggest number of - requests/s improved by more than 50% for common `GET` requests! - We now avoid queuing, buffering and wrapping incoming requests in promises - when we're below limits and instead can directly process common requests. - (#291, #292, #293, #294 and #296 by @clue) - -* Fix: Fix concurrent invoking next middleware request handlers - (#293 by @clue) - -* Small code improvements - (#286 by @seregazhuk) - -* Improve test suite to be less fragile when using `ext-event` and - fix test suite forward compatibility with upcoming EventLoop releases - (#288 and #290 by @clue) - -## 0.8.0 (2017-12-12) - -* Feature / BC break: Add new `Server` facade that buffers and parses incoming - HTTP requests. This provides full PSR-7 compatibility, including support for - form submissions with POST fields and file uploads. - The old `Server` has been renamed to `StreamingServer` for advanced usage - and is used internally. - (#266, #271, #281, #282, #283 and #284 by @WyriHaximus and @clue) - - ```php - // old: handle incomplete/streaming requests - $server = new Server($handler); - - // new: handle complete, buffered and parsed requests - // new: full PSR-7 support, including POST fields and file uploads - $server = new Server($handler); - - // new: handle incomplete/streaming requests - $server = new StreamingServer($handler); - ``` - - > While this is technically a small BC break, this should in fact not break - most consuming code. If you rely on the old request streaming, you can - explicitly use the advanced `StreamingServer` to restore old behavior. - -* Feature: Add support for middleware request handler arrays - (#215, #228, #229, #236, #237, #238, #246, #247, #277, #279 and #285 by @WyriHaximus, @clue and @jsor) - - ```php - // new: middleware request handler arrays - $server = new Server(array( - function (ServerRequestInterface $request, callable $next) { - $request = $request->withHeader('Processed', time()); - return $next($request); - }, - function (ServerRequestInterface $request) { - return new Response(); - } - )); - ``` - -* Feature: Add support for limiting how many next request handlers can be - executed concurrently (`LimitConcurrentRequestsMiddleware`) - (#272 by @clue and @WyriHaximus) - - ```php - // new: explicitly limit concurrency - $server = new Server(array( - new LimitConcurrentRequestsMiddleware(10), - $handler - )); - ``` - -* Feature: Add support for buffering the incoming request body - (`RequestBodyBufferMiddleware`). - This feature mimics PHP's default behavior and respects its `post_max_size` - ini setting by default and allows explicit configuration. - (#216, #224, #263, #276 and #278 by @WyriHaximus and #235 by @andig) - - ```php - // new: buffer up to 10 requests with 8 MiB each - $server = new StreamingServer(array( - new LimitConcurrentRequestsMiddleware(10), - new RequestBodyBufferMiddleware('8M'), - $handler - )); - ``` - -* Feature: Add support for parsing form submissions with POST fields and file - uploads (`RequestBodyParserMiddleware`). - This feature mimics PHP's default behavior and respects its ini settings and - `MAX_FILE_SIZE` POST fields by default and allows explicit configuration. - (#220, #226, #252, #261, #264, #265, #267, #268, #274 by @WyriHaximus and @clue) - - ```php - // new: buffer up to 10 requests with 8 MiB each - // and limit to 4 uploads with 2 MiB each - $server = new StreamingServer(array( - new LimitConcurrentRequestsMiddleware(10), - new RequestBodyBufferMiddleware('8M'), - new RequestBodyParserMiddleware('2M', 4) - $handler - )); - ``` - -* Feature: Update Socket to work around sending secure HTTPS responses with PHP < 7.1.4 - (#244 by @clue) - -* Feature: Support sending same response header multiple times (e.g. `Set-Cookie`) - (#248 by @clue) - -* Feature: Raise maximum request header size to 8k to match common implementations - (#253 by @clue) - -* Improve test suite by adding forward compatibility with PHPUnit 6, test - against PHP 7.1 and PHP 7.2 and refactor and remove risky and duplicate tests. - (#243, #269 and #270 by @carusogabriel and #249 by @clue) - -* Minor code refactoring to move internal classes to `React\Http\Io` namespace - and clean up minor code and documentation issues - (#251 by @clue, #227 by @kalessil, #240 by @christoph-kluge, #230 by @jsor and #280 by @andig) - -## 0.7.4 (2017-08-16) - -* Improvement: Target evenement 3.0 a long side 2.0 and 1.0 - (#212 by @WyriHaximus) - -## 0.7.3 (2017-08-14) - -* Feature: Support `Throwable` when setting previous exception from server callback - (#155 by @jsor) - -* Fix: Fixed URI parsing for origin-form requests that contain scheme separator - such as `/path?param=http://example.com`. - (#209 by @aaronbonneau) - -* Improve test suite by locking Travis distro so new defaults will not break the build - (#211 by @clue) - -## 0.7.2 (2017-07-04) - -* Fix: Stricter check for invalid request-line in HTTP requests - (#206 by @clue) - -* Refactor to use HTTP response reason phrases from response object - (#205 by @clue) - -## 0.7.1 (2017-06-17) - -* Fix: Fix parsing CONNECT request without `Host` header - (#201 by @clue) - -* Internal preparation for future PSR-7 `UploadedFileInterface` - (#199 by @WyriHaximus) - -## 0.7.0 (2017-05-29) - -* Feature / BC break: Use PSR-7 (http-message) standard and - `Request-In-Response-Out`-style request handler callback. - Pass standard PSR-7 `ServerRequestInterface` and expect any standard - PSR-7 `ResponseInterface` in return for the request handler callback. - (#146 and #152 and #170 by @legionth) - - ```php - // old - $app = function (Request $request, Response $response) { - $response->writeHead(200, array('Content-Type' => 'text/plain')); - $response->end("Hello world!\n"); - }; - - // new - $app = function (ServerRequestInterface $request) { - return new Response( - 200, - array('Content-Type' => 'text/plain'), - "Hello world!\n" - ); - }; - ``` - - A `Content-Length` header will automatically be included if the size can be - determined from the response body. - (#164 by @maciejmrozinski) - - The request handler callback will automatically make sure that responses to - HEAD requests and certain status codes, such as `204` (No Content), never - contain a response body. - (#156 by @clue) - - The intermediary `100 Continue` response will automatically be sent if - demanded by a HTTP/1.1 client. - (#144 by @legionth) - - The request handler callback can now return a standard `Promise` if - processing the request needs some time, such as when querying a database. - Similarly, the request handler may return a streaming response if the - response body comes from a `ReadableStreamInterface` or its size is - unknown in advance. - - ```php - // old - $app = function (Request $request, Response $response) use ($db) { - $db->query()->then(function ($result) use ($response) { - $response->writeHead(200, array('Content-Type' => 'text/plain')); - $response->end($result); - }); - }; - - // new - $app = function (ServerRequestInterface $request) use ($db) { - return $db->query()->then(function ($result) { - return new Response( - 200, - array('Content-Type' => 'text/plain'), - $result - ); - }); - }; - ``` - - Pending promies and response streams will automatically be canceled once the - client connection closes. - (#187 and #188 by @clue) - - The `ServerRequestInterface` contains the full effective request URI, - server-side parameters, query parameters and parsed cookies values as - defined in PSR-7. - (#167 by @clue and #174, #175 and #180 by @legionth) - - ```php - $app = function (ServerRequestInterface $request) { - return new Response( - 200, - array('Content-Type' => 'text/plain'), - $request->getUri()->getScheme() - ); - }; - ``` - - Advanced: Support duplex stream response for `Upgrade` requests such as - `Upgrade: WebSocket` or custom protocols and `CONNECT` requests - (#189 and #190 by @clue) - - > Note that the request body will currently not be buffered and parsed by - default, which depending on your particilar use-case, may limit - interoperability with the PSR-7 (http-message) ecosystem. - The provided streaming request body interfaces allow you to perform - buffering and parsing as needed in the request handler callback. - See also the README and examples for more details. - -* Feature / BC break: Replace `request` listener with callback function and - use `listen()` method to support multiple listening sockets - (#97 by @legionth and #193 by @clue) - - ```php - // old - $server = new Server($socket); - $server->on('request', $app); - - // new - $server = new Server($app); - $server->listen($socket); - ``` - -* Feature: Support the more advanced HTTP requests, such as - `OPTIONS * HTTP/1.1` (`OPTIONS` method in asterisk-form), - `GET http://example.com/path HTTP/1.1` (plain proxy requests in absolute-form), - `CONNECT example.com:443 HTTP/1.1` (`CONNECT` proxy requests in authority-form) - and sanitize `Host` header value across all requests. - (#157, #158, #161, #165, #169 and #173 by @clue) - -* Feature: Forward compatibility with Socket v1.0, v0.8, v0.7 and v0.6 and - forward compatibility with Stream v1.0 and v0.7 - (#154, #163, #183, #184 and #191 by @clue) - -* Feature: Simplify examples to ease getting started and - add benchmarking example - (#151 and #162 by @clue) - -* Improve test suite by adding tests for case insensitive chunked transfer - encoding and ignoring HHVM test failures until Travis tests work again. - (#150 by @legionth and #185 by @clue) - -## 0.6.0 (2017-03-09) - -* Feature / BC break: The `Request` and `Response` objects now follow strict - stream semantics and their respective methods and events. - (#116, #129, #133, #135, #136, #137, #138, #140, #141 by @legionth - and #122, #123, #130, #131, #132, #142 by @clue) - - This implies that the `Server` now supports proper detection of the request - message body stream, such as supporting decoding chunked transfer encoding, - delimiting requests with an explicit `Content-Length` header - and those with an empty request message body. - - These streaming semantics are compatible with previous Stream v0.5, future - compatible with v0.5 and upcoming v0.6 versions and can be used like this: - - ```php - $http->on('request', function (Request $request, Response $response) { - $contentLength = 0; - $request->on('data', function ($data) use (&$contentLength) { - $contentLength += strlen($data); - }); - - $request->on('end', function () use ($response, &$contentLength){ - $response->writeHead(200, array('Content-Type' => 'text/plain')); - $response->end("The length of the submitted request body is: " . $contentLength); - }); - - // an error occured - // e.g. on invalid chunked encoded data or an unexpected 'end' event - $request->on('error', function (\Exception $exception) use ($response, &$contentLength) { - $response->writeHead(400, array('Content-Type' => 'text/plain')); - $response->end("An error occured while reading at length: " . $contentLength); - }); - }); - ``` - - Similarly, the `Request` and `Response` now strictly follow the - `close()` method and `close` event semantics. - Closing the `Request` does not interrupt the underlying TCP/IP in - order to allow still sending back a valid response message. - Closing the `Response` does terminate the underlying TCP/IP - connection in order to clean up resources. - - You should make sure to always attach a `request` event listener - like above. The `Server` will not respond to an incoming HTTP - request otherwise and keep the TCP/IP connection pending until the - other side chooses to close the connection. - -* Feature: Support `HTTP/1.1` and `HTTP/1.0` for `Request` and `Response`. - (#124, #125, #126, #127, #128 by @clue and #139 by @legionth) - - The outgoing `Response` will automatically use the same HTTP version as the - incoming `Request` message and will only apply `HTTP/1.1` semantics if - applicable. This includes that the `Response` will automatically attach a - `Date` and `Connection: close` header if applicable. - - This implies that the `Server` now automatically responds with HTTP error - messages for invalid requests (status 400) and those exceeding internal - request header limits (status 431). - -## 0.5.0 (2017-02-16) - -* Feature / BC break: Change `Request` methods to be in line with PSR-7 - (#117 by @clue) - * Rename `getQuery()` to `getQueryParams()` - * Rename `getHttpVersion()` to `getProtocolVersion()` - * Change `getHeaders()` to always return an array of string values - for each header - -* Feature / BC break: Update Socket component to v0.5 and - add secure HTTPS server support - (#90 and #119 by @clue) - - ```php - // old plaintext HTTP server - $socket = new React\Socket\Server($loop); - $socket->listen(8080, '127.0.0.1'); - $http = new React\Http\Server($socket); - - // new plaintext HTTP server - $socket = new React\Socket\Server('127.0.0.1:8080', $loop); - $http = new React\Http\Server($socket); - - // new secure HTTPS server - $socket = new React\Socket\Server('127.0.0.1:8080', $loop); - $socket = new React\Socket\SecureServer($socket, $loop, array( - 'local_cert' => __DIR__ . '/localhost.pem' - )); - $http = new React\Http\Server($socket); - ``` - -* BC break: Mark internal APIs as internal or private and - remove unneeded `ServerInterface` - (#118 by @clue, #95 by @legionth) - -## 0.4.4 (2017-02-13) - -* Feature: Add request header accessors (à la PSR-7) - (#103 by @clue) - - ```php - // get value of host header - $host = $request->getHeaderLine('Host'); - - // get list of all cookie headers - $cookies = $request->getHeader('Cookie'); - ``` - -* Feature: Forward `pause()` and `resume()` from `Request` to underlying connection - (#110 by @clue) - - ```php - // support back-pressure when piping request into slower destination - $request->pipe($dest); - - // manually pause/resume request - $request->pause(); - $request->resume(); - ``` - -* Fix: Fix `100-continue` to be handled case-insensitive and ignore it for HTTP/1.0. - Similarly, outgoing response headers are now handled case-insensitive, e.g - we no longer apply chunked transfer encoding with mixed-case `Content-Length`. - (#107 by @clue) - - ```php - // now handled case-insensitive - $request->expectsContinue(); - - // now works just like properly-cased header - $response->writeHead($status, array('content-length' => 0)); - ``` - -* Fix: Do not emit empty `data` events and ignore empty writes in order to - not mess up chunked transfer encoding - (#108 and #112 by @clue) - -* Lock and test minimum required dependency versions and support PHPUnit v5 - (#113, #115 and #114 by @andig) - -## 0.4.3 (2017-02-10) - -* Fix: Do not take start of body into account when checking maximum header size - (#88 by @nopolabs) - -* Fix: Remove `data` listener if `HeaderParser` emits an error - (#83 by @nick4fake) - -* First class support for PHP 5.3 through PHP 7 and HHVM - (#101 and #102 by @clue, #66 by @WyriHaximus) - -* Improve test suite by adding PHPUnit to require-dev, - improving forward compatibility with newer PHPUnit versions - and replacing unneeded test stubs - (#92 and #93 by @nopolabs, #100 by @legionth) - -## 0.4.2 (2016-11-09) - -* Remove all listeners after emitting error in RequestHeaderParser #68 @WyriHaximus -* Catch Guzzle parse request errors #65 @WyriHaximus -* Remove branch-alias definition as per reactphp/react#343 #58 @WyriHaximus -* Add functional example to ease getting started #64 by @clue -* Naming, immutable array manipulation #37 @cboden - -## 0.4.1 (2015-05-21) - -* Replaced guzzle/parser with guzzlehttp/psr7 by @cboden -* FIX Continue Header by @iannsp -* Missing type hint by @marenzo - -## 0.4.0 (2014-02-02) - -* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks -* BC break: Update to React/Promise 2.0 -* BC break: Update to Evenement 2.0 -* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0 -* Bump React dependencies to v0.4 - -## 0.3.0 (2013-04-14) - -* Bump React dependencies to v0.3 - -## 0.2.6 (2012-12-26) - -* Bug fix: Emit end event when Response closes (@beaucollins) - -## 0.2.3 (2012-11-14) - -* Bug fix: Forward drain events from HTTP response (@cs278) -* Dependency: Updated guzzle deps to `3.0.*` - -## 0.2.2 (2012-10-28) - -* Version bump - -## 0.2.1 (2012-10-14) - -* Feature: Support HTTP 1.1 continue - -## 0.2.0 (2012-09-10) - -* Bump React dependencies to v0.2 - -## 0.1.1 (2012-07-12) - -* Version bump - -## 0.1.0 (2012-07-11) - -* First tagged release diff --git a/vendor/react/http/LICENSE b/vendor/react/http/LICENSE deleted file mode 100644 index d6f8901..0000000 --- a/vendor/react/http/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -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/README.md b/vendor/react/http/README.md deleted file mode 100644 index fd9ba46..0000000 --- a/vendor/react/http/README.md +++ /dev/null @@ -1,3024 +0,0 @@ -# HTTP - -[![CI status](https://github.com/reactphp/http/actions/workflows/ci.yml/badge.svg)](https://github.com/reactphp/http/actions) -[![installs on Packagist](https://img.shields.io/packagist/dt/react/http?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/http) - -Event-driven, streaming HTTP client and server implementation for [ReactPHP](https://reactphp.org/). - -This HTTP library provides re-usable implementations for an HTTP client and -server based on ReactPHP's [`Socket`](https://github.com/reactphp/socket) and -[`EventLoop`](https://github.com/reactphp/event-loop) components. -Its client component allows you to send any number of async HTTP/HTTPS requests -concurrently. -Its server component allows you to build plaintext HTTP and secure HTTPS servers -that accept incoming HTTP requests from HTTP clients (such as web browsers). -This library provides async, streaming means for all of this, so you can handle -multiple concurrent HTTP requests without blocking. - -**Table of contents** - -* [Quickstart example](#quickstart-example) -* [Client Usage](#client-usage) - * [Request methods](#request-methods) - * [Promises](#promises) - * [Cancellation](#cancellation) - * [Timeouts](#timeouts) - * [Authentication](#authentication) - * [Redirects](#redirects) - * [Blocking](#blocking) - * [Concurrency](#concurrency) - * [Streaming response](#streaming-response) - * [Streaming request](#streaming-request) - * [HTTP proxy](#http-proxy) - * [SOCKS proxy](#socks-proxy) - * [SSH proxy](#ssh-proxy) - * [Unix domain sockets](#unix-domain-sockets) -* [Server Usage](#server-usage) - * [HttpServer](#httpserver) - * [listen()](#listen) - * [Server Request](#server-request) - * [Request parameters](#request-parameters) - * [Query parameters](#query-parameters) - * [Request body](#request-body) - * [Streaming incoming request](#streaming-incoming-request) - * [Request method](#request-method) - * [Cookie parameters](#cookie-parameters) - * [Invalid request](#invalid-request) - * [Server Response](#server-response) - * [Deferred response](#deferred-response) - * [Streaming outgoing response](#streaming-outgoing-response) - * [Response length](#response-length) - * [Invalid response](#invalid-response) - * [Default response headers](#default-response-headers) - * [Middleware](#middleware) - * [Custom middleware](#custom-middleware) - * [Third-Party Middleware](#third-party-middleware) -* [API](#api) - * [Browser](#browser) - * [get()](#get) - * [post()](#post) - * [head()](#head) - * [patch()](#patch) - * [put()](#put) - * [delete()](#delete) - * [request()](#request) - * [requestStreaming()](#requeststreaming) - * [withTimeout()](#withtimeout) - * [withFollowRedirects()](#withfollowredirects) - * [withRejectErrorResponse()](#withrejecterrorresponse) - * [withBase()](#withbase) - * [withProtocolVersion()](#withprotocolversion) - * [withResponseBuffer()](#withresponsebuffer) - * [withHeader()](#withheader) - * [withoutHeader()](#withoutheader) - * [React\Http\Message](#reacthttpmessage) - * [Response](#response) - * [html()](#html) - * [json()](#json) - * [plaintext()](#plaintext) - * [xml()](#xml) - * [Request](#request-1) - * [ServerRequest](#serverrequest) - * [Uri](#uri) - * [ResponseException](#responseexception) - * [React\Http\Middleware](#reacthttpmiddleware) - * [StreamingRequestMiddleware](#streamingrequestmiddleware) - * [LimitConcurrentRequestsMiddleware](#limitconcurrentrequestsmiddleware) - * [RequestBodyBufferMiddleware](#requestbodybuffermiddleware) - * [RequestBodyParserMiddleware](#requestbodyparsermiddleware) -* [Install](#install) -* [Tests](#tests) -* [License](#license) - -## Quickstart example - -Once [installed](#install), you can use the following code to access an -HTTP web server and send some simple HTTP GET requests: - -```php -get('http://www.google.com/')->then(function (Psr\Http\Message\ResponseInterface $response) { - var_dump($response->getHeaders(), (string)$response->getBody()); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -This is an HTTP server which responds with `Hello World!` to every request. - -```php -listen($socket); -``` - -See also the [examples](examples/). - -## Client Usage - -### Request methods - -Most importantly, this project provides a [`Browser`](#browser) object that -offers several methods that resemble the HTTP protocol methods: - -```php -$browser->get($url, array $headers = array()); -$browser->head($url, array $headers = array()); -$browser->post($url, array $headers = array(), string|ReadableStreamInterface $body = ''); -$browser->delete($url, array $headers = array(), string|ReadableStreamInterface $body = ''); -$browser->put($url, array $headers = array(), string|ReadableStreamInterface $body = ''); -$browser->patch($url, array $headers = array(), string|ReadableStreamInterface $body = ''); -``` - -Each of these methods requires a `$url` and some optional parameters to send an -HTTP request. Each of these method names matches the respective HTTP request -method, for example the [`get()`](#get) method sends an HTTP `GET` request. - -You can optionally pass an associative array of additional `$headers` that will be -sent with this HTTP request. Additionally, each method will automatically add a -matching `Content-Length` request header if an outgoing request body is given and its -size 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` HTTP request methods). - -If you're using a [streaming request body](#streaming-request), it will default -to using `Transfer-Encoding: chunked` unless you explicitly pass in a matching `Content-Length` -request header. See also [streaming request](#streaming-request) for more details. - -By default, all of the above methods default to sending requests using the -HTTP/1.1 protocol version. If you want to explicitly use the legacy HTTP/1.0 -protocol version, you can use the [`withProtocolVersion()`](#withprotocolversion) -method. If you want to use any other or even custom HTTP request method, you can -use the [`request()`](#request) method. - -Each of the above methods supports async operation and either *fulfills* with a -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) -or *rejects* with an `Exception`. -Please see the following chapter about [promises](#promises) for more details. - -### Promises - -Sending requests is async (non-blocking), so you can actually send multiple -requests in parallel. -The `Browser` will respond to each request with a -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) -message, the order is not guaranteed. -Sending requests uses a [Promise](https://github.com/reactphp/promise)-based -interface that makes it easy to react to when an HTTP request is completed -(i.e. either successfully fulfilled or rejected with an error): - -```php -$browser->get($url)->then( - function (Psr\Http\Message\ResponseInterface $response) { - var_dump('Response received', $response); - }, - function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; - } -); -``` - -If this looks strange to you, you can also use the more traditional [blocking API](#blocking). - -Keep in mind that resolving the Promise with the full response message means the -whole response body has to be kept in memory. -This is easy to get started and works reasonably well for smaller responses -(such as common HTML pages or RESTful or JSON API requests). - -You may also want to look into the [streaming API](#streaming-response): - -* If you're dealing with lots of concurrent requests (100+) or -* If you want to process individual data chunks as they happen (without having to wait for the full response body) or -* If you're expecting a big response body size (1 MiB or more, for example when downloading binary files) or -* If you're unsure about the response body size (better be safe than sorry when accessing arbitrary remote HTTP endpoints and the response body size is unknown in advance). - -### Cancellation - -The returned Promise is implemented in such a way that it can be cancelled -when it is still pending. -Cancelling a pending promise will reject its value with an Exception and -clean up any underlying resources. - -```php -$promise = $browser->get($url); - -Loop::addTimer(2.0, function () use ($promise) { - $promise->cancel(); -}); -``` - -### Timeouts - -This library uses a very efficient HTTP implementation, so most HTTP requests -should usually be completed in mere milliseconds. However, when sending HTTP -requests over an unreliable network (the internet), there are a number of things -that can go wrong and may cause the request to fail after a time. As such, this -library respects PHP's `default_socket_timeout` setting (default 60s) as a timeout -for sending the outgoing HTTP request and waiting for a successful response and -will otherwise cancel the pending request and reject its value with an Exception. - -Note that this timeout value covers creating the underlying transport connection, -sending the HTTP request, receiving the HTTP response headers and its full -response body and following any eventual [redirects](#redirects). See also -[redirects](#redirects) below to configure the number of redirects to follow (or -disable following redirects altogether) and also [streaming](#streaming-response) -below to not take receiving large response bodies into account for this timeout. - -You can use the [`withTimeout()` method](#withtimeout) to pass a custom timeout -value in seconds like this: - -```php -$browser = $browser->withTimeout(10.0); - -$browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { - // response received within 10 seconds maximum - var_dump($response->getHeaders()); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -Similarly, you can use a bool `false` to not apply a timeout at all -or use a bool `true` value to restore the default handling. -See [`withTimeout()`](#withtimeout) for more details. - -If you're using a [streaming response body](#streaming-response), the time it -takes to receive the response body stream will not be included in the timeout. -This allows you to keep this incoming stream open for a longer time, such as -when downloading a very large stream or when streaming data over a long-lived -connection. - -If you're using a [streaming request body](#streaming-request), the time it -takes to send the request body stream will not be included in the timeout. This -allows you to keep this outgoing stream open for a longer time, such as when -uploading a very large stream. - -Note that this timeout handling applies to the higher-level HTTP layer. Lower -layers such as socket and DNS may also apply (different) timeout values. In -particular, the underlying socket connection uses the same `default_socket_timeout` -setting to establish the underlying transport connection. To control this -connection timeout behavior, you can [inject a custom `Connector`](#browser) -like this: - -```php -$browser = new React\Http\Browser( - new React\Socket\Connector( - array( - 'timeout' => 5 - ) - ) -); -``` - -### Authentication - -This library supports [HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) -using the `Authorization: Basic …` request header or allows you to set an explicit -`Authorization` request header. - -By default, this library does not include an outgoing `Authorization` request -header. If the server requires authentication, if may return a `401` (Unauthorized) -status code which will reject the request by default (see also the -[`withRejectErrorResponse()` method](#withrejecterrorresponse) below). - -In order to pass authentication details, you can simply pass the username and -password as part of the request URL like this: - -```php -$promise = $browser->get('https://user:pass@example.com/api'); -``` - -Note that special characters in the authentication details have to be -percent-encoded, see also [`rawurlencode()`](https://www.php.net/manual/en/function.rawurlencode.php). -This example will automatically pass the base64-encoded authentication details -using the outgoing `Authorization: Basic …` request header. If the HTTP endpoint -you're talking to requires any other authentication scheme, you can also pass -this header explicitly. This is common when using (RESTful) HTTP APIs that use -OAuth access tokens or JSON Web Tokens (JWT): - -```php -$token = 'abc123'; - -$promise = $browser->get( - 'https://example.com/api', - array( - 'Authorization' => 'Bearer ' . $token - ) -); -``` - -When following redirects, the `Authorization` request header will never be sent -to any remote hosts by default. When following a redirect where the `Location` -response header contains authentication details, these details will be sent for -following requests. See also [redirects](#redirects) below. - -### Redirects - -By default, this library follows any redirects and obeys `3xx` (Redirection) -status codes using the `Location` response header from the remote server. -The promise will be fulfilled with the last response from the chain of redirects. - -```php -$browser->get($url, $headers)->then(function (Psr\Http\Message\ResponseInterface $response) { - // the final response will end up here - var_dump($response->getHeaders()); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -Any redirected requests will follow the semantics of the original request and -will include the same request headers as the original request except for those -listed below. -If the original request is a temporary (307) or a permanent (308) redirect, request -body and headers will be passed to the redirected request. Otherwise, the request -body will never be passed to the redirected request. Accordingly, each redirected -request will remove any `Content-Length` and `Content-Type` request headers. - -If the original request used HTTP authentication with an `Authorization` request -header, this request header will only be passed as part of the redirected -request if the redirected URL is using the same host. In other words, the -`Authorizaton` request header will not be forwarded to other foreign hosts due to -possible privacy/security concerns. When following a redirect where the `Location` -response header contains authentication details, these details will be sent for -following requests. - -You can use the [`withFollowRedirects()`](#withfollowredirects) method to -control the maximum number of redirects to follow or to return any redirect -responses as-is and apply custom redirection logic like this: - -```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->getHeaders()); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -See also [`withFollowRedirects()`](#withfollowredirects) for more details. - -### Blocking - -As stated above, this library provides you a powerful, async API by default. - -You can also integrate this into your traditional, blocking environment by using -[reactphp/async](https://github.com/reactphp/async). This allows you to simply -await async HTTP requests like this: - -```php -use function React\Async\await; - -$browser = new React\Http\Browser(); - -$promise = $browser->get('http://example.com/'); - -try { - $response = await($promise); - // response successfully received -} catch (Exception $e) { - // an error occurred while performing the request -} -``` - -Similarly, you can also process multiple requests concurrently and await an array of `Response` objects: - -```php -use function React\Async\await; -use function React\Promise\all; - -$promises = array( - $browser->get('http://example.com/'), - $browser->get('http://www.example.org/'), -); - -$responses = await(all($promises)); -``` - -This is made possible thanks to fibers available in PHP 8.1+ and our -compatibility API that also works on all supported PHP versions. -Please refer to [reactphp/async](https://github.com/reactphp/async#readme) for more details. - -Keep in mind the above remark about buffering the whole response message in memory. -As an alternative, you may also see one of the following chapters for the -[streaming API](#streaming-response). - -### Concurrency - -As stated above, this library provides you a powerful, async API. Being able to -send a large number of requests at once is one of the core features of this -project. For instance, you can easily send 100 requests concurrently while -processing SQL queries at the same time. - -Remember, with great power comes great responsibility. Sending an excessive -number of requests may either take up all resources on your side or it may even -get you banned by the remote side if it sees an unreasonable number of requests -from your side. - -```php -// watch out if array contains many elements -foreach ($urls as $url) { - $browser->get($url)->then(function (Psr\Http\Message\ResponseInterface $response) { - var_dump($response->getHeaders()); - }, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; - }); -} -``` - -As a consequence, it's usually recommended to limit concurrency on the sending -side to a reasonable value. It's common to use a rather small limit, as doing -more than a dozen of things at once may easily overwhelm the receiving side. You -can use [clue/reactphp-mq](https://github.com/clue/reactphp-mq) as a lightweight -in-memory queue to concurrently do many (but not too many) things at once: - -```php -// wraps Browser in a Queue object that executes no more than 10 operations at once -$q = new Clue\React\Mq\Queue(10, null, function ($url) use ($browser) { - return $browser->get($url); -}); - -foreach ($urls as $url) { - $q($url)->then(function (Psr\Http\Message\ResponseInterface $response) { - var_dump($response->getHeaders()); - }, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; - }); -} -``` - -Additional requests that exceed the concurrency limit will automatically be -enqueued until one of the pending requests completes. This integrates nicely -with the existing [Promise-based API](#promises). Please refer to -[clue/reactphp-mq](https://github.com/clue/reactphp-mq) for more details. - -This in-memory approach works reasonably well for some thousand outstanding -requests. If you're processing a very large input list (think millions of rows -in a CSV or NDJSON file), you may want to look into using a streaming approach -instead. See [clue/reactphp-flux](https://github.com/clue/reactphp-flux) for -more details. - -### Streaming response - -All of the above examples assume you want to store the whole response body in memory. -This is easy to get started and works reasonably well for smaller responses. - -However, there are several situations where it's usually a better idea to use a -streaming approach, where only small chunks have to be kept in memory: - -* If you're dealing with lots of concurrent requests (100+) or -* If you want to process individual data chunks as they happen (without having to wait for the full response body) or -* If you're expecting a big response body size (1 MiB or more, for example when downloading binary files) or -* If you're unsure about the response body size (better be safe than sorry when accessing arbitrary remote HTTP endpoints and the response body size is unknown in advance). - -You can use the [`requestStreaming()`](#requeststreaming) 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. - -This means it resolves with a normal -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface), -which can be used to access the response message parameters as usual. -You can access the message body as usual, however it now also -implements [ReactPHP's `ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface) -as well as parts of the [PSR-7 `StreamInterface`](https://www.php-fig.org/psr/psr-7/#34-psrhttpmessagestreaminterface). - -```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 the [stream download benchmark example](examples/91-client-benchmark-download.php) and -the [stream forwarding example](examples/21-client-request-streaming-to-stdout.php). - -You can invoke the following methods on the message body: - -```php -$body->on($event, $callback); -$body->eof(); -$body->isReadable(); -$body->pipe(React\Stream\WritableStreamInterface $dest, array $options = array()); -$body->close(); -$body->pause(); -$body->resume(); -``` - -Because the message body is in a streaming state, invoking the following methods -doesn't make much sense: - -```php -$body->__toString(); // '' -$body->detach(); // throws BadMethodCallException -$body->getSize(); // null -$body->tell(); // throws BadMethodCallException -$body->isSeekable(); // false -$body->seek(); // throws BadMethodCallException -$body->rewind(); // throws BadMethodCallException -$body->isWritable(); // false -$body->write(); // throws BadMethodCallException -$body->read(); // throws BadMethodCallException -$body->getContents(); // throws BadMethodCallException -``` - -Note how [timeouts](#timeouts) apply slightly differently when using streaming. -In streaming mode, the timeout value covers creating the underlying transport -connection, sending the HTTP request, receiving the HTTP response headers and -following any eventual [redirects](#redirects). In particular, the timeout value -does not take receiving (possibly large) response bodies into account. - -If you want to integrate the streaming response into a higher level API, then -working with Promise objects that resolve with Stream objects is often inconvenient. -Consider looking into also using [react/promise-stream](https://github.com/reactphp/promise-stream). -The resulting streaming code could look something like this: - -```php -use React\Promise\Stream; - -function download(Browser $browser, string $url): React\Stream\ReadableStreamInterface { - return Stream\unwrapReadable( - $browser->requestStreaming('GET', $url)->then(function (Psr\Http\Message\ResponseInterface $response) { - return $response->getBody(); - }) - ); -} - -$stream = download($browser, $url); -$stream->on('data', function ($data) { - echo $data; -}); -$stream->on('error', function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -See also the [`requestStreaming()`](#requeststreaming) method for more details. - -### Streaming request - -Besides streaming the response body, you can also stream the request body. -This can be useful if you want to send big POST requests (uploading files etc.) -or process many outgoing streams at once. -Instead of passing the body as a string, you can simply pass an instance -implementing [ReactPHP's `ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface) -to the [request methods](#request-methods) like this: - -```php -$browser->post($url, array(), $stream)->then(function (Psr\Http\Message\ResponseInterface $response) { - echo 'Successfully sent.'; -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -If you're using a streaming request body (`React\Stream\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); -``` - -If the streaming request body emits an `error` event or is explicitly closed -without emitting a successful `end` event first, the request will automatically -be closed and rejected. - -### HTTP proxy - -You can also establish your outgoing connections through an HTTP CONNECT proxy server -by adding a dependency to [clue/reactphp-http-proxy](https://github.com/clue/reactphp-http-proxy). - -HTTP CONNECT proxy servers (also commonly known as "HTTPS proxy" or "SSL proxy") -are commonly used to tunnel HTTPS traffic through an intermediary ("proxy"), to -conceal the origin address (anonymity) or to circumvent address blocking -(geoblocking). While many (public) HTTP CONNECT proxy servers often limit this -to HTTPS port `443` only, this can technically be used to tunnel any TCP/IP-based -protocol, such as plain HTTP and TLS-encrypted HTTPS. - -```php -$proxy = new Clue\React\HttpProxy\ProxyConnector('127.0.0.1:8080'); - -$connector = new React\Socket\Connector(array( - 'tcp' => $proxy, - 'dns' => false -)); - -$browser = new React\Http\Browser($connector); -``` - -See also the [HTTP proxy example](examples/11-client-http-proxy.php). - -### SOCKS proxy - -You can also establish your outgoing connections through a SOCKS proxy server -by adding a dependency to [clue/reactphp-socks](https://github.com/clue/reactphp-socks). - -The SOCKS proxy protocol family (SOCKS5, SOCKS4 and SOCKS4a) is commonly used to -tunnel HTTP(S) traffic through an intermediary ("proxy"), to conceal the origin -address (anonymity) or to circumvent address blocking (geoblocking). While many -(public) SOCKS proxy servers often limit this to HTTP(S) port `80` and `443` -only, this can technically be used to tunnel any TCP/IP-based protocol. - -```php -$proxy = new Clue\React\Socks\Client('127.0.0.1:1080'); - -$connector = new React\Socket\Connector(array( - 'tcp' => $proxy, - 'dns' => false -)); - -$browser = new React\Http\Browser($connector); -``` - -See also the [SOCKS proxy example](examples/12-client-socks-proxy.php). - -### SSH proxy - -You can also establish your outgoing connections through an SSH server -by adding a dependency to [clue/reactphp-ssh-proxy](https://github.com/clue/reactphp-ssh-proxy). - -[Secure Shell (SSH)](https://en.wikipedia.org/wiki/Secure_Shell) is a secure -network protocol that is most commonly used to access a login shell on a remote -server. Its architecture allows it to use multiple secure channels over a single -connection. Among others, this can also be used to create an "SSH tunnel", which -is commonly used to tunnel HTTP(S) traffic through an intermediary ("proxy"), to -conceal the origin address (anonymity) or to circumvent address blocking -(geoblocking). This can be used to tunnel any TCP/IP-based protocol (HTTP, SMTP, -IMAP etc.), allows you to access local services that are otherwise not accessible -from the outside (database behind firewall) and as such can also be used for -plain HTTP and TLS-encrypted HTTPS. - -```php -$proxy = new Clue\React\SshProxy\SshSocksConnector('alice@example.com'); - -$connector = new React\Socket\Connector(array( - 'tcp' => $proxy, - 'dns' => false -)); - -$browser = new React\Http\Browser($connector); -``` - -See also the [SSH proxy example](examples/13-client-ssh-proxy.php). - -### Unix domain sockets - -By default, this library supports transport over plaintext TCP/IP and secure -TLS connections for the `http://` and `https://` URL schemes respectively. -This library also supports Unix domain sockets (UDS) when explicitly configured. - -In order to use a UDS path, you have to explicitly configure the connector to -override the destination URL so that the hostname given in the request URL will -no longer be used to establish the connection: - -```php -$connector = new React\Socket\FixedUriConnector( - 'unix:///var/run/docker.sock', - new React\Socket\UnixConnector() -); - -$browser = new React\Http\Browser($connector); - -$client->get('http://localhost/info')->then(function (Psr\Http\Message\ResponseInterface $response) { - var_dump($response->getHeaders(), (string)$response->getBody()); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -See also the [Unix Domain Sockets (UDS) example](examples/14-client-unix-domain-sockets.php). - - -## Server Usage - -### HttpServer - - - -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 React\Http\Message\Response::plaintext( - "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 the -[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. - -### listen() - -The `listen(React\Socket\ServerInterface $socket): void` method can be used to -start 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. - -### Server Request - -As seen above, the [`HttpServer`](#httpserver) class is responsible for handling -incoming connections and then processing each incoming HTTP request. - -The request object will be processed once the request has -been received by the client. -This request object implements the -[PSR-7 `ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface) -which in turn extends the -[PSR-7 `RequestInterface`](https://www.php-fig.org/psr/psr-7/#32-psrhttpmessagerequestinterface) -and will be passed to the callback function like this. - - ```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $body = "The method of the request is: " . $request->getMethod() . "\n"; - $body .= "The requested path is: " . $request->getUri()->getPath() . "\n"; - - return React\Http\Message\Response::plaintext( - $body - ); -}); -``` - -For more details about the request object, also check out the documentation of -[PSR-7 `ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface) -and -[PSR-7 `RequestInterface`](https://www.php-fig.org/psr/psr-7/#32-psrhttpmessagerequestinterface). - -#### Request parameters - -The `getServerParams(): mixed[]` method can be used to -get server-side parameters similar to the `$_SERVER` variable. -The following parameters are currently available: - -* `REMOTE_ADDR` - The IP address of the request sender -* `REMOTE_PORT` - Port of the request sender -* `SERVER_ADDR` - The IP address of the server -* `SERVER_PORT` - The port of the server -* `REQUEST_TIME` - Unix timestamp when the complete request header has been received, - as integer similar to `time()` -* `REQUEST_TIME_FLOAT` - Unix timestamp when the complete request header has been received, - as float similar to `microtime(true)` -* `HTTPS` - Set to 'on' if the request used HTTPS, otherwise it won't be set - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $body = "Your IP is: " . $request->getServerParams()['REMOTE_ADDR'] . "\n"; - - return React\Http\Message\Response::plaintext( - $body - ); -}); -``` - -See also [whatsmyip server example](examples/53-server-whatsmyip.php). - -> Advanced: Note that address parameters will not be set if you're listening on - a Unix domain socket (UDS) path as this protocol lacks the concept of - host/port. - -#### Query parameters - -The `getQueryParams(): array` method can be used to get the query parameters -similiar to the `$_GET` variable. - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $queryParams = $request->getQueryParams(); - - $body = 'The query parameter "foo" is not set. Click the following link '; - $body .= 'to use query parameter in your request'; - - if (isset($queryParams['foo'])) { - $body = 'The value of "foo" is: ' . htmlspecialchars($queryParams['foo']); - } - - return React\Http\Message\Response::html( - $body - ); -}); -``` - -The response in the above example will return a response body with a link. -The URL contains the query parameter `foo` with the value `bar`. -Use [`htmlentities`](https://www.php.net/manual/en/function.htmlentities.php) -like in this example to prevent -[Cross-Site Scripting (abbreviated as XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting). - -See also [server query parameters example](examples/54-server-query-parameter.php). - -#### Request body - -By default, the [`Server`](#httpserver) will buffer and parse the full request body -in memory. This means the given request object includes the parsed request body -and any file uploads. - -> As an alternative to the default buffering logic, you can also use the - [`StreamingRequestMiddleware`](#streamingrequestmiddleware). Jump to the next - chapter to learn more about how to process a - [streaming incoming request](#streaming-incoming-request). - -As stated above, each incoming HTTP request is always represented by the -[PSR-7 `ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface). -This interface provides several methods that are useful when working with the -incoming request body as described below. - -The `getParsedBody(): null|array|object` method can be used to -get the parsed request body, similar to -[PHP's `$_POST` variable](https://www.php.net/manual/en/reserved.variables.post.php). -This method may return a (possibly nested) array structure with all body -parameters or a `null` value if the request body could not be parsed. -By default, this method will only return parsed data for requests using -`Content-Type: application/x-www-form-urlencoded` or `Content-Type: multipart/form-data` -request headers (commonly used for `POST` requests for HTML form submission data). - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $name = $request->getParsedBody()['name'] ?? 'anonymous'; - - return React\Http\Message\Response::plaintext( - "Hello $name!\n" - ); -}); -``` - -See also [form upload example](examples/62-server-form-upload.php) for more details. - -The `getBody(): StreamInterface` method can be used to -get the raw data from this request body, similar to -[PHP's `php://input` stream](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.input). -This method returns an instance of the request body represented by the -[PSR-7 `StreamInterface`](https://www.php-fig.org/psr/psr-7/#34-psrhttpmessagestreaminterface). -This is particularly useful when using a custom request body that will not -otherwise be parsed by default, such as a JSON (`Content-Type: application/json`) or -an XML (`Content-Type: application/xml`) request body (which is commonly used for -`POST`, `PUT` or `PATCH` requests in JSON-based or RESTful/RESTish APIs). - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $data = json_decode((string)$request->getBody()); - $name = $data->name ?? 'anonymous'; - - return React\Http\Message\Response::json( - ['message' => "Hello $name!"] - ); -}); -``` - -See also [JSON API server example](examples/59-server-json-api.php) for more details. - -The `getUploadedFiles(): array` method can be used to -get the uploaded files in this request, similar to -[PHP's `$_FILES` variable](https://www.php.net/manual/en/reserved.variables.files.php). -This method returns a (possibly nested) array structure with all file uploads, each represented by the -[PSR-7 `UploadedFileInterface`](https://www.php-fig.org/psr/psr-7/#36-psrhttpmessageuploadedfileinterface). -This array will only be filled when using the `Content-Type: multipart/form-data` -request header (commonly used for `POST` requests for HTML file uploads). - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $files = $request->getUploadedFiles(); - $name = isset($files['avatar']) ? $files['avatar']->getClientFilename() : 'nothing'; - - return React\Http\Message\Response::plaintext( - "Uploaded $name\n" - ); -}); -``` - -See also [form upload server example](examples/62-server-form-upload.php) for more details. - -The `getSize(): ?int` method can be used to -get the size of the request body, similar to PHP's `$_SERVER['CONTENT_LENGTH']` variable. -This method returns the complete size of the request body measured in number -of bytes as defined by the message boundaries. -This value may be `0` if the request message does not contain a request body -(such as a simple `GET` request). -This method operates on the buffered request body, i.e. the request body size -is always known, even when the request does not specify a `Content-Length` request -header or when using `Transfer-Encoding: chunked` for HTTP/1.1 requests. - -> Note: The `HttpServer` automatically takes care of handling requests with the - additional `Expect: 100-continue` request header. When HTTP/1.1 clients want to - send a bigger request body, they MAY send only the request headers with an - additional `Expect: 100-continue` request header and wait before sending the actual - (large) message body. In this case the server will automatically send an - intermediary `HTTP/1.1 100 Continue` response to the client. This ensures you - will receive the request body without a delay as expected. - -#### Streaming incoming request - -If you're using the advanced [`StreamingRequestMiddleware`](#streamingrequestmiddleware), -the request object will be processed once the request headers have been received. -This means that this happens irrespective of (i.e. *before*) receiving the -(potentially much larger) request body. - -> Note that this is non-standard behavior considered advanced usage. Jump to the - previous chapter to learn more about how to process a buffered [request body](#request-body). - -While this may be uncommon in the PHP ecosystem, this is actually a very powerful -approach that gives you several advantages not otherwise possible: - -* React to requests *before* receiving a large request body, - such as rejecting an unauthenticated request or one that exceeds allowed - message lengths (file uploads). -* Start processing parts of the request body before the remainder of the request - body arrives or if the sender is slowly streaming data. -* Process a large request body without having to buffer anything in memory, - such as accepting a huge file upload or possibly unlimited request body stream. - -The `getBody(): StreamInterface` method can be used to -access the request body stream. -In the streaming mode, this method returns a stream instance that implements both the -[PSR-7 `StreamInterface`](https://www.php-fig.org/psr/psr-7/#34-psrhttpmessagestreaminterface) -and the [ReactPHP `ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface). -However, most of the -[PSR-7 `StreamInterface`](https://www.php-fig.org/psr/psr-7/#34-psrhttpmessagestreaminterface) -methods have been designed under the assumption of being in control of a -synchronous request body. -Given that this does not apply to this server, the following -[PSR-7 `StreamInterface`](https://www.php-fig.org/psr/psr-7/#34-psrhttpmessagestreaminterface) -methods are not used and SHOULD NOT be called: -`tell()`, `eof()`, `seek()`, `rewind()`, `write()` and `read()`. -If this is an issue for your use case and/or you want to access uploaded files, -it's highly recommended to use a buffered [request body](#request-body) or use the -[`RequestBodyBufferMiddleware`](#requestbodybuffermiddleware) instead. -The [ReactPHP `ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface) -gives you access to the incoming request body as the individual chunks arrive: - -```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, $reject) use ($body) { - $bytes = 0; - $body->on('data', function ($data) use (&$bytes) { - $bytes += strlen($data); - }); - - $body->on('end', function () use ($resolve, &$bytes){ - $resolve(React\Http\Message\Response::plaintext( - "Received $bytes bytes\n" - )); - }); - - // an error occures e.g. on invalid chunked encoded data or an unexpected 'end' event - $body->on('error', function (Exception $e) use ($resolve, &$bytes) { - $resolve(React\Http\Message\Response::plaintext( - "Encountered error after $bytes bytes: {$e->getMessage()}\n" - )->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST)); - }); - }); - } -); -``` - -The above example simply counts the number of bytes received in the request body. -This can be used as a skeleton for buffering or processing the request body. - -See also [streaming request server example](examples/63-server-streaming-request.php) for more details. - -The `data` event will be emitted whenever new data is available on the request -body stream. -The server also automatically takes care of decoding any incoming requests using -`Transfer-Encoding: chunked` and will only emit the actual payload as data. - -The `end` event will be emitted when the request body stream terminates -successfully, i.e. it was read until its expected end. - -The `error` event will be emitted in case the request stream contains invalid -data for `Transfer-Encoding: chunked` or when the connection closes before -the complete request stream has been received. -The server will automatically stop reading from the connection and discard all -incoming data instead of closing it. -A response message can still be sent (unless the connection is already closed). - -A `close` event will be emitted after an `error` or `end` event. - -For more details about the request body stream, check out the documentation of -[ReactPHP `ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface). - -The `getSize(): ?int` method can be used to -get the size of the request body, similar to PHP's `$_SERVER['CONTENT_LENGTH']` variable. -This method returns the complete size of the request body measured in number -of bytes as defined by the message boundaries. -This value may be `0` if the request message does not contain a request body -(such as a simple `GET` request). -This method operates on the streaming request body, i.e. the request body size -may be unknown (`null`) when using `Transfer-Encoding: chunked` for HTTP/1.1 requests. - -```php -$http = new React\Http\HttpServer( - new React\Http\Middleware\StreamingRequestMiddleware(), - function (Psr\Http\Message\ServerRequestInterface $request) { - $size = $request->getBody()->getSize(); - if ($size === null) { - $body = "The request does not contain an explicit length. "; - $body .= "This example does not accept chunked transfer encoding.\n"; - - return React\Http\Message\Response::plaintext( - $body - )->withStatus(React\Http\Message\Response::STATUS_LENGTH_REQUIRED); - } - - return React\Http\Message\Response::plaintext( - "Request body size: " . $size . " bytes\n" - ); - } -); -``` - -> Note: The `HttpServer` automatically takes care of handling requests with the - additional `Expect: 100-continue` request header. When HTTP/1.1 clients want to - send a bigger request body, they MAY send only the request headers with an - additional `Expect: 100-continue` request header and wait before sending the actual - (large) message body. In this case the server will automatically send an - intermediary `HTTP/1.1 100 Continue` response to the client. This ensures you - will receive the streaming request body without a delay as expected. - -#### Request method - -Note that the server supports *any* request method (including custom and non- -standard ones) and all request-target formats defined in the HTTP specs for each -respective method, including *normal* `origin-form` requests as well as -proxy requests in `absolute-form` and `authority-form`. -The `getUri(): UriInterface` method can be used to get the effective request -URI which provides you access to individiual URI components. -Note that (depending on the given `request-target`) certain URI components may -or may not be present, for example the `getPath(): string` method will return -an empty string for requests in `asterisk-form` or `authority-form`. -Its `getHost(): string` method will return the host as determined by the -effective request URI, which defaults to the local socket address if an HTTP/1.0 -client did not specify one (i.e. no `Host` header). -Its `getScheme(): string` method will return `http` or `https` depending -on whether the request was made over a secure TLS connection to the target host. - -The `Host` header value will be sanitized to match this host component plus the -port component only if it is non-standard for this URI scheme. - -You can use `getMethod(): string` and `getRequestTarget(): string` to -check this is an accepted request and may want to reject other requests with -an appropriate error code, such as `400` (Bad Request) or `405` (Method Not -Allowed). - -> The `CONNECT` method is useful in a tunneling setup (HTTPS proxy) and not - something most HTTP servers would want to care about. - Note that if you want to handle this method, the client MAY send a different - request-target than the `Host` header value (such as removing default ports) - and the request-target MUST take precendence when forwarding. - -#### Cookie parameters - -The `getCookieParams(): string[]` method can be used to -get all cookies sent with the current request. - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $key = 'greeting'; - - if (isset($request->getCookieParams()[$key])) { - $body = "Your cookie value is: " . $request->getCookieParams()[$key] . "\n"; - - return React\Http\Message\Response::plaintext( - $body - ); - } - - return React\Http\Message\Response::plaintext( - "Your cookie has been set.\n" - )->withHeader('Set-Cookie', $key . '=' . urlencode('Hello world!')); -}); -``` - -The above example will try to set a cookie on first access and -will try to print the cookie value on all subsequent tries. -Note how the example uses the `urlencode()` function to encode -non-alphanumeric characters. -This encoding is also used internally when decoding the name and value of cookies -(which is in line with other implementations, such as PHP's cookie functions). - -See also [cookie server example](examples/55-server-cookie-handling.php) for more details. - -#### Invalid request - -The `HttpServer` class supports both HTTP/1.1 and HTTP/1.0 request messages. -If a client sends an invalid request message, uses an invalid HTTP -protocol version or sends an invalid `Transfer-Encoding` request header value, -the server will automatically send a `400` (Bad Request) HTTP error response -to the client and close the connection. -On top of this, it will emit an `error` event that can be used for logging -purposes like this: - -```php -$http->on('error', function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -Note that the server will also emit an `error` event if you do not return a -valid response object from your request handler function. See also -[invalid response](#invalid-response) for more details. - -### Server Response - -The callback function passed to the constructor of the [`HttpServer`](#httpserver) is -responsible for processing the request and returning a response, which will be -delivered to the client. - -This function MUST return an instance implementing -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) -object or a -[ReactPHP Promise](https://github.com/reactphp/promise) -which resolves with a [PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) object. - -This projects ships a [`Response` class](#response) which implements the -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface). -In its most simple form, you can use it like this: - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - return React\Http\Message\Response::plaintext( - "Hello World!\n" - ); -}); -``` - -We use this [`Response` class](#response) throughout our project examples, but -feel free to use any other implementation of the -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface). -See also the [`Response` class](#response) for more details. - -#### Deferred response - -The example above returns the response directly, because it needs -no time to be processed. -Using a database, the file system or long calculations -(in fact every action that will take >=1ms) to create your -response, will slow down the server. -To prevent this you SHOULD use a -[ReactPHP Promise](https://github.com/reactphp/promise#reactpromise). -This example shows how such a long-term action could look like: - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $promise = new Promise(function ($resolve, $reject) { - Loop::addTimer(1.5, function() use ($resolve) { - $resolve(); - }); - }); - - return $promise->then(function () { - return React\Http\Message\Response::plaintext( - "Hello World!" - ); - }); -}); -``` - -The above example will create a response after 1.5 second. -This example shows that you need a promise, -if your response needs time to created. -The `ReactPHP Promise` will resolve in a `Response` object when the request -body ends. -If the client closes the connection while the promise is still pending, the -promise will automatically be cancelled. -The promise cancellation handler can be used to clean up any pending resources -allocated in this case (if applicable). -If a promise is resolved after the client closes, it will simply be ignored. - -#### Streaming outgoing response - -The `Response` class in this project supports to add an instance which implements the -[ReactPHP `ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface) -for the response body. -So you are able stream data directly into the response body. -Note that other implementations of the -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) -may only support strings. - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $stream = new ThroughStream(); - - // send some data every once in a while with periodic timer - $timer = Loop::addPeriodicTimer(0.5, function () use ($stream) { - $stream->write(microtime(true) . PHP_EOL); - }); - - // end stream after a few seconds - $timeout = Loop::addTimer(5.0, function() use ($stream, $timer) { - Loop::cancelTimer($timer); - $stream->end(); - }); - - // stop timer if stream is closed (such as when connection is closed) - $stream->on('close', function () use ($timer, $timeout) { - Loop::cancelTimer($timer); - Loop::cancelTimer($timeout); - }); - - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), - $stream - ); -}); -``` - -The above example will emit every 0.5 seconds the current Unix timestamp -with microseconds as float to the client and will end after 5 seconds. -This is just a example you could use of the streaming, -you could also send a big amount of data via little chunks -or use it for body data that needs to calculated. - -If the request handler resolves with a response stream that is already closed, -it will simply send an empty response body. -If the client closes the connection while the stream is still open, the -response stream will automatically be closed. -If a promise is resolved with a streaming body after the client closes, the -response stream will automatically be closed. -The `close` event can be used to clean up any pending resources allocated -in this case (if applicable). - -> Note that special care has to be taken if you use a body stream instance that - implements ReactPHP's - [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface) - (such as the `ThroughStream` in the above example). -> -> For *most* cases, this will simply only consume its readable side and forward - (send) any data that is emitted by the stream, thus entirely ignoring the - writable side of the stream. - If however this is either a `101` (Switching Protocols) response or a `2xx` - (Successful) response to a `CONNECT` method, it will also *write* data to the - writable side of the stream. - This can be avoided by either rejecting all requests with the `CONNECT` - method (which is what most *normal* origin HTTP servers would likely do) or - or ensuring that only ever an instance of - [ReactPHP's `ReadableStreamInterface`](https://github.com/reactphp/stream#readablestreaminterface) - is used. -> -> The `101` (Switching Protocols) response code is useful for the more advanced - `Upgrade` requests, such as upgrading to the WebSocket protocol or - implementing custom protocol logic that is out of scope of the HTTP specs and - this HTTP library. - If you want to handle the `Upgrade: WebSocket` header, you will likely want - to look into using [Ratchet](http://socketo.me/) instead. - If you want to handle a custom protocol, you will likely want to look into the - [HTTP specs](https://tools.ietf.org/html/rfc7230#section-6.7) and also see - [examples #81 and #82](examples/) for more details. - In particular, the `101` (Switching Protocols) response code MUST NOT be used - unless you send an `Upgrade` response header value that is also present in - the corresponding HTTP/1.1 `Upgrade` request header value. - The server automatically takes care of sending a `Connection: upgrade` - header value in this case, so you don't have to. -> -> The `CONNECT` method is useful in a tunneling setup (HTTPS proxy) and not - something most origin HTTP servers would want to care about. - The HTTP specs define an opaque "tunneling mode" for this method and make no - use of the message body. - For consistency reasons, this library uses a `DuplexStreamInterface` in the - response body for tunneled application data. - This implies that that a `2xx` (Successful) response to a `CONNECT` request - can in fact use a streaming response body for the tunneled application data, - so that any raw data the client sends over the connection will be piped - through the writable stream for consumption. - Note that while the HTTP specs make no use of the request body for `CONNECT` - requests, one may still be present. Normal request body processing applies - here and the connection will only turn to "tunneling mode" after the request - body has been processed (which should be empty in most cases). - See also [HTTP CONNECT server example](examples/72-server-http-connect-proxy.php) for more details. - -#### Response length - -If the response body size is known, a `Content-Length` response header will be -added automatically. This is the most common use case, for example when using -a `string` response body like this: - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - return React\Http\Message\Response::plaintext( - "Hello World!\n" - ); -}); -``` - -If the response body size is unknown, a `Content-Length` response header can not -be added automatically. When using a [streaming outgoing response](#streaming-outgoing-response) -without an explicit `Content-Length` response header, outgoing HTTP/1.1 response -messages will automatically use `Transfer-Encoding: chunked` while legacy HTTP/1.0 -response messages will contain the plain response body. If you know the length -of your streaming response body, you MAY want to specify it explicitly like this: - -```php -$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { - $stream = new ThroughStream(); - - Loop::addTimer(2.0, function () use ($stream) { - $stream->end("Hello World!\n"); - }); - - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Length' => '13', - 'Content-Type' => 'text/plain', - ), - $stream - ); -}); -``` - -Any response to a `HEAD` request and any response with a `1xx` (Informational), -`204` (No Content) or `304` (Not Modified) status code will *not* include a -message body as per the HTTP specs. -This means that your callback does not have to take special care of this and any -response body will simply be ignored. - -Similarly, any `2xx` (Successful) response to a `CONNECT` request, any response -with a `1xx` (Informational) or `204` (No Content) status code will *not* -include a `Content-Length` or `Transfer-Encoding` header as these do not apply -to these messages. -Note that a response to a `HEAD` request and any response with a `304` (Not -Modified) status code MAY include these headers even though -the message does not contain a response body, because these header would apply -to the message if the same request would have used an (unconditional) `GET`. - -#### Invalid response - -As stated above, each outgoing HTTP response is always represented by the -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface). -If your request handler function returns an invalid value or throws an -unhandled `Exception` or `Throwable`, the server will automatically send a `500` -(Internal Server Error) HTTP error response to the client. -On top of this, it will emit an `error` event that can be used for logging -purposes like this: - -```php -$http->on('error', function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; - if ($e->getPrevious() !== null) { - echo 'Previous: ' . $e->getPrevious()->getMessage() . PHP_EOL; - } -}); -``` - -Note that the server will also emit an `error` event if the client sends an -invalid HTTP request that never reaches your request handler function. See -also [invalid request](#invalid-request) for more details. -Additionally, a [streaming incoming request](#streaming-incoming-request) body -can also emit an `error` event on the request body. - -The server will only send a very generic `500` (Interval Server Error) HTTP -error response without any further details to the client if an unhandled -error occurs. While we understand this might make initial debugging harder, -it also means that the server does not leak any application details or stack -traces to the outside by default. It is usually recommended to catch any -`Exception` or `Throwable` within your request handler function or alternatively -use a [`middleware`](#middleware) to avoid this generic error handling and -create your own HTTP response message instead. - -#### Default response headers - -When a response is returned from the request handler function, it will be -processed by the [`HttpServer`](#httpserver) and then sent back to the client. - -A `Server: ReactPHP/1` response header will be added automatically. You can add -a custom `Server` response header like this: - -```php -$http = new React\Http\HttpServer(function (ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Server' => 'PHP/3' - ) - ); -}); -``` - -If you do not want to send this `Sever` response header at all (such as when you -don't want to expose the underlying server software), you can use an empty -string value like this: - -```php -$http = new React\Http\HttpServer(function (ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Server' => '' - ) - ); -}); -``` - -A `Date` response header will be added automatically with the current system -date and time if none is given. You can add a custom `Date` response header -like this: - -```php -$http = new React\Http\HttpServer(function (ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Date' => gmdate('D, d M Y H:i:s \G\M\T') - ) - ); -}); -``` - -If you do not want to send this `Date` response header at all (such as when you -don't have an appropriate clock to rely on), you can use an empty string value -like this: - -```php -$http = new React\Http\HttpServer(function (ServerRequestInterface $request) { - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Date' => '' - ) - ); -}); -``` - -The `HttpServer` class will automatically add the protocol version of the request, -so you don't have to. For instance, if the client sends the request using the -HTTP/1.1 protocol version, the response message will also use the same protocol -version, no matter what version is returned from the request handler function. - -The server supports persistent connections. An appropriate `Connection: keep-alive` -or `Connection: close` response header will be added automatically, respecting the -matching request header value and HTTP default header values. The server is -responsible for handling the `Connection` response header, so you SHOULD NOT pass -this response header yourself, unless you explicitly want to override the user's -choice with a `Connection: close` response header. - -### Middleware - -As documented above, the [`HttpServer`](#httpserver) accepts a single request handler -argument that is responsible for processing an incoming HTTP request and then -creating and returning an outgoing HTTP response. - -Many common use cases involve validating, processing, manipulating the incoming -HTTP request before passing it to the final business logic request handler. -As such, this project supports the concept of middleware request handlers. - -#### Custom middleware - -A middleware request handler is expected to adhere the following rules: - -* It is a valid `callable`. -* It accepts an instance implementing - [PSR-7 `ServerRequestInterface`](https://www.php-fig.org/psr/psr-7/#321-psrhttpmessageserverrequestinterface) - as first argument and an optional `callable` as second argument. -* It returns either: - * An instance implementing - [PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) - for direct consumption. - * Any promise which can be consumed by - [`Promise\resolve()`](https://reactphp.org/promise/#resolve) resolving to a - [PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) - for deferred consumption. - * It MAY throw an `Exception` (or return a rejected promise) in order to - signal an error condition and abort the chain. -* It calls `$next($request)` to continue processing the next middleware - request handler or returns explicitly without calling `$next` to - abort the chain. - * The `$next` request handler (recursively) invokes the next request - handler from the chain with the same logic as above and returns (or throws) - as above. - * The `$request` may be modified prior to calling `$next($request)` to - change the incoming request the next middleware operates on. - * The `$next` return value may be consumed to modify the outgoing response. - * The `$next` request handler MAY be called more than once if you want to - implement custom "retry" logic etc. - -Note that this very simple definition allows you to use either anonymous -functions or any classes that use the magic `__invoke()` method. -This allows you to easily create custom middleware request handlers on the fly -or use a class based approach to ease using existing middleware implementations. - -While this project does provide the means to *use* middleware implementations, -it does not aim to *define* how middleware implementations should look like. -We realize that there's a vivid ecosystem of middleware implementations and -ongoing effort to standardize interfaces between these with -[PSR-15](https://www.php-fig.org/psr/psr-15/) (HTTP Server Request Handlers) -and support this goal. -As such, this project only bundles a few middleware implementations that are -required to match PHP's request behavior (see below) and otherwise actively -encourages [Third-Party Middleware](#third-party-middleware) implementations. - -In order to use middleware request handlers, simply pass a list of all -callables as defined above to the [`HttpServer`](#httpserver). -The following example adds a middleware request handler that adds the current time to the request as a -header (`Request-Time`) and a final request handler that always returns a `200 OK` status code without a body: - -```php -$http = new React\Http\HttpServer( - function (Psr\Http\Message\ServerRequestInterface $request, callable $next) { - $request = $request->withHeader('Request-Time', time()); - return $next($request); - }, - function (Psr\Http\Message\ServerRequestInterface $request) { - return new React\Http\Message\Response(React\Http\Message\Response::STATUS_OK); - } -); -``` - -> Note how the middleware request handler and the final request handler have a - very simple (and similar) interface. The only difference is that the final - request handler does not receive a `$next` handler. - -Similarly, you can use the result of the `$next` middleware request handler -function to modify the outgoing response. -Note that as per the above documentation, the `$next` middleware request handler may return a -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) -directly or one wrapped in a promise for deferred resolution. -In order to simplify handling both paths, you can simply wrap this in a -[`Promise\resolve()`](https://reactphp.org/promise/#resolve) call like this: - -```php -$http = new React\Http\HttpServer( - function (Psr\Http\Message\ServerRequestInterface $request, callable $next) { - $promise = React\Promise\resolve($next($request)); - return $promise->then(function (ResponseInterface $response) { - return $response->withHeader('Content-Type', 'text/html'); - }); - }, - function (Psr\Http\Message\ServerRequestInterface $request) { - return new React\Http\Message\Response(React\Http\Message\Response::STATUS_OK); - } -); -``` - -Note that the `$next` middleware request handler may also throw an -`Exception` (or return a rejected promise) as described above. -The previous example does not catch any exceptions and would thus signal an -error condition to the `HttpServer`. -Alternatively, you can also catch any `Exception` to implement custom error -handling logic (or logging etc.) by wrapping this in a -[`Promise`](https://reactphp.org/promise/#promise) like this: - -```php -$http = new React\Http\HttpServer( - function (Psr\Http\Message\ServerRequestInterface $request, callable $next) { - $promise = new React\Promise\Promise(function ($resolve) use ($next, $request) { - $resolve($next($request)); - }); - return $promise->then(null, function (Exception $e) { - return React\Http\Message\Response::plaintext( - 'Internal error: ' . $e->getMessage() . "\n" - )->withStatus(React\Http\Message\Response::STATUS_INTERNAL_SERVER_ERROR); - }); - }, - function (Psr\Http\Message\ServerRequestInterface $request) { - if (mt_rand(0, 1) === 1) { - throw new RuntimeException('Database error'); - } - return new React\Http\Message\Response(React\Http\Message\Response::STATUS_OK); - } -); -``` - -#### Third-Party Middleware - -While this project does provide the means to *use* middleware implementations -(see above), it does not aim to *define* how middleware implementations should -look like. We realize that there's a vivid ecosystem of middleware -implementations and ongoing effort to standardize interfaces between these with -[PSR-15](https://www.php-fig.org/psr/psr-15/) (HTTP Server Request Handlers) -and support this goal. -As such, this project only bundles a few middleware implementations that are -required to match PHP's request behavior (see -[middleware implementations](#reacthttpmiddleware)) and otherwise actively -encourages third-party middleware implementations. - -While we would love to support PSR-15 directly in `react/http`, we understand -that this interface does not specifically target async APIs and as such does -not take advantage of promises for [deferred responses](#deferred-response). -The gist of this is that where PSR-15 enforces a -[PSR-7 `ResponseInterface`](https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface) -return value, we also accept a `PromiseInterface`. -As such, we suggest using the external -[PSR-15 middleware adapter](https://github.com/friends-of-reactphp/http-middleware-psr15-adapter) -that uses on the fly monkey patching of these return values which makes using -most PSR-15 middleware possible with this package without any changes required. - -Other than that, you can also use the above [middleware definition](#middleware) -to create custom middleware. A non-exhaustive list of third-party middleware can -be found at the [middleware wiki](https://github.com/reactphp/reactphp/wiki/Users#http-middleware). -If you build or know a custom middleware, make sure to let the world know and -feel free to add it to this list. - -## API - -### Browser - -The `React\Http\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. - -> Note that the browser class is final and shouldn't be extended, it is likely to be marked final in a future release. - -#### get() - -The `get(string $url, array $headers = array()): PromiseInterface` method can be used to -send 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). - -#### post() - -The `post(string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface` method can be used to -send 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); -``` - -#### head() - -The `head(string $url, array $headers = array()): PromiseInterface` method can be used to -send 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; -}); -``` - -#### patch() - -The `patch(string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface` method can be used to -send 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); -``` - -#### put() - -The `put(string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface` method can be used to -send 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); -``` - -#### delete() - -The `delete(string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface` method can be used to -send 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; -}); -``` - -#### request() - -The `request(string $method, string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface` method can be used to -send 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); -``` - -#### requestStreaming() - -The `requestStreaming(string $method, string $url, array $headers = array(), string|ReadableStreamInterface $body = ''): PromiseInterface` method can be used to -send an arbitrary HTTP request and receive 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 [ReactPHP's `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); -``` - -#### withTimeout() - -The `withTimeout(bool|number $timeout): Browser` method can be used to -change 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. - -#### withFollowRedirects() - -The `withFollowRedirects(bool|int $followRedirects): Browser` method can be used to -change 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. - -#### withRejectErrorResponse() - -The `withRejectErrorResponse(bool $obeySuccessCode): Browser` method can be used to -change 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 with a [`ResponseException`](#responseexception): - -```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. - -#### withBase() - -The `withBase(string|null $baseUrl): Browser` method can be used to -change 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. - -#### withProtocolVersion() - -The `withProtocolVersion(string $protocolVersion): Browser` method can be used to -change 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. - -#### withResponseBuffer() - -The `withResponseBuffer(int $maximumSize): Browser` method can be used to -change 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. - -#### withHeader() - -The `withHeader(string $header, string $value): Browser` method can be used to -add a request header for all following requests. - -```php -$browser = $browser->withHeader('User-Agent', 'ACME'); - -$browser->get($url)->then(…); -``` - -Note that the new header will overwrite any headers previously set with -the same name (case-insensitive). Following requests will use these headers -by default unless they are explicitly set for any requests. - -#### withoutHeader() - -The `withoutHeader(string $header): Browser` method can be used to -remove any default request headers previously set via -the [`withHeader()` method](#withheader). - -```php -$browser = $browser->withoutHeader('User-Agent'); - -$browser->get($url)->then(…); -``` - -Note that this method only affects the headers which were set with the -method `withHeader(string $header, string $value): Browser` - -### React\Http\Message - -#### Response - -The `React\Http\Message\Response` class can be used to -represent an outgoing server response message. - -```php -$response = new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/html' - ), - "Hello world!\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 a base class which is - considered an implementation detail that may change in the future. - -##### html() - -The static `html(string $html): Response` method can be used to -create an HTML response. - -```php -$html = << - -Hello wörld! - - -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( - "

Error

\n

Invalid user name given.

\n" -)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); -``` - -##### json() - -The static `json(mixed $data): Response` method can be used to -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); -``` - -##### plaintext() - -The static `plaintext(string $text): Response` method can be used to -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); -``` - -##### xml() - -The static `xml(string $xml): Response` method can be used to -create an XML response. - -```php -$xml = << - - Hello wörld! - - -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( - "Invalid user name given.\n" -)->withStatus(React\Http\Message\Response::STATUS_BAD_REQUEST); -``` - -#### Request - -The `React\Http\Message\Request` class can be used to -respresent an outgoing HTTP request message. - -This class implements the -[PSR-7 `RequestInterface`](https://www.php-fig.org/psr/psr-7/#32-psrhttpmessagerequestinterface) -which extends the -[PSR-7 `MessageInterface`](https://www.php-fig.org/psr/psr-7/#31-psrhttpmessagemessageinterface). - -This is mostly used internally to represent each outgoing HTTP request -message for the HTTP client implementation. Likewise, you can also use this -class with other HTTP client implementations and for tests. - -> Internally, this implementation builds on top of a base class which is - considered an implementation detail that may change in the future. - -#### ServerRequest - -The `React\Http\Message\ServerRequest` class can be used to -respresent 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 a base class which is - considered an implementation detail that may change in the future. - -#### Uri - -The `React\Http\Message\Uri` class can be used to -respresent a URI (or URL). - -This class implements the -[PSR-7 `UriInterface`](https://www.php-fig.org/psr/psr-7/#35-psrhttpmessageuriinterface). - -This is mostly used internally to represent the URI of each HTTP request -message for our HTTP client and server implementations. Likewise, you may -also use this class with other HTTP implementations and for tests. - -#### ResponseException - -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. - -The `getResponse(): ResponseInterface` method can be used to -access its underlying response object. - -### React\Http\Middleware - -#### StreamingRequestMiddleware - -The `React\Http\Middleware\StreamingRequestMiddleware` can be used to -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\Message\Response( - React\Http\Message\Response::STATUS_OK, - [], - "Received $bytes bytes\n" - )); - }); - }); - } -); -``` - -See also [streaming incoming request](#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. - -#### LimitConcurrentRequestsMiddleware - -The `React\Http\Middleware\LimitConcurrentRequestsMiddleware` can be used to -limit 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\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 -); -``` - -#### RequestBodyBufferMiddleware - -One of the built-in middleware is the `React\Http\Middleware\RequestBodyBufferMiddleware` which -can be used to buffer the whole incoming request body in memory. -This can be useful if full PSR-7 compatibility is needed for the request handler -and the default streaming request body handling is not needed. -The constructor accepts one optional argument, the maximum request body size. -When one isn't provided it will use `post_max_size` (default 8 MiB) from PHP's -configuration. -(Note that the value from your matching SAPI will be used, which is the CLI -configuration in most cases.) - -Any incoming request that has a request body that exceeds this limit will be -accepted, but its request body will be discarded (empty request body). -This is done in order to avoid having to keep an incoming request with an -excessive size (for example, think of a 2 GB file upload) in memory. -This allows the next middleware handler to still handle this request, but it -will see an empty request body. -This is similar to PHP's default behavior, where the body will not be parsed -if this limit is exceeded. However, unlike PHP's default behavior, the raw -request body is not available via `php://input`. - -The `RequestBodyBufferMiddleware` will buffer requests with bodies of known size -(i.e. with `Content-Length` header specified) as well as requests with bodies of -unknown size (i.e. with `Transfer-Encoding: chunked` header). - -All requests will be buffered in memory until the request body end has -been reached and then call the next middleware handler with the complete, -buffered request. -Similarly, this will immediately invoke the next middleware handler for requests -that have an empty request body (such as a simple `GET` request) and requests -that are already buffered (such as due to another middleware). - -Note that the given buffer size limit is applied to each request individually. -This means that if you allow a 2 MiB limit and then receive 1000 concurrent -requests, up to 2000 MiB may be allocated for these buffers alone. -As such, it's highly recommended to use this along with the -[`LimitConcurrentRequestsMiddleware`](#limitconcurrentrequestsmiddleware) (see above) to limit -the total number of concurrent requests. - -Usage: - -```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(16 * 1024 * 1024), // 16 MiB - function (Psr\Http\Message\ServerRequestInterface $request) { - // The body from $request->getBody() is now fully available without the need to stream it - return new React\Http\Message\Response(React\Http\Message\Response::STATUS_OK); - }, -); -``` - -#### RequestBodyParserMiddleware - -The `React\Http\Middleware\RequestBodyParserMiddleware` takes a fully buffered request body -(generally from [`RequestBodyBufferMiddleware`](#requestbodybuffermiddleware)), -and parses the form values and file uploads from the incoming HTTP request body. - -This middleware handler takes care of applying values from HTTP -requests that use `Content-Type: application/x-www-form-urlencoded` or -`Content-Type: multipart/form-data` to resemble PHP's default superglobals -`$_POST` and `$_FILES`. -Instead of relying on these superglobals, you can use the -`$request->getParsedBody()` and `$request->getUploadedFiles()` methods -as defined by PSR-7. - -Accordingly, each file upload will be represented as instance implementing the -[PSR-7 `UploadedFileInterface`](https://www.php-fig.org/psr/psr-7/#36-psrhttpmessageuploadedfileinterface). -Due to its blocking nature, the `moveTo()` method is not available and throws -a `RuntimeException` instead. -You can use `$contents = (string)$file->getStream();` to access the file -contents and persist this to your favorite data store. - -```php -$handler = function (Psr\Http\Message\ServerRequestInterface $request) { - // If any, parsed form fields are now available from $request->getParsedBody() - $body = $request->getParsedBody(); - $name = isset($body['name']) ? $body['name'] : 'unnamed'; - - $files = $request->getUploadedFiles(); - $avatar = isset($files['avatar']) ? $files['avatar'] : null; - if ($avatar instanceof Psr\Http\Message\UploadedFileInterface) { - if ($avatar->getError() === UPLOAD_ERR_OK) { - $uploaded = $avatar->getSize() . ' bytes'; - } elseif ($avatar->getError() === UPLOAD_ERR_INI_SIZE) { - $uploaded = 'file too large'; - } else { - $uploaded = 'with error'; - } - } else { - $uploaded = 'nothing'; - } - - return new React\Http\Message\Response( - React\Http\Message\Response::STATUS_OK, - array( - 'Content-Type' => 'text/plain' - ), - $name . ' uploaded ' . $uploaded - ); -}; - -$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(16 * 1024 * 1024), // 16 MiB - new React\Http\Middleware\RequestBodyParserMiddleware(), - $handler -); -``` - -See also [form upload server example](examples/62-server-form-upload.php) for more details. - -By default, this middleware respects the -[`upload_max_filesize`](https://www.php.net/manual/en/ini.core.php#ini.upload-max-filesize) -(default `2M`) ini setting. -Files that exceed this limit will be rejected with an `UPLOAD_ERR_INI_SIZE` error. -You can control the maximum filesize for each individual file upload by -explicitly passing the maximum filesize in bytes as the first parameter to the -constructor like this: - -```php -new React\Http\Middleware\RequestBodyParserMiddleware(8 * 1024 * 1024); // 8 MiB limit per file -``` - -By default, this middleware respects the -[`file_uploads`](https://www.php.net/manual/en/ini.core.php#ini.file-uploads) -(default `1`) and -[`max_file_uploads`](https://www.php.net/manual/en/ini.core.php#ini.max-file-uploads) -(default `20`) ini settings. -These settings control if any and how many files can be uploaded in a single request. -If you upload more files in a single request, additional files will be ignored -and the `getUploadedFiles()` method returns a truncated array. -Note that upload fields left blank on submission do not count towards this limit. -You can control the maximum number of file uploads per request by explicitly -passing the second parameter to the constructor like this: - -```php -new React\Http\Middleware\RequestBodyParserMiddleware(10 * 1024, 100); // 100 files with 10 KiB each -``` - -> Note that this middleware handler simply parses everything that is already - buffered in the request body. - It is imperative that the request body is buffered by a prior middleware - handler as given in the example above. - This previous middleware handler is also responsible for rejecting incoming - requests that exceed allowed message sizes (such as big file uploads). - The [`RequestBodyBufferMiddleware`](#requestbodybuffermiddleware) used above - simply discards excessive request bodies, resulting in an empty body. - If you use this middleware without buffering first, it will try to parse an - empty (streaming) body and may thus assume an empty data structure. - See also [`RequestBodyBufferMiddleware`](#requestbodybuffermiddleware) for - more details. - -> PHP's `MAX_FILE_SIZE` hidden field is respected by this middleware. - Files that exceed this limit will be rejected with an `UPLOAD_ERR_FORM_SIZE` error. - -> This middleware respects the - [`max_input_vars`](https://www.php.net/manual/en/info.configuration.php#ini.max-input-vars) - (default `1000`) and - [`max_input_nesting_level`](https://www.php.net/manual/en/info.configuration.php#ini.max-input-nesting-level) - (default `64`) ini settings. - -> Note that this middleware ignores the - [`enable_post_data_reading`](https://www.php.net/manual/en/ini.core.php#ini.enable-post-data-reading) - (default `1`) ini setting because it makes little sense to respect here and - is left up to higher-level implementations. - If you want to respect this setting, you have to check its value and - effectively avoid using this middleware entirely. - -## Install - -The recommended way to install this library is [through Composer](https://getcomposer.org/). -[New to Composer?](https://getcomposer.org/doc/00-intro.md) - -This project follows [SemVer](https://semver.org/). -This will install the latest supported version: - -```bash -composer require react/http:^1.11 -``` - -See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. - -This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.3 through current PHP 8+ and -HHVM. -It's *highly recommended to use the latest supported PHP version* for this project. - -## Tests - -To run the test suite, you first need to clone this repo and then install all -dependencies [through Composer](https://getcomposer.org/): - -```bash -composer install -``` - -To run the test suite, go to the project root and run: - -```bash -vendor/bin/phpunit -``` - -The test suite also contains a number of functional integration tests that rely -on a stable internet connection. -If you do not want to run these, they can simply be skipped like this: - -```bash -vendor/bin/phpunit --exclude-group internet -``` - -## License - -MIT, see [LICENSE file](LICENSE). diff --git a/vendor/react/http/composer.json b/vendor/react/http/composer.json deleted file mode 100644 index 4234210..0000000 --- a/vendor/react/http/composer.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "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": "^3.2 || ^2.3 || ^1.2.1", - "react/socket": "^1.16", - "react/stream": "^1.4" - }, - "require-dev": { - "clue/http-proxy-react": "^1.8", - "clue/reactphp-ssh-proxy": "^1.4", - "clue/socks-react": "^1.4", - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.2 || ^3 || ^2", - "react/promise-stream": "^1.4", - "react/promise-timer": "^1.11" - }, - "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 deleted file mode 100644 index 9da0dca..0000000 --- a/vendor/react/http/src/Browser.php +++ /dev/null @@ -1,858 +0,0 @@ - 'ReactPHP/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 ?: new Connector(array(), $loop)), - $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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - */ - 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 - )); - } - - /** - * Add a request header for all following requests. - * - * ```php - * $browser = $browser->withHeader('User-Agent', 'ACME'); - * - * $browser->get($url)->then(…); - * ``` - * - * Note that the new header will overwrite any headers previously set with - * the same name (case-insensitive). Following requests will use these headers - * by default unless they are explicitly set for any requests. - * - * @param string $header - * @param string $value - * @return Browser - */ - public function withHeader($header, $value) - { - $browser = $this->withoutHeader($header); - $browser->defaultHeaders[$header] = $value; - - return $browser; - } - - /** - * Remove any default request headers previously set via - * the [`withHeader()` method](#withheader). - * - * ```php - * $browser = $browser->withoutHeader('User-Agent'); - * - * $browser->get($url)->then(…); - * ``` - * - * Note that this method only affects the headers which were set with the - * method `withHeader(string $header, string $value): Browser` - * - * @param string $header - * @return Browser - */ - public function withoutHeader($header) - { - $browser = clone $this; - - /** @var string|int $key */ - foreach (\array_keys($browser->defaultHeaders) as $key) { - if (\strcasecmp($key, $header) === 0) { - unset($browser->defaultHeaders[$key]); - break; - } - } - - return $browser; - } - - /** - * 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 - */ - 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, new Uri($url)); - } - - foreach ($this->defaultHeaders as $key => $value) { - $explicitHeaderExists = false; - foreach (\array_keys($headers) as $headerKey) { - if (\strcasecmp($headerKey, $key) === 0) { - $explicitHeaderExists = true; - break; - } - } - if (!$explicitHeaderExists) { - $headers[$key] = $value; - } - } - - 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 deleted file mode 100644 index 7a5180a..0000000 --- a/vendor/react/http/src/Client/Client.php +++ /dev/null @@ -1,27 +0,0 @@ -connectionManager = $connectionManager; - } - - /** @return ClientRequestStream */ - public function request(RequestInterface $request) - { - return new ClientRequestStream($this->connectionManager, $request); - } -} diff --git a/vendor/react/http/src/HttpServer.php b/vendor/react/http/src/HttpServer.php deleted file mode 100644 index f233473..0000000 --- a/vendor/react/http/src/HttpServer.php +++ /dev/null @@ -1,351 +0,0 @@ - '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/AbstractMessage.php b/vendor/react/http/src/Io/AbstractMessage.php deleted file mode 100644 index a0706bb..0000000 --- a/vendor/react/http/src/Io/AbstractMessage.php +++ /dev/null @@ -1,172 +0,0 @@ -@,;:\\\"\/\[\]?={}\x00-\x20\x7F]++):[\x20\x09]*+((?:[\x20\x09]*+[\x21-\x7E\x80-\xFF]++)*+)[\x20\x09]*+[\r]?+\n/m'; - - /** @var array */ - private $headers = array(); - - /** @var array */ - private $headerNamesLowerCase = array(); - - /** @var string */ - private $protocolVersion; - - /** @var StreamInterface */ - private $body; - - /** - * @param string $protocolVersion - * @param array $headers - * @param StreamInterface $body - */ - protected function __construct($protocolVersion, array $headers, StreamInterface $body) - { - foreach ($headers as $name => $value) { - if ($value !== array()) { - if (\is_array($value)) { - foreach ($value as &$one) { - $one = (string) $one; - } - } else { - $value = array((string) $value); - } - - $lower = \strtolower($name); - if (isset($this->headerNamesLowerCase[$lower])) { - $value = \array_merge($this->headers[$this->headerNamesLowerCase[$lower]], $value); - unset($this->headers[$this->headerNamesLowerCase[$lower]]); - } - - $this->headers[$name] = $value; - $this->headerNamesLowerCase[$lower] = $name; - } - } - - $this->protocolVersion = (string) $protocolVersion; - $this->body = $body; - } - - public function getProtocolVersion() - { - return $this->protocolVersion; - } - - public function withProtocolVersion($version) - { - if ((string) $version === $this->protocolVersion) { - return $this; - } - - $message = clone $this; - $message->protocolVersion = (string) $version; - - return $message; - } - - public function getHeaders() - { - return $this->headers; - } - - public function hasHeader($name) - { - return isset($this->headerNamesLowerCase[\strtolower($name)]); - } - - public function getHeader($name) - { - $lower = \strtolower($name); - return isset($this->headerNamesLowerCase[$lower]) ? $this->headers[$this->headerNamesLowerCase[$lower]] : array(); - } - - public function getHeaderLine($name) - { - return \implode(', ', $this->getHeader($name)); - } - - public function withHeader($name, $value) - { - if ($value === array()) { - return $this->withoutHeader($name); - } elseif (\is_array($value)) { - foreach ($value as &$one) { - $one = (string) $one; - } - } else { - $value = array((string) $value); - } - - $lower = \strtolower($name); - if (isset($this->headerNamesLowerCase[$lower]) && $this->headerNamesLowerCase[$lower] === (string) $name && $this->headers[$this->headerNamesLowerCase[$lower]] === $value) { - return $this; - } - - $message = clone $this; - if (isset($message->headerNamesLowerCase[$lower])) { - unset($message->headers[$message->headerNamesLowerCase[$lower]]); - } - - $message->headers[$name] = $value; - $message->headerNamesLowerCase[$lower] = $name; - - return $message; - } - - public function withAddedHeader($name, $value) - { - if ($value === array()) { - return $this; - } - - return $this->withHeader($name, \array_merge($this->getHeader($name), \is_array($value) ? $value : array($value))); - } - - public function withoutHeader($name) - { - $lower = \strtolower($name); - if (!isset($this->headerNamesLowerCase[$lower])) { - return $this; - } - - $message = clone $this; - unset($message->headers[$message->headerNamesLowerCase[$lower]], $message->headerNamesLowerCase[$lower]); - - return $message; - } - - public function getBody() - { - return $this->body; - } - - public function withBody(StreamInterface $body) - { - if ($body === $this->body) { - return $this; - } - - $message = clone $this; - $message->body = $body; - - return $message; - } -} diff --git a/vendor/react/http/src/Io/AbstractRequest.php b/vendor/react/http/src/Io/AbstractRequest.php deleted file mode 100644 index f32307f..0000000 --- a/vendor/react/http/src/Io/AbstractRequest.php +++ /dev/null @@ -1,156 +0,0 @@ - $headers - * @param StreamInterface $body - * @param string unknown $protocolVersion - */ - protected function __construct( - $method, - $uri, - array $headers, - StreamInterface $body, - $protocolVersion - ) { - if (\is_string($uri)) { - $uri = new Uri($uri); - } elseif (!$uri instanceof UriInterface) { - throw new \InvalidArgumentException( - 'Argument #2 ($uri) expected string|Psr\Http\Message\UriInterface' - ); - } - - // assign default `Host` request header from URI unless already given explicitly - $host = $uri->getHost(); - if ($host !== '') { - foreach ($headers as $name => $value) { - if (\strtolower($name) === 'host' && $value !== array()) { - $host = ''; - break; - } - } - if ($host !== '') { - $port = $uri->getPort(); - if ($port !== null && (!($port === 80 && $uri->getScheme() === 'http') || !($port === 443 && $uri->getScheme() === 'https'))) { - $host .= ':' . $port; - } - - $headers = array('Host' => $host) + $headers; - } - } - - parent::__construct($protocolVersion, $headers, $body); - - $this->method = $method; - $this->uri = $uri; - } - - public function getRequestTarget() - { - if ($this->requestTarget !== null) { - return $this->requestTarget; - } - - $target = $this->uri->getPath(); - if ($target === '') { - $target = '/'; - } - if (($query = $this->uri->getQuery()) !== '') { - $target .= '?' . $query; - } - - return $target; - } - - public function withRequestTarget($requestTarget) - { - if ((string) $requestTarget === $this->requestTarget) { - return $this; - } - - $request = clone $this; - $request->requestTarget = (string) $requestTarget; - - return $request; - } - - public function getMethod() - { - return $this->method; - } - - public function withMethod($method) - { - if ((string) $method === $this->method) { - return $this; - } - - $request = clone $this; - $request->method = (string) $method; - - return $request; - } - - public function getUri() - { - return $this->uri; - } - - public function withUri(UriInterface $uri, $preserveHost = false) - { - if ($uri === $this->uri) { - return $this; - } - - $request = clone $this; - $request->uri = $uri; - - $host = $uri->getHost(); - $port = $uri->getPort(); - if ($port !== null && $host !== '' && (!($port === 80 && $uri->getScheme() === 'http') || !($port === 443 && $uri->getScheme() === 'https'))) { - $host .= ':' . $port; - } - - // update `Host` request header if URI contains a new host and `$preserveHost` is false - if ($host !== '' && (!$preserveHost || $request->getHeaderLine('Host') === '')) { - // first remove all headers before assigning `Host` header to ensure it always comes first - foreach (\array_keys($request->getHeaders()) as $name) { - $request = $request->withoutHeader($name); - } - - // add `Host` header first, then all other original headers - $request = $request->withHeader('Host', $host); - foreach ($this->withoutHeader('Host')->getHeaders() as $name => $value) { - $request = $request->withHeader($name, $value); - } - } - - return $request; - } -} diff --git a/vendor/react/http/src/Io/BufferedBody.php b/vendor/react/http/src/Io/BufferedBody.php deleted file mode 100644 index 4a4d839..0000000 --- a/vendor/react/http/src/Io/BufferedBody.php +++ /dev/null @@ -1,179 +0,0 @@ -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 deleted file mode 100644 index 2f58f42..0000000 --- a/vendor/react/http/src/Io/ChunkedDecoder.php +++ /dev/null @@ -1,175 +0,0 @@ -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 deleted file mode 100644 index c84ef54..0000000 --- a/vendor/react/http/src/Io/ChunkedEncoder.php +++ /dev/null @@ -1,92 +0,0 @@ -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/ClientConnectionManager.php b/vendor/react/http/src/Io/ClientConnectionManager.php deleted file mode 100644 index faac98b..0000000 --- a/vendor/react/http/src/Io/ClientConnectionManager.php +++ /dev/null @@ -1,137 +0,0 @@ -connector = $connector; - $this->loop = $loop; - } - - /** - * @return PromiseInterface - */ - public function connect(UriInterface $uri) - { - $scheme = $uri->getScheme(); - if ($scheme !== 'https' && $scheme !== 'http') { - return \React\Promise\reject(new \InvalidArgumentException( - 'Invalid request URL given' - )); - } - - $port = $uri->getPort(); - if ($port === null) { - $port = $scheme === 'https' ? 443 : 80; - } - $uri = ($scheme === 'https' ? 'tls://' : '') . $uri->getHost() . ':' . $port; - - // Reuse idle connection for same URI if available - foreach ($this->idleConnections as $id => $connection) { - if ($this->idleUris[$id] === $uri) { - assert($this->idleStreamHandlers[$id] instanceof \Closure); - $connection->removeListener('close', $this->idleStreamHandlers[$id]); - $connection->removeListener('data', $this->idleStreamHandlers[$id]); - $connection->removeListener('error', $this->idleStreamHandlers[$id]); - - assert($this->idleTimers[$id] instanceof TimerInterface); - $this->loop->cancelTimer($this->idleTimers[$id]); - unset($this->idleUris[$id], $this->idleConnections[$id], $this->idleTimers[$id], $this->idleStreamHandlers[$id]); - - return \React\Promise\resolve($connection); - } - } - - // Create new connection if no idle connection to same URI is available - return $this->connector->connect($uri); - } - - /** - * Hands back an idle connection to the connection manager for possible future reuse. - * - * @return void - */ - public function keepAlive(UriInterface $uri, ConnectionInterface $connection) - { - $scheme = $uri->getScheme(); - assert($scheme === 'https' || $scheme === 'http'); - - $port = $uri->getPort(); - if ($port === null) { - $port = $scheme === 'https' ? 443 : 80; - } - - $this->idleUris[] = ($scheme === 'https' ? 'tls://' : '') . $uri->getHost() . ':' . $port; - $this->idleConnections[] = $connection; - - $that = $this; - $cleanUp = function () use ($connection, $that) { - // call public method to support legacy PHP 5.3 - $that->cleanUpConnection($connection); - }; - - // clean up and close connection when maximum time to keep-alive idle connection has passed - $this->idleTimers[] = $this->loop->addTimer($this->maximumTimeToKeepAliveIdleConnection, $cleanUp); - - // clean up and close connection when unexpected close/data/error event happens during idle time - $this->idleStreamHandlers[] = $cleanUp; - $connection->on('close', $cleanUp); - $connection->on('data', $cleanUp); - $connection->on('error', $cleanUp); - } - - /** - * @internal - * @return void - */ - public function cleanUpConnection(ConnectionInterface $connection) // private (PHP 5.4+) - { - $id = \array_search($connection, $this->idleConnections, true); - if ($id === false) { - return; - } - - assert(\is_int($id)); - assert($this->idleTimers[$id] instanceof TimerInterface); - $this->loop->cancelTimer($this->idleTimers[$id]); - unset($this->idleUris[$id], $this->idleConnections[$id], $this->idleTimers[$id], $this->idleStreamHandlers[$id]); - - $connection->close(); - } -} diff --git a/vendor/react/http/src/Io/ClientRequestState.php b/vendor/react/http/src/Io/ClientRequestState.php deleted file mode 100644 index 73a63a1..0000000 --- a/vendor/react/http/src/Io/ClientRequestState.php +++ /dev/null @@ -1,16 +0,0 @@ -connectionManager = $connectionManager; - $this->request = $request; - } - - public function isWritable() - { - return self::STATE_END > $this->state && !$this->ended; - } - - private function writeHead() - { - $this->state = self::STATE_WRITING_HEAD; - - $expected = 0; - $headers = "{$this->request->getMethod()} {$this->request->getRequestTarget()} HTTP/{$this->request->getProtocolVersion()}\r\n"; - foreach ($this->request->getHeaders() as $name => $values) { - if (\strpos($name, ':') !== false) { - $expected = -1; - break; - } - foreach ($values as $value) { - $headers .= "$name: $value\r\n"; - ++$expected; - } - } - - /** @var array $m legacy PHP 5.3 only */ - if (!\preg_match('#^\S+ \S+ HTTP/1\.[01]\r\n#m', $headers) || \substr_count($headers, "\n") !== ($expected + 1) || (\PHP_VERSION_ID >= 50400 ? \preg_match_all(AbstractMessage::REGEX_HEADERS, $headers) : \preg_match_all(AbstractMessage::REGEX_HEADERS, $headers, $m)) !== $expected) { - $this->closeError(new \InvalidArgumentException('Unable to send request with invalid request headers')); - return; - } - - $connectionRef = &$this->connection; - $stateRef = &$this->state; - $pendingWrites = &$this->pendingWrites; - $that = $this; - - $promise = $this->connectionManager->connect($this->request->getUri()); - $promise->then( - function (ConnectionInterface $connection) use ($headers, &$connectionRef, &$stateRef, &$pendingWrites, $that) { - $connectionRef = $connection; - assert($connectionRef instanceof ConnectionInterface); - - $connection->on('drain', array($that, 'handleDrain')); - $connection->on('data', array($that, 'handleData')); - $connection->on('end', array($that, 'handleEnd')); - $connection->on('error', array($that, 'handleError')); - $connection->on('close', array($that, 'close')); - - $more = $connection->write($headers . "\r\n" . $pendingWrites); - - assert($stateRef === ClientRequestStream::STATE_WRITING_HEAD); - $stateRef = ClientRequestStream::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->connection->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) - $eom = \strpos($this->buffer, "\r\n\r\n"); - $eomLegacy = \strpos($this->buffer, "\n\n"); - if ($eom !== false || $eomLegacy !== false) { - try { - if ($eom !== false && ($eomLegacy === false || $eom < $eomLegacy)) { - $response = Response::parseMessage(\substr($this->buffer, 0, $eom + 2)); - $bodyChunk = (string) \substr($this->buffer, $eom + 4); - } else { - $response = Response::parseMessage(\substr($this->buffer, 0, $eomLegacy + 1)); - $bodyChunk = (string) \substr($this->buffer, $eomLegacy + 2); - } - } catch (\InvalidArgumentException $exception) { - $this->closeError($exception); - return; - } - - // response headers successfully received => remove listeners for connection events - $connection = $this->connection; - assert($connection instanceof ConnectionInterface); - $connection->removeListener('drain', array($this, 'handleDrain')); - $connection->removeListener('data', array($this, 'handleData')); - $connection->removeListener('end', array($this, 'handleEnd')); - $connection->removeListener('error', array($this, 'handleError')); - $connection->removeListener('close', array($this, 'close')); - $this->connection = null; - $this->buffer = ''; - - // take control over connection handling and check if we can reuse the connection once response body closes - $that = $this; - $request = $this->request; - $connectionManager = $this->connectionManager; - $successfulEndReceived = false; - $input = $body = new CloseProtectionStream($connection); - $input->on('close', function () use ($connection, $that, $connectionManager, $request, $response, &$successfulEndReceived) { - // only reuse connection after successful response and both request and response allow keep alive - if ($successfulEndReceived && $connection->isReadable() && $that->hasMessageKeepAliveEnabled($response) && $that->hasMessageKeepAliveEnabled($request)) { - $connectionManager->keepAlive($request->getUri(), $connection); - } else { - $connection->close(); - } - - $that->close(); - }); - - // determine length of response body - $length = null; - $code = $response->getStatusCode(); - if ($this->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'); - } - $response = $response->withBody($body = new ReadableBodyStream($body, $length)); - $body->on('end', function () use (&$successfulEndReceived) { - $successfulEndReceived = true; - }); - - // emit response with streaming response body (see `Sender`) - $this->emit('response', array($response, $body)); - - // re-emit HTTP response body to trigger body parsing if parts of it are buffered - if ($bodyChunk !== '') { - $input->handleData($bodyChunk); - } elseif ($length === 0) { - $input->handleEnd(); - } - } - } - - /** @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 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 = ''; - $this->buffer = ''; - - if ($this->connection instanceof ConnectionInterface) { - $this->connection->close(); - $this->connection = null; - } - - $this->emit('close'); - $this->removeAllListeners(); - } - - /** - * @internal - * @return bool - * @link https://www.rfc-editor.org/rfc/rfc9112#section-9.3 - * @link https://www.rfc-editor.org/rfc/rfc7230#section-6.1 - */ - public function hasMessageKeepAliveEnabled(MessageInterface $message) - { - // @link https://www.rfc-editor.org/rfc/rfc9110#section-7.6.1 - $connectionOptions = \array_map('trim', \explode(',', \strtolower($message->getHeaderLine('Connection')))); - - if (\in_array('close', $connectionOptions, true)) { - return false; - } - - if ($message->getProtocolVersion() === '1.1') { - return true; - } - - if (\in_array('keep-alive', $connectionOptions, true)) { - return true; - } - - return false; - } -} diff --git a/vendor/react/http/src/Io/Clock.php b/vendor/react/http/src/Io/Clock.php deleted file mode 100644 index 92c1cb0..0000000 --- a/vendor/react/http/src/Io/Clock.php +++ /dev/null @@ -1,54 +0,0 @@ -loop = $loop; - } - - /** @return float */ - public function now() - { - if ($this->now === null) { - $this->now = \microtime(true); - - // remember clock for current loop tick only and update on next tick - $now =& $this->now; - $this->loop->futureTick(function () use (&$now) { - assert($now !== null); - $now = null; - }); - } - - return $this->now; - } -} diff --git a/vendor/react/http/src/Io/CloseProtectionStream.php b/vendor/react/http/src/Io/CloseProtectionStream.php deleted file mode 100644 index 2e1ed6e..0000000 --- a/vendor/react/http/src/Io/CloseProtectionStream.php +++ /dev/null @@ -1,111 +0,0 @@ -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 deleted file mode 100644 index 5056219..0000000 --- a/vendor/react/http/src/Io/EmptyBodyStream.php +++ /dev/null @@ -1,142 +0,0 @@ -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 deleted file mode 100644 index 25d15a1..0000000 --- a/vendor/react/http/src/Io/HttpBodyStream.php +++ /dev/null @@ -1,182 +0,0 @@ -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 deleted file mode 100644 index 612aae2..0000000 --- a/vendor/react/http/src/Io/IniUtil.php +++ /dev/null @@ -1,48 +0,0 @@ -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 deleted file mode 100644 index dedf6ff..0000000 --- a/vendor/react/http/src/Io/MiddlewareRunner.php +++ /dev/null @@ -1,61 +0,0 @@ -middleware = \array_values($middleware); - } - - /** - * @param ServerRequestInterface $request - * @return ResponseInterface|PromiseInterface - * @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 deleted file mode 100644 index 539107a..0000000 --- a/vendor/react/http/src/Io/MultipartParser.php +++ /dev/null @@ -1,345 +0,0 @@ -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; - - $this->maxMultipartBodyParts = $this->maxInputVars + $this->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->multipartBodyPartCount = 0; - $this->cursor = 0; - $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) - $this->cursor = \strpos($buffer, $boundary . "\r\n"); - - while ($this->cursor !== false) { - // search following boundary (preceded by newline) - // ignore last if not followed by boundary (SHOULD end with "--") - $this->cursor += $len + 2; - $end = \strpos($buffer, "\r\n" . $boundary, $this->cursor); - if ($end === false) { - break; - } - - // parse one part and continue searching for next - $this->parsePart(\substr($buffer, $this->cursor, $end - $this->cursor)); - $this->cursor = $end; - - if (++$this->multipartBodyPartCount > $this->maxMultipartBodyParts) { - break; - } - } - } - - 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 deleted file mode 100644 index fb5ed45..0000000 --- a/vendor/react/http/src/Io/PauseBufferStream.php +++ /dev/null @@ -1,188 +0,0 @@ -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 deleted file mode 100644 index daef45f..0000000 --- a/vendor/react/http/src/Io/ReadableBodyStream.php +++ /dev/null @@ -1,153 +0,0 @@ -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 deleted file mode 100644 index 8975ce5..0000000 --- a/vendor/react/http/src/Io/RequestHeaderParser.php +++ /dev/null @@ -1,179 +0,0 @@ -> */ - private $connectionParams = array(); - - public function __construct(Clock $clock) - { - $this->clock = $clock; - } - - 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 - ); - } 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 ConnectionInterface $connection - * @return ServerRequestInterface - * @throws \InvalidArgumentException - * @internal - */ - public function parseRequest($headers, ConnectionInterface $connection) - { - // reuse same connection params for all server params for this connection - $cid = \PHP_VERSION_ID < 70200 ? \spl_object_hash($connection) : \spl_object_id($connection); - if (isset($this->connectionParams[$cid])) { - $serverParams = $this->connectionParams[$cid]; - } else { - // assign new server params for new connection - $serverParams = array(); - - // scheme is `http` unless TLS is used - $localSocketUri = $connection->getLocalAddress(); - $localParts = $localSocketUri === null ? array() : \parse_url($localSocketUri); - if (isset($localParts['scheme']) && $localParts['scheme'] === 'tls') { - $serverParams['HTTPS'] = 'on'; - } - - // 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']; - } - - // apply REMOTE_ADDR and REMOTE_PORT if source address is known - // address should always be known, unless this is over Unix domain sockets (UDS) - $remoteSocketUri = $connection->getRemoteAddress(); - if ($remoteSocketUri !== null) { - $remoteAddress = \parse_url($remoteSocketUri); - $serverParams['REMOTE_ADDR'] = $remoteAddress['host']; - $serverParams['REMOTE_PORT'] = $remoteAddress['port']; - } - - // remember server params for all requests from this connection, reset on connection close - $this->connectionParams[$cid] = $serverParams; - $params =& $this->connectionParams; - $connection->on('close', function () use (&$params, $cid) { - assert(\is_array($params)); - unset($params[$cid]); - }); - } - - // create new obj implementing ServerRequestInterface by preserving all - // previous properties and restoring original request-target - $serverParams['REQUEST_TIME'] = (int) ($now = $this->clock->now()); - $serverParams['REQUEST_TIME_FLOAT'] = $now; - - return ServerRequest::parseMessage($headers, $serverParams); - } -} diff --git a/vendor/react/http/src/Io/Sender.php b/vendor/react/http/src/Io/Sender.php deleted file mode 100644 index 5f456b2..0000000 --- a/vendor/react/http/src/Io/Sender.php +++ /dev/null @@ -1,152 +0,0 @@ -http = $http; - } - - /** - * - * @internal - * @param RequestInterface $request - * @return PromiseInterface Promise - */ - public function send(RequestInterface $request) - { - // support HTTP/1.1 and HTTP/1.0 only, ensured by `Browser` already - assert(\in_array($request->getProtocolVersion(), array('1.0', '1.1'), true)); - - $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 && $size !== 0 && $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; - } - - // automatically add `Authorization: Basic …` request header if URL includes `user:pass@host` - if ($request->getUri()->getUserInfo() !== '' && !$request->hasHeader('Authorization')) { - $request = $request->withHeader('Authorization', 'Basic ' . \base64_encode($request->getUri()->getUserInfo())); - } - - $requestStream = $this->http->request($request); - - $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) use ($deferred, $request) { - $deferred->resolve($response); - }); - - 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 deleted file mode 100644 index 143edaa..0000000 --- a/vendor/react/http/src/Io/StreamingServer.php +++ /dev/null @@ -1,405 +0,0 @@ - '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; - - /** @var Clock */ - private $clock; - - /** - * 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->callback = $requestHandler; - $this->clock = new Clock($loop); - $this->parser = new RequestHeaderParser($this->clock); - - $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 - $connectionOnCloseResponseCancelerHandler = function () {}; - if ($response instanceof PromiseInterface && \method_exists($response, 'cancel')) { - $connectionOnCloseResponseCanceler = function () use ($response) { - $response->cancel(); - }; - $connectionOnCloseResponseCancelerHandler = function () use ($connectionOnCloseResponseCanceler, $conn) { - if ($connectionOnCloseResponseCanceler !== null) { - $conn->removeListener('close', $connectionOnCloseResponseCanceler); - } - }; - $conn->on('close', $connectionOnCloseResponseCanceler); - } - - // 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); - } - )->then($connectionOnCloseResponseCancelerHandler, $connectionOnCloseResponseCancelerHandler); - } - - /** @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', (int) $this->clock->now()) . ' 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 ($method === 'HEAD' && $response->hasHeader('Content-Length')) { - // HEAD Request: preserve explicit 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 - $expected = 0; - $headers = "HTTP/" . $version . " " . $code . " " . $response->getReasonPhrase() . "\r\n"; - foreach ($response->getHeaders() as $name => $values) { - if (\strpos($name, ':') !== false) { - $expected = -1; - break; - } - foreach ($values as $value) { - $headers .= $name . ": " . $value . "\r\n"; - ++$expected; - } - } - - /** @var array $m legacy PHP 5.3 only */ - if ($code < 100 || $code > 999 || \substr_count($headers, "\n") !== ($expected + 1) || (\PHP_VERSION_ID >= 50400 ? \preg_match_all(AbstractMessage::REGEX_HEADERS, $headers) : \preg_match_all(AbstractMessage::REGEX_HEADERS, $headers, $m)) !== $expected) { - $this->emit('error', array(new \InvalidArgumentException('Unable to send response with invalid response headers'))); - $this->writeError($connection, Response::STATUS_INTERNAL_SERVER_ERROR, $request); - return; - } - - // 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 deleted file mode 100644 index 64738f5..0000000 --- a/vendor/react/http/src/Io/Transaction.php +++ /dev/null @@ -1,330 +0,0 @@ -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) - { - $state = new ClientRequestState(); - $deferred = new Deferred(function () use ($state) { - if ($state->pending !== null) { - $state->pending->cancel(); - $state->pending = null; - } - }); - - // 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, $state)->then( - function (ResponseInterface $response) use ($state, $deferred, $loop, &$timeout) { - if ($state->timeout !== null) { - $loop->cancelTimer($state->timeout); - $state->timeout = null; - } - $timeout = -1; - $deferred->resolve($response); - }, - function ($e) use ($state, $deferred, $loop, &$timeout) { - if ($state->timeout !== null) { - $loop->cancelTimer($state->timeout); - $state->timeout = null; - } - $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, $state, &$timeout) { - if ($timeout >= 0) { - $that->applyTimeout($deferred, $state, $timeout); - } - }); - } else { - $this->applyTimeout($deferred, $state, $timeout); - } - - return $deferred->promise(); - } - - /** - * @internal - * @param number $timeout - * @return void - */ - public function applyTimeout(Deferred $deferred, ClientRequestState $state, $timeout) - { - $state->timeout = $this->loop->addTimer($timeout, function () use ($timeout, $deferred, $state) { - $deferred->reject(new \RuntimeException( - 'Request timed out after ' . $timeout . ' seconds' - )); - if ($state->pending !== null) { - $state->pending->cancel(); - $state->pending = null; - } - }); - } - - private function next(RequestInterface $request, Deferred $deferred, ClientRequestState $state) - { - $this->progress('request', array($request)); - - $that = $this; - ++$state->numRequests; - - $promise = $this->sender->send($request); - - if (!$this->streaming) { - $promise = $promise->then(function ($response) use ($deferred, $state, $that) { - return $that->bufferResponse($response, $deferred, $state); - }); - } - - $state->pending = $promise; - - return $promise->then( - function (ResponseInterface $response) use ($request, $that, $deferred, $state) { - return $that->onResponse($response, $request, $deferred, $state); - } - ); - } - - /** - * @internal - * @return PromiseInterface Promise - */ - public function bufferResponse(ResponseInterface $response, Deferred $deferred, ClientRequestState $state) - { - $body = $response->getBody(); - $size = $body->getSize(); - - if ($size !== null && $size > $this->maximumSize) { - $body->close(); - return \React\Promise\reject(new \OverflowException( - 'Response body size of ' . $size . ' bytes exceeds maximum of ' . $this->maximumSize . ' bytes', - \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90 - )); - } - - // body is not streaming => already buffered - if (!$body instanceof ReadableStreamInterface) { - return \React\Promise\resolve($response); - } - - /** @var ?\Closure $closer */ - $closer = null; - $maximumSize = $this->maximumSize; - - return $state->pending = new Promise(function ($resolve, $reject) use ($body, $maximumSize, $response, &$closer) { - // resolve with current buffer when stream closes successfully - $buffer = ''; - $body->on('close', $closer = function () use (&$buffer, $response, $maximumSize, $resolve, $reject) { - $resolve($response->withBody(new BufferedBody($buffer))); - }); - - // buffer response body data in memory - $body->on('data', function ($data) use (&$buffer, $maximumSize, $body, $closer, $reject) { - $buffer .= $data; - - // close stream and reject promise if limit is exceeded - if (isset($buffer[$maximumSize])) { - $buffer = ''; - assert($closer instanceof \Closure); - $body->removeListener('close', $closer); - $body->close(); - - $reject(new \OverflowException( - 'Response body size exceeds maximum of ' . $maximumSize . ' bytes', - \defined('SOCKET_EMSGSIZE') ? \SOCKET_EMSGSIZE : 90 - )); - } - }); - - // reject buffering if body emits error - $body->on('error', function (\Exception $e) use ($reject) { - $reject(new \RuntimeException( - 'Error while buffering response body: ' . $e->getMessage(), - $e->getCode(), - $e - )); - }); - }, function () use ($body, &$closer) { - // cancelled buffering: remove close handler to avoid resolving, then close and reject - assert($closer instanceof \Closure); - $body->removeListener('close', $closer); - $body->close(); - - throw new \RuntimeException('Cancelled buffering response body'); - }); - } - - /** - * @internal - * @throws ResponseException - * @return ResponseInterface|PromiseInterface - */ - public function onResponse(ResponseInterface $response, RequestInterface $request, Deferred $deferred, ClientRequestState $state) - { - $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, $state); - } - - // 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 - * @param Deferred $deferred - * @param ClientRequestState $state - * @return PromiseInterface - * @throws \RuntimeException - */ - private function onResponseRedirect(ResponseInterface $response, RequestInterface $request, Deferred $deferred, ClientRequestState $state) - { - // resolve location relative to last request URI - $location = Uri::resolve($request->getUri(), new Uri($response->getHeaderLine('Location'))); - - $request = $this->makeRedirectRequest($request, $location, $response->getStatusCode()); - $this->progress('redirect', array($request)); - - if ($state->numRequests >= $this->maxRedirects) { - throw new \RuntimeException('Maximum number of redirects (' . $this->maxRedirects . ') exceeded'); - } - - return $this->next($request, $deferred, $state); - } - - /** - * @param RequestInterface $request - * @param UriInterface $location - * @param int $statusCode - * @return RequestInterface - * @throws \RuntimeException - */ - private function makeRedirectRequest(RequestInterface $request, UriInterface $location, $statusCode) - { - // Remove authorization if changing hostnames (but not if just changing ports or protocols). - $originalHost = $request->getUri()->getHost(); - if ($location->getHost() !== $originalHost) { - $request = $request->withoutHeader('Authorization'); - } - - $request = $request->withoutHeader('Host')->withUri($location); - - if ($statusCode === Response::STATUS_TEMPORARY_REDIRECT || $statusCode === Response::STATUS_PERMANENT_REDIRECT) { - if ($request->getBody() instanceof ReadableStreamInterface) { - throw new \RuntimeException('Unable to redirect request with streaming body'); - } - } else { - $request = $request - ->withMethod($request->getMethod() === 'HEAD' ? 'HEAD' : 'GET') - ->withoutHeader('Content-Type') - ->withoutHeader('Content-Length') - ->withBody(new BufferedBody('')); - } - - return $request; - } - - 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 deleted file mode 100644 index f2a6c9e..0000000 --- a/vendor/react/http/src/Io/UploadedFile.php +++ /dev/null @@ -1,130 +0,0 @@ -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/Request.php b/vendor/react/http/src/Message/Request.php deleted file mode 100644 index 3de8c1b..0000000 --- a/vendor/react/http/src/Message/Request.php +++ /dev/null @@ -1,57 +0,0 @@ - Internally, this implementation builds on top of a base class which is - * considered an implementation detail that may change in the future. - * - * @see RequestInterface - */ -final class Request extends AbstractRequest implements RequestInterface -{ - /** - * @param string $method HTTP method for the request. - * @param string|UriInterface $url URL for the request. - * @param array $headers Headers for the message. - * @param string|ReadableStreamInterface|StreamInterface $body Message body. - * @param string $version HTTP protocol version. - * @throws \InvalidArgumentException for an invalid URL or body - */ - public function __construct( - $method, - $url, - array $headers = array(), - $body = '', - $version = '1.1' - ) { - if (\is_string($body)) { - $body = new BufferedBody($body); - } elseif ($body instanceof ReadableStreamInterface && !$body instanceof StreamInterface) { - $body = new ReadableBodyStream($body); - } elseif (!$body instanceof StreamInterface) { - throw new \InvalidArgumentException('Invalid request body given'); - } - - parent::__construct($method, $url, $headers, $body, $version); - } -} diff --git a/vendor/react/http/src/Message/Response.php b/vendor/react/http/src/Message/Response.php deleted file mode 100644 index fa6366e..0000000 --- a/vendor/react/http/src/Message/Response.php +++ /dev/null @@ -1,414 +0,0 @@ - 'text/html' - * ), - * "Hello world!\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 a base class which is - * considered an implementation detail that may change in the future. - * - * @see \Psr\Http\Message\ResponseInterface - */ -final class Response extends AbstractMessage implements ResponseInterface, StatusCodeInterface -{ - /** - * Create an HTML response - * - * ```php - * $html = << - * - * Hello wörld! - * - * - * 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( - * "

Error

\n

Invalid user name given.

\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 = << - * - * Hello wörld! - * - * - * 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( - * "Invalid user name given.\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); - } - - /** - * @var bool - * @see self::$phrasesMap - */ - private static $phrasesInitialized = false; - - /** - * Map of standard HTTP status codes to standard reason phrases. - * - * This map will be fully populated with all standard reason phrases on - * first access. By default, it only contains a subset of HTTP status codes - * that have a custom mapping to reason phrases (such as those with dashes - * and all caps words). See `self::STATUS_*` for all possible status code - * constants. - * - * @var array - * @see self::STATUS_* - * @see self::getReasonPhraseForStatusCode() - */ - private static $phrasesMap = array( - 200 => 'OK', - 203 => 'Non-Authoritative Information', - 207 => 'Multi-Status', - 226 => 'IM Used', - 414 => 'URI Too Large', - 418 => 'I\'m a teapot', - 505 => 'HTTP Version Not Supported' - ); - - /** @var int */ - private $statusCode; - - /** @var string */ - private $reasonPhrase; - - /** - * @param int $status HTTP status code (e.g. 200/404), see `self::STATUS_*` constants - * @param array $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($version, $headers, $body); - - $this->statusCode = (int) $status; - $this->reasonPhrase = ($reason !== '' && $reason !== null) ? (string) $reason : self::getReasonPhraseForStatusCode($status); - } - - public function getStatusCode() - { - return $this->statusCode; - } - - public function withStatus($code, $reasonPhrase = '') - { - if ((string) $reasonPhrase === '') { - $reasonPhrase = self::getReasonPhraseForStatusCode($code); - } - - if ($this->statusCode === (int) $code && $this->reasonPhrase === (string) $reasonPhrase) { - return $this; - } - - $response = clone $this; - $response->statusCode = (int) $code; - $response->reasonPhrase = (string) $reasonPhrase; - - return $response; - } - - public function getReasonPhrase() - { - return $this->reasonPhrase; - } - - /** - * @param int $code - * @return string default reason phrase for given status code or empty string if unknown - */ - private static function getReasonPhraseForStatusCode($code) - { - if (!self::$phrasesInitialized) { - self::$phrasesInitialized = true; - - // map all `self::STATUS_` constants from status code to reason phrase - // e.g. `self::STATUS_NOT_FOUND = 404` will be mapped to `404 Not Found` - $ref = new \ReflectionClass(__CLASS__); - foreach ($ref->getConstants() as $name => $value) { - if (!isset(self::$phrasesMap[$value]) && \strpos($name, 'STATUS_') === 0) { - self::$phrasesMap[$value] = \ucwords(\strtolower(\str_replace('_', ' ', \substr($name, 7)))); - } - } - } - - return isset(self::$phrasesMap[$code]) ? self::$phrasesMap[$code] : ''; - } - - /** - * [Internal] Parse incoming HTTP protocol message - * - * @internal - * @param string $message - * @return self - * @throws \InvalidArgumentException if given $message is not a valid HTTP response message - */ - public static function parseMessage($message) - { - $start = array(); - if (!\preg_match('#^HTTP/(?\d\.\d) (?\d{3})(?: (?[^\r\n]*+))?[\r]?+\n#m', $message, $start)) { - throw new \InvalidArgumentException('Unable to parse invalid status-line'); - } - - // only support HTTP/1.1 and HTTP/1.0 requests - if ($start['version'] !== '1.1' && $start['version'] !== '1.0') { - throw new \InvalidArgumentException('Received response with invalid protocol version'); - } - - // check number of valid header fields matches number of lines + status line - $matches = array(); - $n = \preg_match_all(self::REGEX_HEADERS, $message, $matches, \PREG_SET_ORDER); - if (\substr_count($message, "\n") !== $n + 1) { - throw new \InvalidArgumentException('Unable to parse invalid response header fields'); - } - - // format all header fields into associative array - $headers = array(); - foreach ($matches as $match) { - $headers[$match[1]][] = $match[2]; - } - - return new self( - (int) $start['status'], - $headers, - '', - $start['version'], - isset($start['reason']) ? $start['reason'] : '' - ); - } -} diff --git a/vendor/react/http/src/Message/ResponseException.php b/vendor/react/http/src/Message/ResponseException.php deleted file mode 100644 index f4912c9..0000000 --- a/vendor/react/http/src/Message/ResponseException.php +++ /dev/null @@ -1,43 +0,0 @@ -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 deleted file mode 100644 index 32a0f62..0000000 --- a/vendor/react/http/src/Message/ServerRequest.php +++ /dev/null @@ -1,331 +0,0 @@ - Internally, this implementation builds on top of a base class which is - * considered an implementation detail that may change in the future. - * - * @see ServerRequestInterface - */ -final class ServerRequest extends AbstractRequest 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 $headers Headers for the message. - * @param string|ReadableStreamInterface|StreamInterface $body Message body. - * @param string $version HTTP protocol version. - * @param array $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() - ) { - if (\is_string($body)) { - $body = new BufferedBody($body); - } elseif ($body instanceof ReadableStreamInterface && !$body instanceof StreamInterface) { - $temp = new self($method, '', $headers); - $size = (int) $temp->getHeaderLine('Content-Length'); - if (\strtolower($temp->getHeaderLine('Transfer-Encoding')) === 'chunked') { - $size = null; - } - $body = new HttpBodyStream($body, $size); - } elseif (!$body instanceof StreamInterface) { - throw new \InvalidArgumentException('Invalid server request body given'); - } - - parent::__construct($method, $url, $headers, $body, $version); - - $this->serverParams = $serverParams; - - $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 = $nameValuePair[0]; - $value = \urldecode($nameValuePair[1]); - $result[$key] = $value; - } - } - - return $result; - } - - /** - * [Internal] Parse incoming HTTP protocol message - * - * @internal - * @param string $message - * @param array $serverParams - * @return self - * @throws \InvalidArgumentException if given $message is not a valid HTTP request message - */ - public static function parseMessage($message, array $serverParams) - { - // parse request line like "GET /path HTTP/1.1" - $start = array(); - if (!\preg_match('#^(?[^ ]+) (?[^ ]+) HTTP/(?\d\.\d)#m', $message, $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); - } - - // check number of valid header fields matches number of lines + request line - $matches = array(); - $n = \preg_match_all(self::REGEX_HEADERS, $message, $matches, \PREG_SET_ORDER); - if (\substr_count($message, "\n") !== $n + 1) { - throw new \InvalidArgumentException('Unable to parse invalid request header fields'); - } - - // format all header fields into associative array - $host = null; - $headers = array(); - foreach ($matches as $match) { - $headers[$match[1]][] = $match[2]; - - // match `Host` request header - if ($host === null && \strtolower($match[1]) === 'host') { - $host = $match[2]; - } - } - - // scheme is `http` unless TLS is used - $scheme = isset($serverParams['HTTPS']) ? 'https://' : 'http://'; - - // default host if unset comes from local socket address or defaults to localhost - $hasHost = $host !== null; - if ($host === null) { - $host = isset($serverParams['SERVER_ADDR'], $serverParams['SERVER_PORT']) ? $serverParams['SERVER_ADDR'] . ':' . $serverParams['SERVER_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']; - } - } - - $request = new self( - $start['method'], - $uri, - $headers, - '', - $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/Message/Uri.php b/vendor/react/http/src/Message/Uri.php deleted file mode 100644 index 1eaf24f..0000000 --- a/vendor/react/http/src/Message/Uri.php +++ /dev/null @@ -1,356 +0,0 @@ -scheme = \strtolower($parts['scheme']); - } - - if (isset($parts['user']) || isset($parts['pass'])) { - $this->userInfo = $this->encode(isset($parts['user']) ? $parts['user'] : '', \PHP_URL_USER) . (isset($parts['pass']) ? ':' . $this->encode($parts['pass'], \PHP_URL_PASS) : ''); - } - - if (isset($parts['host'])) { - $this->host = \strtolower($parts['host']); - } - - if (isset($parts['port']) && !(($parts['port'] === 80 && $this->scheme === 'http') || ($parts['port'] === 443 && $this->scheme === 'https'))) { - $this->port = $parts['port']; - } - - if (isset($parts['path'])) { - $this->path = $this->encode($parts['path'], \PHP_URL_PATH); - } - - if (isset($parts['query'])) { - $this->query = $this->encode($parts['query'], \PHP_URL_QUERY); - } - - if (isset($parts['fragment'])) { - $this->fragment = $this->encode($parts['fragment'], \PHP_URL_FRAGMENT); - } - } - - public function getScheme() - { - return $this->scheme; - } - - public function getAuthority() - { - if ($this->host === '') { - return ''; - } - - return ($this->userInfo !== '' ? $this->userInfo . '@' : '') . $this->host . ($this->port !== null ? ':' . $this->port : ''); - } - - public function getUserInfo() - { - return $this->userInfo; - } - - public function getHost() - { - return $this->host; - } - - public function getPort() - { - return $this->port; - } - - public function getPath() - { - return $this->path; - } - - public function getQuery() - { - return $this->query; - } - - public function getFragment() - { - return $this->fragment; - } - - public function withScheme($scheme) - { - $scheme = \strtolower($scheme); - if ($scheme === $this->scheme) { - return $this; - } - - if (!\preg_match('#^[a-z]*$#', $scheme)) { - throw new \InvalidArgumentException('Invalid URI scheme given'); - } - - $new = clone $this; - $new->scheme = $scheme; - - if (($this->port === 80 && $scheme === 'http') || ($this->port === 443 && $scheme === 'https')) { - $new->port = null; - } - - return $new; - } - - public function withUserInfo($user, $password = null) - { - $userInfo = $this->encode($user, \PHP_URL_USER) . ($password !== null ? ':' . $this->encode($password, \PHP_URL_PASS) : ''); - if ($userInfo === $this->userInfo) { - return $this; - } - - $new = clone $this; - $new->userInfo = $userInfo; - - return $new; - } - - public function withHost($host) - { - $host = \strtolower($host); - if ($host === $this->host) { - return $this; - } - - if (\preg_match('#[\s%+]#', $host) || ($host !== '' && \parse_url('http://' . $host, \PHP_URL_HOST) !== $host)) { - throw new \InvalidArgumentException('Invalid URI host given'); - } - - $new = clone $this; - $new->host = $host; - - return $new; - } - - public function withPort($port) - { - $port = $port === null ? null : (int) $port; - if (($port === 80 && $this->scheme === 'http') || ($port === 443 && $this->scheme === 'https')) { - $port = null; - } - - if ($port === $this->port) { - return $this; - } - - if ($port !== null && ($port < 1 || $port > 0xffff)) { - throw new \InvalidArgumentException('Invalid URI port given'); - } - - $new = clone $this; - $new->port = $port; - - return $new; - } - - public function withPath($path) - { - $path = $this->encode($path, \PHP_URL_PATH); - if ($path === $this->path) { - return $this; - } - - $new = clone $this; - $new->path = $path; - - return $new; - } - - public function withQuery($query) - { - $query = $this->encode($query, \PHP_URL_QUERY); - if ($query === $this->query) { - return $this; - } - - $new = clone $this; - $new->query = $query; - - return $new; - } - - public function withFragment($fragment) - { - $fragment = $this->encode($fragment, \PHP_URL_FRAGMENT); - if ($fragment === $this->fragment) { - return $this; - } - - $new = clone $this; - $new->fragment = $fragment; - - return $new; - } - - public function __toString() - { - $uri = ''; - if ($this->scheme !== '') { - $uri .= $this->scheme . ':'; - } - - $authority = $this->getAuthority(); - if ($authority !== '') { - $uri .= '//' . $authority; - } - - if ($authority !== '' && isset($this->path[0]) && $this->path[0] !== '/') { - $uri .= '/' . $this->path; - } elseif ($authority === '' && isset($this->path[0]) && $this->path[0] === '/') { - $uri .= '/' . \ltrim($this->path, '/'); - } else { - $uri .= $this->path; - } - - if ($this->query !== '') { - $uri .= '?' . $this->query; - } - - if ($this->fragment !== '') { - $uri .= '#' . $this->fragment; - } - - return $uri; - } - - /** - * @param string $part - * @param int $component - * @return string - */ - private function encode($part, $component) - { - return \preg_replace_callback( - '/(?:[^a-z0-9_\-\.~!\$&\'\(\)\*\+,;=' . ($component === \PHP_URL_PATH ? ':@\/' : ($component === \PHP_URL_QUERY || $component === \PHP_URL_FRAGMENT ? ':@\/\?' : '')) . '%]++|%(?![a-f0-9]{2}))/i', - function (array $match) { - return \rawurlencode($match[0]); - }, - $part - ); - } - - /** - * [Internal] Resolve URI relative to base URI and return new absolute URI - * - * @internal - * @param UriInterface $base - * @param UriInterface $rel - * @return UriInterface - * @throws void - */ - public static function resolve(UriInterface $base, UriInterface $rel) - { - if ($rel->getScheme() !== '') { - return $rel->getPath() === '' ? $rel : $rel->withPath(self::removeDotSegments($rel->getPath())); - } - - $reset = false; - $new = $base; - if ($rel->getAuthority() !== '') { - $reset = true; - $userInfo = \explode(':', $rel->getUserInfo(), 2); - $new = $base->withUserInfo($userInfo[0], isset($userInfo[1]) ? $userInfo[1]: null)->withHost($rel->getHost())->withPort($rel->getPort()); - } - - if ($reset && $rel->getPath() === '') { - $new = $new->withPath(''); - } elseif (($path = $rel->getPath()) !== '') { - $start = ''; - if ($path === '' || $path[0] !== '/') { - $start = $base->getPath(); - if (\substr($start, -1) !== '/') { - $start .= '/../'; - } - } - $reset = true; - $new = $new->withPath(self::removeDotSegments($start . $path)); - } - if ($reset || $rel->getQuery() !== '') { - $reset = true; - $new = $new->withQuery($rel->getQuery()); - } - if ($reset || $rel->getFragment() !== '') { - $new = $new->withFragment($rel->getFragment()); - } - - return $new; - } - - /** - * @param string $path - * @return string - */ - private static function removeDotSegments($path) - { - $segments = array(); - foreach (\explode('/', $path) as $segment) { - if ($segment === '..') { - \array_pop($segments); - } elseif ($segment !== '.' && $segment !== '') { - $segments[] = $segment; - } - } - return '/' . \implode('/', $segments) . ($path !== '/' && \substr($path, -1) === '/' ? '/' : ''); - } -} diff --git a/vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php b/vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php deleted file mode 100644 index b1c00da..0000000 --- a/vendor/react/http/src/Middleware/LimitConcurrentRequestsMiddleware.php +++ /dev/null @@ -1,211 +0,0 @@ -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(null); - } -} diff --git a/vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php b/vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php deleted file mode 100644 index ddb39f5..0000000 --- a/vendor/react/http/src/Middleware/RequestBodyBufferMiddleware.php +++ /dev/null @@ -1,109 +0,0 @@ -sizeLimit = IniUtil::iniSizeToBytes($sizeLimit); - } - - public function __invoke(ServerRequestInterface $request, $next) - { - $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 || !$body->isReadable()) { - // 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 $next($request); - } - - // request body of known size exceeding limit - $sizeLimit = $this->sizeLimit; - if ($size > $this->sizeLimit) { - $sizeLimit = 0; - } - - /** @var ?\Closure $closer */ - $closer = null; - - return new Promise(function ($resolve, $reject) use ($body, &$closer, $sizeLimit, $request, $next) { - // buffer request body data in memory, discard but keep buffering if limit is reached - $buffer = ''; - $bufferer = null; - $body->on('data', $bufferer = function ($data) use (&$buffer, $sizeLimit, $body, &$bufferer) { - $buffer .= $data; - - // 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 (isset($buffer[$sizeLimit])) { - assert($bufferer instanceof \Closure); - $body->removeListener('data', $bufferer); - $bufferer = null; - $buffer = ''; - } - }); - - // call $next with current buffer and resolve or reject with its results - $body->on('close', $closer = function () use (&$buffer, $request, $resolve, $reject, $next) { - try { - // resolve with result of next handler - $resolve($next($request->withBody(new BufferedBody($buffer)))); - } catch (\Exception $e) { - $reject($e); - } catch (\Throwable $e) { // @codeCoverageIgnoreStart - // reject Errors just like Exceptions (PHP 7+) - $reject($e); // @codeCoverageIgnoreEnd - } - }); - - // reject buffering if body emits error - $body->on('error', function (\Exception $e) use ($reject, $body, $closer) { - // remove close handler to avoid resolving, then close and reject - assert($closer instanceof \Closure); - $body->removeListener('close', $closer); - $body->close(); - - $reject(new \RuntimeException( - 'Error while buffering request body: ' . $e->getMessage(), - $e->getCode(), - $e - )); - }); - }, function () use ($body, &$closer) { - // cancelled buffering: remove close handler to avoid resolving, then close and reject - assert($closer instanceof \Closure); - $body->removeListener('close', $closer); - $body->close(); - - throw new \RuntimeException('Cancelled buffering request body'); - }); - } -} diff --git a/vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php b/vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php deleted file mode 100644 index be5ba16..0000000 --- a/vendor/react/http/src/Middleware/RequestBodyParserMiddleware.php +++ /dev/null @@ -1,46 +0,0 @@ -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 deleted file mode 100644 index 6ab74b7..0000000 --- a/vendor/react/http/src/Middleware/StreamingRequestMiddleware.php +++ /dev/null @@ -1,69 +0,0 @@ -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 deleted file mode 100644 index 9bb9cf7..0000000 --- a/vendor/react/http/src/Server.php +++ /dev/null @@ -1,18 +0,0 @@ -connect($uri)->then(function (React\Socket\ConnectionInterface $conn) { - // … - }, function (Exception $e) { - echo 'Error:' . $e->getMessage() . PHP_EOL; - }); - ``` - -* Improve test suite, test against PHP 8.1 release. - (#274 by @SimonFrings) - -## 1.9.0 (2021-08-03) - -* Feature: Add new `SocketServer` and deprecate `Server` to avoid class name collisions. - (#263 by @clue) - - The new `SocketServer` class has been added with an improved constructor signature - as a replacement for the previous `Server` class in order to avoid any ambiguities. - The previous name has been deprecated and should not be used anymore. - In its most basic form, the deprecated `Server` can now be considered an alias for new `SocketServer`. - - ```php - // deprecated - $socket = new React\Socket\Server(0); - $socket = new React\Socket\Server('127.0.0.1:8000'); - $socket = new React\Socket\Server('127.0.0.1:8000', null, $context); - $socket = new React\Socket\Server('127.0.0.1:8000', $loop, $context); - - // new - $socket = new React\Socket\SocketServer('127.0.0.1:0'); - $socket = new React\Socket\SocketServer('127.0.0.1:8000'); - $socket = new React\Socket\SocketServer('127.0.0.1:8000', $context); - $socket = new React\Socket\SocketServer('127.0.0.1:8000', $context, $loop); - ``` - -* Feature: Update `Connector` signature to take optional `$context` as first argument. - (#264 by @clue) - - The new signature has been added to match the new `SocketServer` and - consistently move the now commonly unneeded loop argument to the last argument. - The previous signature has been deprecated and should not be used anymore. - In its most basic form, both signatures are compatible. - - ```php - // deprecated - $connector = new React\Socket\Connector(null, $context); - $connector = new React\Socket\Connector($loop, $context); - - // new - $connector = new React\Socket\Connector($context); - $connector = new React\Socket\Connector($context, $loop); - ``` - -## 1.8.0 (2021-07-11) - -A major new feature release, see [**release announcement**](https://clue.engineering/2021/announcing-reactphp-default-loop). - -* Feature: Simplify usage by supporting new [default loop](https://reactphp.org/event-loop/#loop). - (#260 by @clue) - - ```php - // old (still supported) - $socket = new React\Socket\Server('127.0.0.1:8080', $loop); - $connector = new React\Socket\Connector($loop); - - // new (using default loop) - $socket = new React\Socket\Server('127.0.0.1:8080'); - $connector = new React\Socket\Connector(); - ``` - -## 1.7.0 (2021-06-25) - -* Feature: Support falling back to multiple DNS servers from DNS config. - (#257 by @clue) - - If you're using the default `Connector`, it will now use all DNS servers - configured on your system. If you have multiple DNS servers configured and - connectivity to the primary DNS server is broken, it will now fall back to - your other DNS servers, thus providing improved connectivity and redundancy - for broken DNS configurations. - -* Feature: Use round robin for happy eyeballs DNS responses (load balancing). - (#247 by @clue) - - If you're using the default `Connector`, it will now randomize the order of - the IP addresses resolved via DNS when connecting. This allows the load to - be distributed more evenly across all returned IP addresses. This can be - used as a very basic DNS load balancing mechanism. - -* Internal improvement to avoid unhandled rejection for future Promise API. - (#258 by @clue) - -* Improve test suite, use GitHub actions for continuous integration (CI). - (#254 by @SimonFrings) - -## 1.6.0 (2020-08-28) - -* Feature: Support upcoming PHP 8 release. - (#246 by @clue) - -* Feature: Change default socket backlog size to 511. - (#242 by @clue) - -* Fix: Fix closing connection when cancelling during TLS handshake. - (#241 by @clue) - -* Fix: Fix blocking during possible `accept()` race condition - when multiple socket servers listen on same socket address. - (#244 by @clue) - -* Improve test suite, update PHPUnit config and add full core team to the license. - (#243 by @SimonFrings and #245 by @WyriHaximus) - -## 1.5.0 (2020-07-01) - -* Feature / Fix: Improve error handling and reporting for happy eyeballs and - immediately try next connection when one connection attempt fails. - (#230, #231, #232 and #233 by @clue) - - Error messages for failed connection attempts now include more details to - ease debugging. Additionally, the happy eyeballs algorithm has been improved - to avoid having to wait for some timers to expire which significantly - improves connection setup times (in particular when IPv6 isn't available). - -* Improve test suite, minor code cleanup and improve code coverage to 100%. - Update to PHPUnit 9 and skip legacy TLS 1.0 / TLS 1.1 tests if disabled by - system. Run tests on Windows and simplify Travis CI test matrix for Mac OS X - setup and skip all TLS tests on legacy HHVM. - (#229, #235, #236 and #238 by @clue and #239 by @SimonFrings) - -## 1.4.0 (2020-03-12) - -A major new feature release, see [**release announcement**](https://clue.engineering/2020/introducing-ipv6-for-reactphp). - -* Feature: Add IPv6 support to `Connector` (implement "Happy Eyeballs" algorithm to support IPv6 probing). - IPv6 support is turned on by default, use new `happy_eyeballs` option in `Connector` to toggle behavior. - (#196, #224 and #225 by @WyriHaximus and @clue) - -* Feature: Default to using DNS cache (with max 256 entries) for `Connector`. - (#226 by @clue) - -* Add `.gitattributes` to exclude dev files from exports and some minor code style fixes. - (#219 by @reedy and #218 by @mmoreram) - -* Improve test suite to fix failing test cases when using new DNS component, - significantly improve test performance by awaiting events instead of sleeping, - exclude TLS 1.3 test on PHP 7.3, run tests on PHP 7.4 and simplify test matrix. - (#208, #209, #210, #217 and #223 by @clue) - -## 1.3.0 (2019-07-10) - -* Feature: Forward compatibility with upcoming stable DNS component. - (#206 by @clue) - -## 1.2.1 (2019-06-03) - -* Avoid uneeded fragmented TLS work around for PHP 7.3.3+ and - work around failing test case detecting EOF on TLS 1.3 socket streams. - (#201 and #202 by @clue) - -* Improve TLS certificate/passphrase example. - (#190 by @jsor) - -## 1.2.0 (2019-01-07) - -* Feature / Fix: Improve TLS 1.3 support. - (#186 by @clue) - - TLS 1.3 is now an official standard as of August 2018! :tada: - The protocol has major improvements in the areas of security, performance, and privacy. - TLS 1.3 is supported by default as of [OpenSSL 1.1.1](https://www.openssl.org/blog/blog/2018/09/11/release111/). - For example, this version ships with Ubuntu 18.10 (and newer) by default, meaning that recent installations support TLS 1.3 out of the box :shipit: - -* Fix: Avoid possibility of missing remote address when TLS handshake fails. - (#188 by @clue) - -* Improve performance by prefixing all global functions calls with `\` to skip the look up and resolve process and go straight to the global function. - (#183 by @WyriHaximus) - -* Update documentation to use full class names with namespaces. - (#187 by @clue) - -* Improve test suite to avoid some possible race conditions, - test against PHP 7.3 on Travis and - use dedicated `assertInstanceOf()` assertions. - (#185 by @clue, #178 by @WyriHaximus and #181 by @carusogabriel) - -## 1.1.0 (2018-10-01) - -* Feature: Improve error reporting for failed connection attempts and improve - cancellation forwarding during DNS lookup, TCP/IP connection or TLS handshake. - (#168, #169, #170, #171, #176 and #177 by @clue) - - All error messages now always contain a reference to the remote URI to give - more details which connection actually failed and the reason for this error. - Accordingly, failures during DNS lookup will now mention both the remote URI - as well as the DNS error reason. TCP/IP connection issues and errors during - a secure TLS handshake will both mention the remote URI as well as the - underlying socket error. Similarly, lost/dropped connections during a TLS - handshake will now report a lost connection instead of an empty error reason. - - For most common use cases this means that simply reporting the `Exception` - message should give the most relevant details for any connection issues: - - ```php - $promise = $connector->connect('tls://example.com:443'); - $promise->then(function (ConnectionInterface $conn) use ($loop) { - // … - }, function (Exception $e) { - echo $e->getMessage(); - }); - ``` - -## 1.0.0 (2018-07-11) - -* First stable LTS release, now following [SemVer](https://semver.org/). - We'd like to emphasize that this component is production ready and battle-tested. - We plan to support all long-term support (LTS) releases for at least 24 months, - so you have a rock-solid foundation to build on top of. - -> Contains no other changes, so it's actually fully compatible with the v0.8.12 release. - -## 0.8.12 (2018-06-11) - -* Feature: Improve memory consumption for failed and cancelled connection attempts. - (#161 by @clue) - -* Improve test suite to fix Travis config to test against legacy PHP 5.3 again. - (#162 by @clue) - -## 0.8.11 (2018-04-24) - -* Feature: Improve memory consumption for cancelled connection attempts and - simplify skipping DNS lookup when connecting to IP addresses. - (#159 and #160 by @clue) - -## 0.8.10 (2018-02-28) - -* Feature: Update DNS dependency to support loading system default DNS - nameserver config on all supported platforms - (`/etc/resolv.conf` on Unix/Linux/Mac/Docker/WSL and WMIC on Windows) - (#152 by @clue) - - This means that connecting to hosts that are managed by a local DNS server, - such as a corporate DNS server or when using Docker containers, will now - work as expected across all platforms with no changes required: - - ```php - $connector = new Connector($loop); - $connector->connect('intranet.example:80')->then(function ($connection) { - // … - }); - ``` - -## 0.8.9 (2018-01-18) - -* Feature: Support explicitly choosing TLS version to negotiate with remote side - by respecting `crypto_method` context parameter for all classes. - (#149 by @clue) - - By default, all connector and server classes support TLSv1.0+ and exclude - support for legacy SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly - choose the TLS version you want to negotiate with the remote side: - - ```php - // new: now supports 'crypto_method` context parameter for all classes - $connector = new Connector($loop, array( - 'tls' => array( - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT - ) - )); - ``` - -* Minor internal clean up to unify class imports - (#148 by @clue) - -## 0.8.8 (2018-01-06) - -* Improve test suite by adding test group to skip integration tests relying on - internet connection and fix minor documentation typo. - (#146 by @clue and #145 by @cn007b) - -## 0.8.7 (2017-12-24) - -* Fix: Fix closing socket resource before removing from loop - (#141 by @clue) - - This fixes the root cause of an uncaught `Exception` that only manifested - itself after the recent Stream v0.7.4 component update and only if you're - using `ext-event` (`ExtEventLoop`). - -* Improve test suite by testing against PHP 7.2 - (#140 by @carusogabriel) - -## 0.8.6 (2017-11-18) - -* Feature: Add Unix domain socket (UDS) support to `Server` with `unix://` URI scheme - and add advanced `UnixServer` class. - (#120 by @andig) - - ```php - // new: Server now supports "unix://" scheme - $server = new Server('unix:///tmp/server.sock', $loop); - - // new: advanced usage - $server = new UnixServer('/tmp/server.sock', $loop); - ``` - -* Restructure examples to ease getting started - (#136 by @clue) - -* Improve test suite by adding forward compatibility with PHPUnit 6 and - ignore Mac OS X test failures for now until Travis tests work again - (#133 by @gabriel-caruso and #134 by @clue) - -## 0.8.5 (2017-10-23) - -* Fix: Work around PHP bug with Unix domain socket (UDS) paths for Mac OS X - (#123 by @andig) - -* Fix: Fix `SecureServer` to return `null` URI if server socket is already closed - (#129 by @clue) - -* Improve test suite by adding forward compatibility with PHPUnit v5 and - forward compatibility with upcoming EventLoop releases in tests and - test Mac OS X on Travis - (#122 by @andig and #125, #127 and #130 by @clue) - -* Readme improvements - (#118 by @jsor) - -## 0.8.4 (2017-09-16) - -* Feature: Add `FixedUriConnector` decorator to use fixed, preconfigured URI instead - (#117 by @clue) - - This can be useful for consumers that do not support certain URIs, such as - when you want to explicitly connect to a Unix domain socket (UDS) path - instead of connecting to a default address assumed by an higher-level API: - - ```php - $connector = new FixedUriConnector( - 'unix:///var/run/docker.sock', - new UnixConnector($loop) - ); - - // destination will be ignored, actually connects to Unix domain socket - $promise = $connector->connect('localhost:80'); - ``` - -## 0.8.3 (2017-09-08) - -* Feature: Reduce memory consumption for failed connections - (#113 by @valga) - -* Fix: Work around write chunk size for TLS streams for PHP < 7.1.14 - (#114 by @clue) - -## 0.8.2 (2017-08-25) - -* Feature: Update DNS dependency to support hosts file on all platforms - (#112 by @clue) - - This means that connecting to hosts such as `localhost` will now work as - expected across all platforms with no changes required: - - ```php - $connector = new Connector($loop); - $connector->connect('localhost:8080')->then(function ($connection) { - // … - }); - ``` - -## 0.8.1 (2017-08-15) - -* Feature: Forward compatibility with upcoming EventLoop v1.0 and v0.5 and - target evenement 3.0 a long side 2.0 and 1.0 - (#104 by @clue and #111 by @WyriHaximus) - -* Improve test suite by locking Travis distro so new defaults will not break the build and - fix HHVM build for now again and ignore future HHVM build errors - (#109 and #110 by @clue) - -* Minor documentation fixes - (#103 by @christiaan and #108 by @hansott) - -## 0.8.0 (2017-05-09) - -* Feature: New `Server` class now acts as a facade for existing server classes - and renamed old `Server` to `TcpServer` for advanced usage. - (#96 and #97 by @clue) - - The `Server` class is now the main class in this package that implements the - `ServerInterface` and allows you to accept incoming streaming connections, - such as plaintext TCP/IP or secure TLS connection streams. - - > This is not a BC break and consumer code does not have to be updated. - -* Feature / BC break: All addresses are now URIs that include the URI scheme - (#98 by @clue) - - ```diff - - $parts = parse_url('tcp://' . $conn->getRemoteAddress()); - + $parts = parse_url($conn->getRemoteAddress()); - ``` - -* Fix: Fix `unix://` addresses for Unix domain socket (UDS) paths - (#100 by @clue) - -* Feature: Forward compatibility with Stream v1.0 and v0.7 - (#99 by @clue) - -## 0.7.2 (2017-04-24) - -* Fix: Work around latest PHP 7.0.18 and 7.1.4 no longer accepting full URIs - (#94 by @clue) - -## 0.7.1 (2017-04-10) - -* Fix: Ignore HHVM errors when closing connection that is already closing - (#91 by @clue) - -## 0.7.0 (2017-04-10) - -* Feature: Merge SocketClient component into this component - (#87 by @clue) - - This means that this package now provides async, streaming plaintext TCP/IP - and secure TLS socket server and client connections for ReactPHP. - - ``` - $connector = new React\Socket\Connector($loop); - $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) { - $connection->write('…'); - }); - ``` - - Accordingly, the `ConnectionInterface` is now used to represent both incoming - server side connections as well as outgoing client side connections. - - If you've previously used the SocketClient component to establish outgoing - client connections, upgrading should take no longer than a few minutes. - All classes have been merged as-is from the latest `v0.7.0` release with no - other changes, so you can simply update your code to use the updated namespace - like this: - - ```php - // old from SocketClient component and namespace - $connector = new React\SocketClient\Connector($loop); - $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) { - $connection->write('…'); - }); - - // new - $connector = new React\Socket\Connector($loop); - $connector->connect('google.com:80')->then(function (ConnectionInterface $conn) { - $connection->write('…'); - }); - ``` - -## 0.6.0 (2017-04-04) - -* Feature: Add `LimitingServer` to limit and keep track of open connections - (#86 by @clue) - - ```php - $server = new Server(0, $loop); - $server = new LimitingServer($server, 100); - - $server->on('connection', function (ConnectionInterface $connection) { - $connection->write('hello there!' . PHP_EOL); - … - }); - ``` - -* Feature / BC break: Add `pause()` and `resume()` methods to limit active - connections - (#84 by @clue) - - ```php - $server = new Server(0, $loop); - $server->pause(); - - $loop->addTimer(1.0, function() use ($server) { - $server->resume(); - }); - ``` - -## 0.5.1 (2017-03-09) - -* Feature: Forward compatibility with Stream v0.5 and upcoming v0.6 - (#79 by @clue) - -## 0.5.0 (2017-02-14) - -* Feature / BC break: Replace `listen()` call with URIs passed to constructor - and reject listening on hostnames with `InvalidArgumentException` - and replace `ConnectionException` with `RuntimeException` for consistency - (#61, #66 and #72 by @clue) - - ```php - // old - $server = new Server($loop); - $server->listen(8080); - - // new - $server = new Server(8080, $loop); - ``` - - Similarly, you can now pass a full listening URI to the constructor to change - the listening host: - - ```php - // old - $server = new Server($loop); - $server->listen(8080, '127.0.0.1'); - - // new - $server = new Server('127.0.0.1:8080', $loop); - ``` - - Trying to start listening on (DNS) host names will now throw an - `InvalidArgumentException`, use IP addresses instead: - - ```php - // old - $server = new Server($loop); - $server->listen(8080, 'localhost'); - - // new - $server = new Server('127.0.0.1:8080', $loop); - ``` - - If trying to listen fails (such as if port is already in use or port below - 1024 may require root access etc.), it will now throw a `RuntimeException`, - the `ConnectionException` class has been removed: - - ```php - // old: throws React\Socket\ConnectionException - $server = new Server($loop); - $server->listen(80); - - // new: throws RuntimeException - $server = new Server(80, $loop); - ``` - -* Feature / BC break: Rename `shutdown()` to `close()` for consistency throughout React - (#62 by @clue) - - ```php - // old - $server->shutdown(); - - // new - $server->close(); - ``` - -* Feature / BC break: Replace `getPort()` with `getAddress()` - (#67 by @clue) - - ```php - // old - echo $server->getPort(); // 8080 - - // new - echo $server->getAddress(); // 127.0.0.1:8080 - ``` - -* Feature / BC break: `getRemoteAddress()` returns full address instead of only IP - (#65 by @clue) - - ```php - // old - echo $connection->getRemoteAddress(); // 192.168.0.1 - - // new - echo $connection->getRemoteAddress(); // 192.168.0.1:51743 - ``` - -* Feature / BC break: Add `getLocalAddress()` method - (#68 by @clue) - - ```php - echo $connection->getLocalAddress(); // 127.0.0.1:8080 - ``` - -* BC break: The `Server` and `SecureServer` class are now marked `final` - and you can no longer `extend` them - (which was never documented or recommended anyway). - Public properties and event handlers are now internal only. - Please use composition instead of extension. - (#71, #70 and #69 by @clue) - -## 0.4.6 (2017-01-26) - -* Feature: Support socket context options passed to `Server` - (#64 by @clue) - -* Fix: Properly return `null` for unknown addresses - (#63 by @clue) - -* Improve documentation for `ServerInterface` and lock test suite requirements - (#60 by @clue, #57 by @shaunbramley) - -## 0.4.5 (2017-01-08) - -* Feature: Add `SecureServer` for secure TLS connections - (#55 by @clue) - -* Add functional integration tests - (#54 by @clue) - -## 0.4.4 (2016-12-19) - -* Feature / Fix: `ConnectionInterface` should extend `DuplexStreamInterface` + documentation - (#50 by @clue) - -* Feature / Fix: Improve test suite and switch to normal stream handler - (#51 by @clue) - -* Feature: Add examples - (#49 by @clue) - -## 0.4.3 (2016-03-01) - -* Bug fix: Suppress errors on stream_socket_accept to prevent PHP from crashing -* Support for PHP7 and HHVM -* Support PHP 5.3 again - -## 0.4.2 (2014-05-25) - -* Verify stream is a valid resource in Connection - -## 0.4.1 (2014-04-13) - -* Bug fix: Check read buffer for data before shutdown signal and end emit (@ArtyDev) -* Bug fix: v0.3.4 changes merged for v0.4.1 - -## 0.3.4 (2014-03-30) - -* Bug fix: Reset socket to non-blocking after shutting down (PHP bug) - -## 0.4.0 (2014-02-02) - -* BC break: Bump minimum PHP version to PHP 5.4, remove 5.3 specific hacks -* BC break: Update to React/Promise 2.0 -* BC break: Update to Evenement 2.0 -* Dependency: Autoloading and filesystem structure now PSR-4 instead of PSR-0 -* Bump React dependencies to v0.4 - -## 0.3.3 (2013-07-08) - -* Version bump - -## 0.3.2 (2013-05-10) - -* Version bump - -## 0.3.1 (2013-04-21) - -* Feature: Support binding to IPv6 addresses (@clue) - -## 0.3.0 (2013-04-14) - -* Bump React dependencies to v0.3 - -## 0.2.6 (2012-12-26) - -* Version bump - -## 0.2.3 (2012-11-14) - -* Version bump - -## 0.2.0 (2012-09-10) - -* Bump React dependencies to v0.2 - -## 0.1.1 (2012-07-12) - -* Version bump - -## 0.1.0 (2012-07-11) - -* First tagged release diff --git a/vendor/react/socket/LICENSE b/vendor/react/socket/LICENSE deleted file mode 100644 index d6f8901..0000000 --- a/vendor/react/socket/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -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/socket/README.md b/vendor/react/socket/README.md deleted file mode 100644 index e77e676..0000000 --- a/vendor/react/socket/README.md +++ /dev/null @@ -1,1564 +0,0 @@ -# Socket - -[![CI status](https://github.com/reactphp/socket/workflows/CI/badge.svg)](https://github.com/reactphp/socket/actions) -[![installs on Packagist](https://img.shields.io/packagist/dt/react/socket?color=blue&label=installs%20on%20Packagist)](https://packagist.org/packages/react/socket) - -Async, streaming plaintext TCP/IP and secure TLS socket server and client -connections for [ReactPHP](https://reactphp.org/). - -The socket library provides re-usable interfaces for a socket-layer -server and client based on the [`EventLoop`](https://github.com/reactphp/event-loop) -and [`Stream`](https://github.com/reactphp/stream) components. -Its server component allows you to build networking servers that accept incoming -connections from networking clients (such as an HTTP server). -Its client component allows you to build networking clients that establish -outgoing connections to networking servers (such as an HTTP or database client). -This library provides async, streaming means for all of this, so you can -handle multiple concurrent connections without blocking. - -**Table of Contents** - -* [Quickstart example](#quickstart-example) -* [Connection usage](#connection-usage) - * [ConnectionInterface](#connectioninterface) - * [getRemoteAddress()](#getremoteaddress) - * [getLocalAddress()](#getlocaladdress) -* [Server usage](#server-usage) - * [ServerInterface](#serverinterface) - * [connection event](#connection-event) - * [error event](#error-event) - * [getAddress()](#getaddress) - * [pause()](#pause) - * [resume()](#resume) - * [close()](#close) - * [SocketServer](#socketserver) - * [Advanced server usage](#advanced-server-usage) - * [TcpServer](#tcpserver) - * [SecureServer](#secureserver) - * [UnixServer](#unixserver) - * [LimitingServer](#limitingserver) - * [getConnections()](#getconnections) -* [Client usage](#client-usage) - * [ConnectorInterface](#connectorinterface) - * [connect()](#connect) - * [Connector](#connector) - * [Advanced client usage](#advanced-client-usage) - * [TcpConnector](#tcpconnector) - * [HappyEyeBallsConnector](#happyeyeballsconnector) - * [DnsConnector](#dnsconnector) - * [SecureConnector](#secureconnector) - * [TimeoutConnector](#timeoutconnector) - * [UnixConnector](#unixconnector) - * [FixUriConnector](#fixeduriconnector) -* [Install](#install) -* [Tests](#tests) -* [License](#license) - -## Quickstart example - -Here is a server that closes the connection if you send it anything: - -```php -$socket = new React\Socket\SocketServer('127.0.0.1:8080'); - -$socket->on('connection', function (React\Socket\ConnectionInterface $connection) { - $connection->write("Hello " . $connection->getRemoteAddress() . "!\n"); - $connection->write("Welcome to this amazing server!\n"); - $connection->write("Here's a tip: don't say anything.\n"); - - $connection->on('data', function ($data) use ($connection) { - $connection->close(); - }); -}); -``` - -See also the [examples](examples). - -Here's a client that outputs the output of said server and then attempts to -send it a string: - -```php -$connector = new React\Socket\Connector(); - -$connector->connect('127.0.0.1:8080')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->pipe(new React\Stream\WritableResourceStream(STDOUT)); - $connection->write("Hello World!\n"); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -## Connection usage - -### ConnectionInterface - -The `ConnectionInterface` is used to represent any incoming and outgoing -connection, such as a normal TCP/IP connection. - -An incoming or outgoing connection is a duplex stream (both readable and -writable) that implements React's -[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface). -It contains additional properties for the local and remote address (client IP) -where this connection has been established to/from. - -Most commonly, instances implementing this `ConnectionInterface` are emitted -by all classes implementing the [`ServerInterface`](#serverinterface) and -used by all classes implementing the [`ConnectorInterface`](#connectorinterface). - -Because the `ConnectionInterface` implements the underlying -[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface) -you can use any of its events and methods as usual: - -```php -$connection->on('data', function ($chunk) { - echo $chunk; -}); - -$connection->on('end', function () { - echo 'ended'; -}); - -$connection->on('error', function (Exception $e) { - echo 'error: ' . $e->getMessage(); -}); - -$connection->on('close', function () { - echo 'closed'; -}); - -$connection->write($data); -$connection->end($data = null); -$connection->close(); -// … -``` - -For more details, see the -[`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface). - -#### getRemoteAddress() - -The `getRemoteAddress(): ?string` method returns the full remote address -(URI) where this connection has been established with. - -```php -$address = $connection->getRemoteAddress(); -echo 'Connection with ' . $address . PHP_EOL; -``` - -If the remote address can not be determined or is unknown at this time (such as -after the connection has been closed), it MAY return a `NULL` value instead. - -Otherwise, it will return the full address (URI) as a string value, such -as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`, -`unix://example.sock` or `unix:///path/to/example.sock`. -Note that individual URI components are application specific and depend -on the underlying transport protocol. - -If this is a TCP/IP based connection and you only want the remote IP, you may -use something like this: - -```php -$address = $connection->getRemoteAddress(); -$ip = trim(parse_url($address, PHP_URL_HOST), '[]'); -echo 'Connection with ' . $ip . PHP_EOL; -``` - -#### getLocalAddress() - -The `getLocalAddress(): ?string` method returns the full local address -(URI) where this connection has been established with. - -```php -$address = $connection->getLocalAddress(); -echo 'Connection with ' . $address . PHP_EOL; -``` - -If the local address can not be determined or is unknown at this time (such as -after the connection has been closed), it MAY return a `NULL` value instead. - -Otherwise, it will return the full address (URI) as a string value, such -as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`, -`unix://example.sock` or `unix:///path/to/example.sock`. -Note that individual URI components are application specific and depend -on the underlying transport protocol. - -This method complements the [`getRemoteAddress()`](#getremoteaddress) method, -so they should not be confused. - -If your `TcpServer` instance is listening on multiple interfaces (e.g. using -the address `0.0.0.0`), you can use this method to find out which interface -actually accepted this connection (such as a public or local interface). - -If your system has multiple interfaces (e.g. a WAN and a LAN interface), -you can use this method to find out which interface was actually -used for this connection. - -## Server usage - -### ServerInterface - -The `ServerInterface` is responsible for providing an interface for accepting -incoming streaming connections, such as a normal TCP/IP connection. - -Most higher-level components (such as a HTTP server) accept an instance -implementing this interface to accept incoming streaming connections. -This is usually done via dependency injection, so it's fairly simple to actually -swap this implementation against any other implementation of this interface. -This means that you SHOULD typehint against this interface instead of a concrete -implementation of this interface. - -Besides defining a few methods, this interface also implements the -[`EventEmitterInterface`](https://github.com/igorw/evenement) -which allows you to react to certain events. - -#### connection event - -The `connection` event will be emitted whenever a new connection has been -established, i.e. a new client connects to this server socket: - -```php -$socket->on('connection', function (React\Socket\ConnectionInterface $connection) { - echo 'new connection' . PHP_EOL; -}); -``` - -See also the [`ConnectionInterface`](#connectioninterface) for more details -about handling the incoming connection. - -#### error event - -The `error` event will be emitted whenever there's an error accepting a new -connection from a client. - -```php -$socket->on('error', function (Exception $e) { - echo 'error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -Note that this is not a fatal error event, i.e. the server keeps listening for -new connections even after this event. - -#### getAddress() - -The `getAddress(): ?string` method can be used to -return the full address (URI) this server is currently listening on. - -```php -$address = $socket->getAddress(); -echo 'Server listening on ' . $address . PHP_EOL; -``` - -If the address can not be determined or is unknown at this time (such as -after the socket has been closed), it MAY return a `NULL` value instead. - -Otherwise, it will return the full address (URI) as a string value, such -as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443` -`unix://example.sock` or `unix:///path/to/example.sock`. -Note that individual URI components are application specific and depend -on the underlying transport protocol. - -If this is a TCP/IP based server and you only want the local port, you may -use something like this: - -```php -$address = $socket->getAddress(); -$port = parse_url($address, PHP_URL_PORT); -echo 'Server listening on port ' . $port . PHP_EOL; -``` - -#### pause() - -The `pause(): void` method can be used to -pause accepting new incoming connections. - -Removes the socket resource from the EventLoop and thus stop accepting -new connections. Note that the listening socket stays active and is not -closed. - -This means that new incoming connections will stay pending in the -operating system backlog until its configurable backlog is filled. -Once the backlog is filled, the operating system may reject further -incoming connections until the backlog is drained again by resuming -to accept new connections. - -Once the server is paused, no futher `connection` events SHOULD -be emitted. - -```php -$socket->pause(); - -$socket->on('connection', assertShouldNeverCalled()); -``` - -This method is advisory-only, though generally not recommended, the -server MAY continue emitting `connection` events. - -Unless otherwise noted, a successfully opened server SHOULD NOT start -in paused state. - -You can continue processing events by calling `resume()` again. - -Note that both methods can be called any number of times, in particular -calling `pause()` more than once SHOULD NOT have any effect. -Similarly, calling this after `close()` is a NO-OP. - -#### resume() - -The `resume(): void` method can be used to -resume accepting new incoming connections. - -Re-attach the socket resource to the EventLoop after a previous `pause()`. - -```php -$socket->pause(); - -Loop::addTimer(1.0, function () use ($socket) { - $socket->resume(); -}); -``` - -Note that both methods can be called any number of times, in particular -calling `resume()` without a prior `pause()` SHOULD NOT have any effect. -Similarly, calling this after `close()` is a NO-OP. - -#### close() - -The `close(): void` method can be used to -shut down this listening socket. - -This will stop listening for new incoming connections on this socket. - -```php -echo 'Shutting down server socket' . PHP_EOL; -$socket->close(); -``` - -Calling this method more than once on the same instance is a NO-OP. - -### SocketServer - - - -The `SocketServer` class is the main class in this package that implements the -[`ServerInterface`](#serverinterface) and allows you to accept incoming -streaming connections, such as plaintext TCP/IP or secure TLS connection streams. - -In order to accept plaintext TCP/IP connections, you can simply pass a host -and port combination like this: - -```php -$socket = new React\Socket\SocketServer('127.0.0.1:8080'); -``` - -Listening on the localhost address `127.0.0.1` means it will not be reachable from -outside of this system. -In order to change the host the socket is listening on, you can provide an IP -address of an interface or use the special `0.0.0.0` address to listen on all -interfaces: - -```php -$socket = new React\Socket\SocketServer('0.0.0.0:8080'); -``` - -If you want to listen on an IPv6 address, you MUST enclose the host in square -brackets: - -```php -$socket = new React\Socket\SocketServer('[::1]:8080'); -``` - -In order to use a random port assignment, you can use the port `0`: - -```php -$socket = new React\Socket\SocketServer('127.0.0.1:0'); -$address = $socket->getAddress(); -``` - -To listen on a Unix domain socket (UDS) path, you MUST prefix the URI with the -`unix://` scheme: - -```php -$socket = new React\Socket\SocketServer('unix:///tmp/server.sock'); -``` - -In order to listen on an existing file descriptor (FD) number, you MUST prefix -the URI with `php://fd/` like this: - -```php -$socket = new React\Socket\SocketServer('php://fd/3'); -``` - -If the given URI is invalid, does not contain a port, any other scheme or if it -contains a hostname, it will throw an `InvalidArgumentException`: - -```php -// throws InvalidArgumentException due to missing port -$socket = new React\Socket\SocketServer('127.0.0.1'); -``` - -If the given URI appears to be valid, but listening on it fails (such as if port -is already in use or port below 1024 may require root access etc.), it will -throw a `RuntimeException`: - -```php -$first = new React\Socket\SocketServer('127.0.0.1:8080'); - -// throws RuntimeException because port is already in use -$second = new React\Socket\SocketServer('127.0.0.1:8080'); -``` - -> Note that these error conditions may vary depending on your system and/or - configuration. - See the exception message and code for more details about the actual error - condition. - -Optionally, you can specify [TCP socket context options](https://www.php.net/manual/en/context.socket.php) -for the underlying stream socket resource like this: - -```php -$socket = new React\Socket\SocketServer('[::1]:8080', array( - 'tcp' => array( - 'backlog' => 200, - 'so_reuseport' => true, - 'ipv6_v6only' => true - ) -)); -``` - -> Note that available [socket context options](https://www.php.net/manual/en/context.socket.php), - their defaults and effects of changing these may vary depending on your system - and/or PHP version. - Passing unknown context options has no effect. - The `backlog` context option defaults to `511` unless given explicitly. - -You can start a secure TLS (formerly known as SSL) server by simply prepending -the `tls://` URI scheme. -Internally, it will wait for plaintext TCP/IP connections and then performs a -TLS handshake for each connection. -It thus requires valid [TLS context options](https://www.php.net/manual/en/context.ssl.php), -which in its most basic form may look something like this if you're using a -PEM encoded certificate file: - -```php -$socket = new React\Socket\SocketServer('tls://127.0.0.1:8080', array( - 'tls' => array( - 'local_cert' => 'server.pem' - ) -)); -``` - -> Note that the certificate file will not be loaded on instantiation but when an - incoming connection initializes its TLS context. - This implies that any invalid certificate file paths or contents will only cause - an `error` event at a later time. - -If your private key is encrypted with a passphrase, you have to specify it -like this: - -```php -$socket = new React\Socket\SocketServer('tls://127.0.0.1:8000', array( - 'tls' => array( - 'local_cert' => 'server.pem', - 'passphrase' => 'secret' - ) -)); -``` - -By default, this server supports TLSv1.0+ and excludes support for legacy -SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you -want to negotiate with the remote side: - -```php -$socket = new React\Socket\SocketServer('tls://127.0.0.1:8000', array( - 'tls' => array( - 'local_cert' => 'server.pem', - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER - ) -)); -``` - -> Note that available [TLS context options](https://www.php.net/manual/en/context.ssl.php), - their defaults and effects of changing these may vary depending on your system - and/or PHP version. - The outer context array allows you to also use `tcp` (and possibly more) - context options at the same time. - Passing unknown context options has no effect. - If you do not use the `tls://` scheme, then passing `tls` context options - has no effect. - -Whenever a client connects, it will emit a `connection` event with a connection -instance implementing [`ConnectionInterface`](#connectioninterface): - -```php -$socket->on('connection', function (React\Socket\ConnectionInterface $connection) { - echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL; - - $connection->write('hello there!' . PHP_EOL); - … -}); -``` - -See also the [`ServerInterface`](#serverinterface) 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. - -> Note that the `SocketServer` class is a concrete implementation for TCP/IP sockets. - If you want to typehint in your higher-level protocol implementation, you SHOULD - use the generic [`ServerInterface`](#serverinterface) instead. - -> Changelog v1.9.0: This class has been added with an improved constructor signature - as a replacement for the previous `Server` class in order to avoid any ambiguities. - The previous name has been deprecated and should not be used anymore. - -### Advanced server usage - -#### TcpServer - -The `TcpServer` class implements the [`ServerInterface`](#serverinterface) and -is responsible for accepting plaintext TCP/IP connections. - -```php -$server = new React\Socket\TcpServer(8080); -``` - -As above, the `$uri` parameter can consist of only a port, in which case the -server will default to listening on the localhost address `127.0.0.1`, -which means it will not be reachable from outside of this system. - -In order to use a random port assignment, you can use the port `0`: - -```php -$server = new React\Socket\TcpServer(0); -$address = $server->getAddress(); -``` - -In order to change the host the socket is listening on, you can provide an IP -address through the first parameter provided to the constructor, optionally -preceded by the `tcp://` scheme: - -```php -$server = new React\Socket\TcpServer('192.168.0.1:8080'); -``` - -If you want to listen on an IPv6 address, you MUST enclose the host in square -brackets: - -```php -$server = new React\Socket\TcpServer('[::1]:8080'); -``` - -If the given URI is invalid, does not contain a port, any other scheme or if it -contains a hostname, it will throw an `InvalidArgumentException`: - -```php -// throws InvalidArgumentException due to missing port -$server = new React\Socket\TcpServer('127.0.0.1'); -``` - -If the given URI appears to be valid, but listening on it fails (such as if port -is already in use or port below 1024 may require root access etc.), it will -throw a `RuntimeException`: - -```php -$first = new React\Socket\TcpServer(8080); - -// throws RuntimeException because port is already in use -$second = new React\Socket\TcpServer(8080); -``` - -> Note that these error conditions may vary depending on your system and/or -configuration. -See the exception message and code for more details about the actual error -condition. - -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. - -Optionally, you can specify [socket context options](https://www.php.net/manual/en/context.socket.php) -for the underlying stream socket resource like this: - -```php -$server = new React\Socket\TcpServer('[::1]:8080', null, array( - 'backlog' => 200, - 'so_reuseport' => true, - 'ipv6_v6only' => true -)); -``` - -> Note that available [socket context options](https://www.php.net/manual/en/context.socket.php), -their defaults and effects of changing these may vary depending on your system -and/or PHP version. -Passing unknown context options has no effect. -The `backlog` context option defaults to `511` unless given explicitly. - -Whenever a client connects, it will emit a `connection` event with a connection -instance implementing [`ConnectionInterface`](#connectioninterface): - -```php -$server->on('connection', function (React\Socket\ConnectionInterface $connection) { - echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL; - - $connection->write('hello there!' . PHP_EOL); - … -}); -``` - -See also the [`ServerInterface`](#serverinterface) for more details. - -#### SecureServer - -The `SecureServer` class implements the [`ServerInterface`](#serverinterface) -and is responsible for providing a secure TLS (formerly known as SSL) server. - -It does so by wrapping a [`TcpServer`](#tcpserver) instance which waits for plaintext -TCP/IP connections and then performs a TLS handshake for each connection. -It thus requires valid [TLS context options](https://www.php.net/manual/en/context.ssl.php), -which in its most basic form may look something like this if you're using a -PEM encoded certificate file: - -```php -$server = new React\Socket\TcpServer(8000); -$server = new React\Socket\SecureServer($server, null, array( - 'local_cert' => 'server.pem' -)); -``` - -> Note that the certificate file will not be loaded on instantiation but when an -incoming connection initializes its TLS context. -This implies that any invalid certificate file paths or contents will only cause -an `error` event at a later time. - -If your private key is encrypted with a passphrase, you have to specify it -like this: - -```php -$server = new React\Socket\TcpServer(8000); -$server = new React\Socket\SecureServer($server, null, array( - 'local_cert' => 'server.pem', - 'passphrase' => 'secret' -)); -``` - -By default, this server supports TLSv1.0+ and excludes support for legacy -SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you -want to negotiate with the remote side: - -```php -$server = new React\Socket\TcpServer(8000); -$server = new React\Socket\SecureServer($server, null, array( - 'local_cert' => 'server.pem', - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_SERVER -)); -``` - -> Note that available [TLS context options](https://www.php.net/manual/en/context.ssl.php), -their defaults and effects of changing these may vary depending on your system -and/or PHP version. -Passing unknown context options has no effect. - -Whenever a client completes the TLS handshake, it will emit a `connection` event -with a connection instance implementing [`ConnectionInterface`](#connectioninterface): - -```php -$server->on('connection', function (React\Socket\ConnectionInterface $connection) { - echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL; - - $connection->write('hello there!' . PHP_EOL); - … -}); -``` - -Whenever a client fails to perform a successful TLS handshake, it will emit an -`error` event and then close the underlying TCP/IP connection: - -```php -$server->on('error', function (Exception $e) { - echo 'Error' . $e->getMessage() . PHP_EOL; -}); -``` - -See also the [`ServerInterface`](#serverinterface) for more details. - -Note that the `SecureServer` class is a concrete implementation for TLS sockets. -If you want to typehint in your higher-level protocol implementation, you SHOULD -use the generic [`ServerInterface`](#serverinterface) instead. - -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. - -> Advanced usage: Despite allowing any `ServerInterface` as first parameter, -you SHOULD pass a `TcpServer` instance as first parameter, unless you -know what you're doing. -Internally, the `SecureServer` has to set the required TLS context options on -the underlying stream resources. -These resources are not exposed through any of the interfaces defined in this -package, but only through the internal `Connection` class. -The `TcpServer` class is guaranteed to emit connections that implement -the `ConnectionInterface` and uses the internal `Connection` class in order to -expose these underlying resources. -If you use a custom `ServerInterface` and its `connection` event does not -meet this requirement, the `SecureServer` will emit an `error` event and -then close the underlying connection. - -#### UnixServer - -The `UnixServer` class implements the [`ServerInterface`](#serverinterface) and -is responsible for accepting connections on Unix domain sockets (UDS). - -```php -$server = new React\Socket\UnixServer('/tmp/server.sock'); -``` - -As above, the `$uri` parameter can consist of only a socket path or socket path -prefixed by the `unix://` scheme. - -If the given URI appears to be valid, but listening on it fails (such as if the -socket is already in use or the file not accessible etc.), it will throw a -`RuntimeException`: - -```php -$first = new React\Socket\UnixServer('/tmp/same.sock'); - -// throws RuntimeException because socket is already in use -$second = new React\Socket\UnixServer('/tmp/same.sock'); -``` - -> Note that these error conditions may vary depending on your system and/or - configuration. - In particular, Zend PHP does only report "Unknown error" when the UDS path - already exists and can not be bound. You may want to check `is_file()` on the - given UDS path to report a more user-friendly error message in this case. - See the exception message and code for more details about the actual error - condition. - -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. - -Whenever a client connects, it will emit a `connection` event with a connection -instance implementing [`ConnectionInterface`](#connectioninterface): - -```php -$server->on('connection', function (React\Socket\ConnectionInterface $connection) { - echo 'New connection' . PHP_EOL; - - $connection->write('hello there!' . PHP_EOL); - … -}); -``` - -See also the [`ServerInterface`](#serverinterface) for more details. - -#### LimitingServer - -The `LimitingServer` decorator wraps a given `ServerInterface` and is responsible -for limiting and keeping track of open connections to this server instance. - -Whenever the underlying server emits a `connection` event, it will check its -limits and then either - - keep track of this connection by adding it to the list of - open connections and then forward the `connection` event - - or reject (close) the connection when its limits are exceeded and will - forward an `error` event instead. - -Whenever a connection closes, it will remove this connection from the list of -open connections. - -```php -$server = new React\Socket\LimitingServer($server, 100); -$server->on('connection', function (React\Socket\ConnectionInterface $connection) { - $connection->write('hello there!' . PHP_EOL); - … -}); -``` - -See also the [second example](examples) for more details. - -You have to pass a maximum number of open connections to ensure -the server will automatically reject (close) connections once this limit -is exceeded. In this case, it will emit an `error` event to inform about -this and no `connection` event will be emitted. - -```php -$server = new React\Socket\LimitingServer($server, 100); -$server->on('connection', function (React\Socket\ConnectionInterface $connection) { - $connection->write('hello there!' . PHP_EOL); - … -}); -``` - -You MAY pass a `null` limit in order to put no limit on the number of -open connections and keep accepting new connection until you run out of -operating system resources (such as open file handles). This may be -useful if you do not want to take care of applying a limit but still want -to use the `getConnections()` method. - -You can optionally configure the server to pause accepting new -connections once the connection limit is reached. In this case, it will -pause the underlying server and no longer process any new connections at -all, thus also no longer closing any excessive connections. -The underlying operating system is responsible for keeping a backlog of -pending connections until its limit is reached, at which point it will -start rejecting further connections. -Once the server is below the connection limit, it will continue consuming -connections from the backlog and will process any outstanding data on -each connection. -This mode may be useful for some protocols that are designed to wait for -a response message (such as HTTP), but may be less useful for other -protocols that demand immediate responses (such as a "welcome" message in -an interactive chat). - -```php -$server = new React\Socket\LimitingServer($server, 100, true); -$server->on('connection', function (React\Socket\ConnectionInterface $connection) { - $connection->write('hello there!' . PHP_EOL); - … -}); -``` - -##### getConnections() - -The `getConnections(): ConnectionInterface[]` method can be used to -return an array with all currently active connections. - -```php -foreach ($server->getConnection() as $connection) { - $connection->write('Hi!'); -} -``` - -## Client usage - -### ConnectorInterface - -The `ConnectorInterface` is responsible for providing an interface for -establishing streaming connections, such as a normal TCP/IP connection. - -This is the main interface defined in this package and it is used throughout -React's vast ecosystem. - -Most higher-level components (such as HTTP, database or other networking -service clients) accept an instance implementing this interface to create their -TCP/IP connection to the underlying networking service. -This is usually done via dependency injection, so it's fairly simple to actually -swap this implementation against any other implementation of this interface. - -The interface only offers a single method: - -#### connect() - -The `connect(string $uri): PromiseInterface` method can be used to -create a streaming connection to the given remote address. - -It returns a [Promise](https://github.com/reactphp/promise) which either -fulfills with a stream implementing [`ConnectionInterface`](#connectioninterface) -on success or rejects with an `Exception` if the connection is not successful: - -```php -$connector->connect('google.com:443')->then( - function (React\Socket\ConnectionInterface $connection) { - // connection successfully established - }, - function (Exception $error) { - // failed to connect due to $error - } -); -``` - -See also [`ConnectionInterface`](#connectioninterface) for more details. - -The returned Promise MUST be implemented in such a way that it can be -cancelled when it is still pending. Cancelling a pending promise MUST -reject its value with an `Exception`. It SHOULD clean up any underlying -resources and references as applicable: - -```php -$promise = $connector->connect($uri); - -$promise->cancel(); -``` - -### Connector - -The `Connector` class is the main class in this package that implements the -[`ConnectorInterface`](#connectorinterface) and allows you to create streaming connections. - -You can use this connector to create any kind of streaming connections, such -as plaintext TCP/IP, secure TLS or local Unix connection streams. - -It binds to the main event loop and can be used like this: - -```php -$connector = new React\Socket\Connector(); - -$connector->connect($uri)->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}, function (Exception $e) { - echo 'Error: ' . $e->getMessage() . PHP_EOL; -}); -``` - -In order to create a plaintext TCP/IP connection, you can simply pass a host -and port combination like this: - -```php -$connector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -> If you do no specify a URI scheme in the destination URI, it will assume - `tcp://` as a default and establish a plaintext TCP/IP connection. - Note that TCP/IP connections require a host and port part in the destination - URI like above, all other URI components are optional. - -In order to create a secure TLS connection, you can use the `tls://` URI scheme -like this: - -```php -$connector->connect('tls://www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -In order to create a local Unix domain socket connection, you can use the -`unix://` URI scheme like this: - -```php -$connector->connect('unix:///tmp/demo.sock')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -> The [`getRemoteAddress()`](#getremoteaddress) method will return the target - Unix domain socket (UDS) path as given to the `connect()` method, including - the `unix://` scheme, for example `unix:///tmp/demo.sock`. - The [`getLocalAddress()`](#getlocaladdress) method will most likely return a - `null` value as this value is not applicable to UDS connections here. - -Under the hood, the `Connector` is implemented as a *higher-level facade* -for the lower-level connectors implemented in this package. This means it -also shares all of their features and implementation details. -If you want to typehint in your higher-level protocol implementation, you SHOULD -use the generic [`ConnectorInterface`](#connectorinterface) instead. - -As of `v1.4.0`, the `Connector` class defaults to using the -[happy eyeballs algorithm](https://en.wikipedia.org/wiki/Happy_Eyeballs) to -automatically connect over IPv4 or IPv6 when a hostname is given. -This automatically attempts to connect using both IPv4 and IPv6 at the same time -(preferring IPv6), thus avoiding the usual problems faced by users with imperfect -IPv6 connections or setups. -If you want to revert to the old behavior of only doing an IPv4 lookup and -only attempt a single IPv4 connection, you can set up the `Connector` like this: - -```php -$connector = new React\Socket\Connector(array( - 'happy_eyeballs' => false -)); -``` - -Similarly, you can also affect the default DNS behavior as follows. -The `Connector` class will try to detect your system DNS settings (and uses -Google's public DNS server `8.8.8.8` as a fallback if unable to determine your -system settings) to resolve all public hostnames into underlying IP addresses by -default. -If you explicitly want to use a custom DNS server (such as a local DNS relay or -a company wide DNS server), you can set up the `Connector` like this: - -```php -$connector = new React\Socket\Connector(array( - 'dns' => '127.0.1.1' -)); - -$connector->connect('localhost:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -If you do not want to use a DNS resolver at all and want to connect to IP -addresses only, you can also set up your `Connector` like this: - -```php -$connector = new React\Socket\Connector(array( - 'dns' => false -)); - -$connector->connect('127.0.0.1:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -Advanced: If you need a custom DNS `React\Dns\Resolver\ResolverInterface` instance, you -can also set up your `Connector` like this: - -```php -$dnsResolverFactory = new React\Dns\Resolver\Factory(); -$resolver = $dnsResolverFactory->createCached('127.0.1.1'); - -$connector = new React\Socket\Connector(array( - 'dns' => $resolver -)); - -$connector->connect('localhost:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -By default, the `tcp://` and `tls://` URI schemes will use timeout value that -respects your `default_socket_timeout` ini setting (which defaults to 60s). -If you want a custom timeout value, you can simply pass this like this: - -```php -$connector = new React\Socket\Connector(array( - 'timeout' => 10.0 -)); -``` - -Similarly, if you do not want to apply a timeout at all and let the operating -system handle this, you can pass a boolean flag like this: - -```php -$connector = new React\Socket\Connector(array( - 'timeout' => false -)); -``` - -By default, the `Connector` supports the `tcp://`, `tls://` and `unix://` -URI schemes. If you want to explicitly prohibit any of these, you can simply -pass boolean flags like this: - -```php -// only allow secure TLS connections -$connector = new React\Socket\Connector(array( - 'tcp' => false, - 'tls' => true, - 'unix' => false, -)); - -$connector->connect('tls://google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -The `tcp://` and `tls://` also accept additional context options passed to -the underlying connectors. -If you want to explicitly pass additional context options, you can simply -pass arrays of context options like this: - -```php -// allow insecure TLS connections -$connector = new React\Socket\Connector(array( - 'tcp' => array( - 'bindto' => '192.168.0.1:0' - ), - 'tls' => array( - 'verify_peer' => false, - 'verify_peer_name' => false - ), -)); - -$connector->connect('tls://localhost:443')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -By default, this connector supports TLSv1.0+ and excludes support for legacy -SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you -want to negotiate with the remote side: - -```php -$connector = new React\Socket\Connector(array( - 'tls' => array( - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT - ) -)); -``` - -> For more details about context options, please refer to the PHP documentation - about [socket context options](https://www.php.net/manual/en/context.socket.php) - and [SSL context options](https://www.php.net/manual/en/context.ssl.php). - -Advanced: By default, the `Connector` supports the `tcp://`, `tls://` and -`unix://` URI schemes. -For this, it sets up the required connector classes automatically. -If you want to explicitly pass custom connectors for any of these, you can simply -pass an instance implementing the `ConnectorInterface` like this: - -```php -$dnsResolverFactory = new React\Dns\Resolver\Factory(); -$resolver = $dnsResolverFactory->createCached('127.0.1.1'); -$tcp = new React\Socket\HappyEyeBallsConnector(null, new React\Socket\TcpConnector(), $resolver); - -$tls = new React\Socket\SecureConnector($tcp); - -$unix = new React\Socket\UnixConnector(); - -$connector = new React\Socket\Connector(array( - 'tcp' => $tcp, - 'tls' => $tls, - 'unix' => $unix, - - 'dns' => false, - 'timeout' => false, -)); - -$connector->connect('google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -> Internally, the `tcp://` connector will always be wrapped by the DNS resolver, - unless you disable DNS like in the above example. In this case, the `tcp://` - connector receives the actual hostname instead of only the resolved IP address - and is thus responsible for performing the lookup. - Internally, the automatically created `tls://` connector will always wrap the - underlying `tcp://` connector for establishing the underlying plaintext - TCP/IP connection before enabling secure TLS mode. If you want to use a custom - underlying `tcp://` connector for secure TLS connections only, you may - explicitly pass a `tls://` connector like above instead. - Internally, the `tcp://` and `tls://` connectors will always be wrapped by - `TimeoutConnector`, unless you disable timeouts like in the above example. - -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. - -> Changelog v1.9.0: The constructur signature has been updated to take the -> optional `$context` as the first parameter and the optional `$loop` as a second -> argument. The previous signature has been deprecated and should not be used anymore. -> -> ```php -> // constructor signature as of v1.9.0 -> $connector = new React\Socket\Connector(array $context = [], ?LoopInterface $loop = null); -> -> // legacy constructor signature before v1.9.0 -> $connector = new React\Socket\Connector(?LoopInterface $loop = null, array $context = []); -> ``` - -### Advanced client usage - -#### TcpConnector - -The `TcpConnector` class implements the -[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext -TCP/IP connections to any IP-port-combination: - -```php -$tcpConnector = new React\Socket\TcpConnector(); - -$tcpConnector->connect('127.0.0.1:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -See also the [examples](examples). - -Pending connection attempts can be cancelled by cancelling its pending promise like so: - -```php -$promise = $tcpConnector->connect('127.0.0.1:80'); - -$promise->cancel(); -``` - -Calling `cancel()` on a pending promise will close the underlying socket -resource, thus cancelling the pending TCP/IP connection, and reject the -resulting promise. - -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. - -You can optionally pass additional -[socket context options](https://www.php.net/manual/en/context.socket.php) -to the constructor like this: - -```php -$tcpConnector = new React\Socket\TcpConnector(null, array( - 'bindto' => '192.168.0.1:0' -)); -``` - -Note that this class only allows you to connect to IP-port-combinations. -If the given URI is invalid, does not contain a valid IP address and port -or contains any other scheme, it will reject with an -`InvalidArgumentException`: - -If the given URI appears to be valid, but connecting to it fails (such as if -the remote host rejects the connection etc.), it will reject with a -`RuntimeException`. - -If you want to connect to hostname-port-combinations, see also the following chapter. - -> Advanced usage: Internally, the `TcpConnector` allocates an empty *context* -resource for each stream resource. -If the destination URI contains a `hostname` query parameter, its value will -be used to set up the TLS peer name. -This is used by the `SecureConnector` and `DnsConnector` to verify the peer -name and can also be used if you want a custom TLS peer name. - -#### HappyEyeBallsConnector - -The `HappyEyeBallsConnector` class implements the -[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext -TCP/IP connections to any hostname-port-combination. Internally it implements the -happy eyeballs algorithm from [`RFC6555`](https://tools.ietf.org/html/rfc6555) and -[`RFC8305`](https://tools.ietf.org/html/rfc8305) to support IPv6 and IPv4 hostnames. - -It does so by decorating a given `TcpConnector` instance so that it first -looks up the given domain name via DNS (if applicable) and then establishes the -underlying TCP/IP connection to the resolved target IP address. - -Make sure to set up your DNS resolver and underlying TCP connector like this: - -```php -$dnsResolverFactory = new React\Dns\Resolver\Factory(); -$dns = $dnsResolverFactory->createCached('8.8.8.8'); - -$dnsConnector = new React\Socket\HappyEyeBallsConnector(null, $tcpConnector, $dns); - -$dnsConnector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -See also the [examples](examples). - -Pending connection attempts can be cancelled by cancelling its pending promise like so: - -```php -$promise = $dnsConnector->connect('www.google.com:80'); - -$promise->cancel(); -``` - -Calling `cancel()` on a pending promise will cancel the underlying DNS lookups -and/or the underlying TCP/IP connection(s) and reject the resulting promise. - -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. - -> Advanced usage: Internally, the `HappyEyeBallsConnector` relies on a `Resolver` to -look up the IP addresses for the given hostname. -It will then replace the hostname in the destination URI with this IP's and -append a `hostname` query parameter and pass this updated URI to the underlying -connector. -The Happy Eye Balls algorithm describes looking the IPv6 and IPv4 address for -the given hostname so this connector sends out two DNS lookups for the A and -AAAA records. It then uses all IP addresses (both v6 and v4) and tries to -connect to all of them with a 50ms interval in between. Alterating between IPv6 -and IPv4 addresses. When a connection is established all the other DNS lookups -and connection attempts are cancelled. - -#### DnsConnector - -The `DnsConnector` class implements the -[`ConnectorInterface`](#connectorinterface) and allows you to create plaintext -TCP/IP connections to any hostname-port-combination. - -It does so by decorating a given `TcpConnector` instance so that it first -looks up the given domain name via DNS (if applicable) and then establishes the -underlying TCP/IP connection to the resolved target IP address. - -Make sure to set up your DNS resolver and underlying TCP connector like this: - -```php -$dnsResolverFactory = new React\Dns\Resolver\Factory(); -$dns = $dnsResolverFactory->createCached('8.8.8.8'); - -$dnsConnector = new React\Socket\DnsConnector($tcpConnector, $dns); - -$dnsConnector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write('...'); - $connection->end(); -}); -``` - -See also the [examples](examples). - -Pending connection attempts can be cancelled by cancelling its pending promise like so: - -```php -$promise = $dnsConnector->connect('www.google.com:80'); - -$promise->cancel(); -``` - -Calling `cancel()` on a pending promise will cancel the underlying DNS lookup -and/or the underlying TCP/IP connection and reject the resulting promise. - -> Advanced usage: Internally, the `DnsConnector` relies on a `React\Dns\Resolver\ResolverInterface` -to look up the IP address for the given hostname. -It will then replace the hostname in the destination URI with this IP and -append a `hostname` query parameter and pass this updated URI to the underlying -connector. -The underlying connector is thus responsible for creating a connection to the -target IP address, while this query parameter can be used to check the original -hostname and is used by the `TcpConnector` to set up the TLS peer name. -If a `hostname` is given explicitly, this query parameter will not be modified, -which can be useful if you want a custom TLS peer name. - -#### SecureConnector - -The `SecureConnector` class implements the -[`ConnectorInterface`](#connectorinterface) and allows you to create secure -TLS (formerly known as SSL) connections to any hostname-port-combination. - -It does so by decorating a given `DnsConnector` instance so that it first -creates a plaintext TCP/IP connection and then enables TLS encryption on this -stream. - -```php -$secureConnector = new React\Socket\SecureConnector($dnsConnector); - -$secureConnector->connect('www.google.com:443')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n"); - ... -}); -``` - -See also the [examples](examples). - -Pending connection attempts can be cancelled by cancelling its pending promise like so: - -```php -$promise = $secureConnector->connect('www.google.com:443'); - -$promise->cancel(); -``` - -Calling `cancel()` on a pending promise will cancel the underlying TCP/IP -connection and/or the SSL/TLS negotiation and reject the resulting promise. - -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. - -You can optionally pass additional -[SSL context options](https://www.php.net/manual/en/context.ssl.php) -to the constructor like this: - -```php -$secureConnector = new React\Socket\SecureConnector($dnsConnector, null, array( - 'verify_peer' => false, - 'verify_peer_name' => false -)); -``` - -By default, this connector supports TLSv1.0+ and excludes support for legacy -SSLv2/SSLv3. As of PHP 5.6+ you can also explicitly choose the TLS version you -want to negotiate with the remote side: - -```php -$secureConnector = new React\Socket\SecureConnector($dnsConnector, null, array( - 'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT -)); -``` - -> Advanced usage: Internally, the `SecureConnector` relies on setting up the -required *context options* on the underlying stream resource. -It should therefor be used with a `TcpConnector` somewhere in the connector -stack so that it can allocate an empty *context* resource for each stream -resource and verify the peer name. -Failing to do so may result in a TLS peer name mismatch error or some hard to -trace race conditions, because all stream resources will use a single, shared -*default context* resource otherwise. - -#### TimeoutConnector - -The `TimeoutConnector` class implements the -[`ConnectorInterface`](#connectorinterface) and allows you to add timeout -handling to any existing connector instance. - -It does so by decorating any given [`ConnectorInterface`](#connectorinterface) -instance and starting a timer that will automatically reject and abort any -underlying connection attempt if it takes too long. - -```php -$timeoutConnector = new React\Socket\TimeoutConnector($connector, 3.0); - -$timeoutConnector->connect('google.com:80')->then(function (React\Socket\ConnectionInterface $connection) { - // connection succeeded within 3.0 seconds -}); -``` - -See also any of the [examples](examples). - -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. - -Pending connection attempts can be cancelled by cancelling its pending promise like so: - -```php -$promise = $timeoutConnector->connect('google.com:80'); - -$promise->cancel(); -``` - -Calling `cancel()` on a pending promise will cancel the underlying connection -attempt, abort the timer and reject the resulting promise. - -#### UnixConnector - -The `UnixConnector` class implements the -[`ConnectorInterface`](#connectorinterface) and allows you to connect to -Unix domain socket (UDS) paths like this: - -```php -$connector = new React\Socket\UnixConnector(); - -$connector->connect('/tmp/demo.sock')->then(function (React\Socket\ConnectionInterface $connection) { - $connection->write("HELLO\n"); -}); -``` - -Connecting to Unix domain sockets is an atomic operation, i.e. its promise will -settle (either resolve or reject) immediately. -As such, calling `cancel()` on the resulting promise has no effect. - -> The [`getRemoteAddress()`](#getremoteaddress) method will return the target - Unix domain socket (UDS) path as given to the `connect()` method, prepended - with the `unix://` scheme, for example `unix:///tmp/demo.sock`. - The [`getLocalAddress()`](#getlocaladdress) method will most likely return a - `null` value as this value is not applicable to UDS connections here. - -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. - -#### FixedUriConnector - -The `FixedUriConnector` class implements the -[`ConnectorInterface`](#connectorinterface) and decorates an existing Connector -to always use a fixed, preconfigured URI. - -This can be useful for consumers that do not support certain URIs, such as -when you want to explicitly connect to a Unix domain socket (UDS) path -instead of connecting to a default address assumed by an higher-level API: - -```php -$connector = new React\Socket\FixedUriConnector( - 'unix:///var/run/docker.sock', - new React\Socket\UnixConnector() -); - -// destination will be ignored, actually connects to Unix domain socket -$promise = $connector->connect('localhost:80'); -``` - -## Install - -The recommended way to install this library is [through Composer](https://getcomposer.org/). -[New to Composer?](https://getcomposer.org/doc/00-intro.md) - -This project follows [SemVer](https://semver.org/). -This will install the latest supported version: - -```bash -composer require react/socket:^1.16 -``` - -See also the [CHANGELOG](CHANGELOG.md) for details about version upgrades. - -This project aims to run on any platform and thus does not require any PHP -extensions and supports running on legacy PHP 5.3 through current PHP 8+ and HHVM. -It's *highly recommended to use the latest supported PHP version* for this project, -partly due to its vast performance improvements and partly because legacy PHP -versions require several workarounds as described below. - -Secure TLS connections received some major upgrades starting with PHP 5.6, with -the defaults now being more secure, while older versions required explicit -context options. -This library does not take responsibility over these context options, so it's -up to consumers of this library to take care of setting appropriate context -options as described above. - -PHP < 7.3.3 (and PHP < 7.2.15) suffers from a bug where feof() might -block with 100% CPU usage on fragmented TLS records. -We try to work around this by always consuming the complete receive -buffer at once to avoid stale data in TLS buffers. This is known to -work around high CPU usage for well-behaving peers, but this may -cause very large data chunks for high throughput scenarios. The buggy -behavior can still be triggered due to network I/O buffers or -malicious peers on affected versions, upgrading is highly recommended. - -PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big -chunks of data over TLS streams at once. -We try to work around this by limiting the write chunk size to 8192 -bytes for older PHP versions only. -This is only a work-around and has a noticable performance penalty on -affected versions. - -This project also supports running on HHVM. -Note that really old HHVM < 3.8 does not support secure TLS connections, as it -lacks the required `stream_socket_enable_crypto()` function. -As such, trying to create a secure TLS connections on affected versions will -return a rejected promise instead. -This issue is also covered by our test suite, which will skip related tests -on affected versions. - -## Tests - -To run the test suite, you first need to clone this repo and then install all -dependencies [through Composer](https://getcomposer.org/): - -```bash -composer install -``` - -To run the test suite, go to the project root and run: - -```bash -vendor/bin/phpunit -``` - -The test suite also contains a number of functional integration tests that rely -on a stable internet connection. -If you do not want to run these, they can simply be skipped like this: - -```bash -vendor/bin/phpunit --exclude-group internet -``` - -## License - -MIT, see [LICENSE file](LICENSE). diff --git a/vendor/react/socket/composer.json b/vendor/react/socket/composer.json deleted file mode 100644 index b1e1d25..0000000 --- a/vendor/react/socket/composer.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "react/socket", - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": ["async", "socket", "stream", "connection", "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", - "react/dns": "^1.13", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.6 || ^1.2.1", - "react/stream": "^1.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3.3 || ^2", - "react/promise-stream": "^1.4", - "react/promise-timer": "^1.11" - }, - "autoload": { - "psr-4": { - "React\\Socket\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "React\\Tests\\Socket\\": "tests/" - } - } -} diff --git a/vendor/react/socket/src/Connection.php b/vendor/react/socket/src/Connection.php deleted file mode 100644 index 65ae26b..0000000 --- a/vendor/react/socket/src/Connection.php +++ /dev/null @@ -1,183 +0,0 @@ -= 70300 && \PHP_VERSION_ID < 70303); - - // PHP < 7.1.4 (and PHP < 7.0.18) suffers from a bug when writing big - // chunks of data over TLS streams at once. - // We try to work around this by limiting the write chunk size to 8192 - // bytes for older PHP versions only. - // This is only a work-around and has a noticable performance penalty on - // affected versions. Please update your PHP version. - // This applies to all streams because TLS may be enabled later on. - // See https://github.com/reactphp/socket/issues/105 - $limitWriteChunks = (\PHP_VERSION_ID < 70018 || (\PHP_VERSION_ID >= 70100 && \PHP_VERSION_ID < 70104)); - - $this->input = new DuplexResourceStream( - $resource, - $loop, - $clearCompleteBuffer ? -1 : null, - new WritableResourceStream($resource, $loop, null, $limitWriteChunks ? 8192 : null) - ); - - $this->stream = $resource; - - Util::forwardEvents($this->input, $this, array('data', 'end', 'error', 'close', 'pipe', 'drain')); - - $this->input->on('close', array($this, 'close')); - } - - public function isReadable() - { - return $this->input->isReadable(); - } - - public function isWritable() - { - return $this->input->isWritable(); - } - - public function pause() - { - $this->input->pause(); - } - - public function resume() - { - $this->input->resume(); - } - - public function pipe(WritableStreamInterface $dest, array $options = array()) - { - return $this->input->pipe($dest, $options); - } - - public function write($data) - { - return $this->input->write($data); - } - - public function end($data = null) - { - $this->input->end($data); - } - - public function close() - { - $this->input->close(); - $this->handleClose(); - $this->removeAllListeners(); - } - - public function handleClose() - { - if (!\is_resource($this->stream)) { - return; - } - - // Try to cleanly shut down socket and ignore any errors in case other - // side already closed. Underlying Stream implementation will take care - // of closing stream resource, so we otherwise keep this open here. - @\stream_socket_shutdown($this->stream, \STREAM_SHUT_RDWR); - } - - public function getRemoteAddress() - { - if (!\is_resource($this->stream)) { - return null; - } - - return $this->parseAddress(\stream_socket_get_name($this->stream, true)); - } - - public function getLocalAddress() - { - if (!\is_resource($this->stream)) { - return null; - } - - return $this->parseAddress(\stream_socket_get_name($this->stream, false)); - } - - private function parseAddress($address) - { - if ($address === false) { - return null; - } - - if ($this->unix) { - // remove trailing colon from address for HHVM < 3.19: https://3v4l.org/5C1lo - // note that technically ":" is a valid address, so keep this in place otherwise - if (\substr($address, -1) === ':' && \defined('HHVM_VERSION_ID') && \HHVM_VERSION_ID < 31900) { - $address = (string)\substr($address, 0, -1); // @codeCoverageIgnore - } - - // work around unknown addresses should return null value: https://3v4l.org/5C1lo and https://bugs.php.net/bug.php?id=74556 - // PHP uses "\0" string and HHVM uses empty string (colon removed above) - if ($address === '' || $address[0] === "\x00" ) { - return null; // @codeCoverageIgnore - } - - return 'unix://' . $address; - } - - // check if this is an IPv6 address which includes multiple colons but no square brackets - $pos = \strrpos($address, ':'); - if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') { - $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore - } - - return ($this->encryptionEnabled ? 'tls' : 'tcp') . '://' . $address; - } -} diff --git a/vendor/react/socket/src/ConnectionInterface.php b/vendor/react/socket/src/ConnectionInterface.php deleted file mode 100644 index 64613b5..0000000 --- a/vendor/react/socket/src/ConnectionInterface.php +++ /dev/null @@ -1,119 +0,0 @@ -on('data', function ($chunk) { - * echo $chunk; - * }); - * - * $connection->on('end', function () { - * echo 'ended'; - * }); - * - * $connection->on('error', function (Exception $e) { - * echo 'error: ' . $e->getMessage(); - * }); - * - * $connection->on('close', function () { - * echo 'closed'; - * }); - * - * $connection->write($data); - * $connection->end($data = null); - * $connection->close(); - * // … - * ``` - * - * For more details, see the - * [`DuplexStreamInterface`](https://github.com/reactphp/stream#duplexstreaminterface). - * - * @see DuplexStreamInterface - * @see ServerInterface - * @see ConnectorInterface - */ -interface ConnectionInterface extends DuplexStreamInterface -{ - /** - * Returns the full remote address (URI) where this connection has been established with - * - * ```php - * $address = $connection->getRemoteAddress(); - * echo 'Connection with ' . $address . PHP_EOL; - * ``` - * - * If the remote address can not be determined or is unknown at this time (such as - * after the connection has been closed), it MAY return a `NULL` value instead. - * - * Otherwise, it will return the full address (URI) as a string value, such - * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`, - * `unix://example.sock` or `unix:///path/to/example.sock`. - * Note that individual URI components are application specific and depend - * on the underlying transport protocol. - * - * If this is a TCP/IP based connection and you only want the remote IP, you may - * use something like this: - * - * ```php - * $address = $connection->getRemoteAddress(); - * $ip = trim(parse_url($address, PHP_URL_HOST), '[]'); - * echo 'Connection with ' . $ip . PHP_EOL; - * ``` - * - * @return ?string remote address (URI) or null if unknown - */ - public function getRemoteAddress(); - - /** - * Returns the full local address (full URI with scheme, IP and port) where this connection has been established with - * - * ```php - * $address = $connection->getLocalAddress(); - * echo 'Connection with ' . $address . PHP_EOL; - * ``` - * - * If the local address can not be determined or is unknown at this time (such as - * after the connection has been closed), it MAY return a `NULL` value instead. - * - * Otherwise, it will return the full address (URI) as a string value, such - * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80`, `tls://127.0.0.1:443`, - * `unix://example.sock` or `unix:///path/to/example.sock`. - * Note that individual URI components are application specific and depend - * on the underlying transport protocol. - * - * This method complements the [`getRemoteAddress()`](#getremoteaddress) method, - * so they should not be confused. - * - * If your `TcpServer` instance is listening on multiple interfaces (e.g. using - * the address `0.0.0.0`), you can use this method to find out which interface - * actually accepted this connection (such as a public or local interface). - * - * If your system has multiple interfaces (e.g. a WAN and a LAN interface), - * you can use this method to find out which interface was actually - * used for this connection. - * - * @return ?string local address (URI) or null if unknown - * @see self::getRemoteAddress() - */ - public function getLocalAddress(); -} diff --git a/vendor/react/socket/src/Connector.php b/vendor/react/socket/src/Connector.php deleted file mode 100644 index 15faa46..0000000 --- a/vendor/react/socket/src/Connector.php +++ /dev/null @@ -1,236 +0,0 @@ - true, - 'tls' => true, - 'unix' => true, - - 'dns' => true, - 'timeout' => true, - 'happy_eyeballs' => true, - ); - - if ($context['timeout'] === true) { - $context['timeout'] = (float)\ini_get("default_socket_timeout"); - } - - if ($context['tcp'] instanceof ConnectorInterface) { - $tcp = $context['tcp']; - } else { - $tcp = new TcpConnector( - $loop, - \is_array($context['tcp']) ? $context['tcp'] : array() - ); - } - - if ($context['dns'] !== false) { - if ($context['dns'] instanceof ResolverInterface) { - $resolver = $context['dns']; - } else { - if ($context['dns'] !== true) { - $config = $context['dns']; - } else { - // try to load nameservers from system config or default to Google's public DNS - $config = DnsConfig::loadSystemConfigBlocking(); - if (!$config->nameservers) { - $config->nameservers[] = '8.8.8.8'; // @codeCoverageIgnore - } - } - - $factory = new DnsFactory(); - $resolver = $factory->createCached( - $config, - $loop - ); - } - - if ($context['happy_eyeballs'] === true) { - $tcp = new HappyEyeBallsConnector($loop, $tcp, $resolver); - } else { - $tcp = new DnsConnector($tcp, $resolver); - } - } - - if ($context['tcp'] !== false) { - $context['tcp'] = $tcp; - - if ($context['timeout'] !== false) { - $context['tcp'] = new TimeoutConnector( - $context['tcp'], - $context['timeout'], - $loop - ); - } - - $this->connectors['tcp'] = $context['tcp']; - } - - if ($context['tls'] !== false) { - if (!$context['tls'] instanceof ConnectorInterface) { - $context['tls'] = new SecureConnector( - $tcp, - $loop, - \is_array($context['tls']) ? $context['tls'] : array() - ); - } - - if ($context['timeout'] !== false) { - $context['tls'] = new TimeoutConnector( - $context['tls'], - $context['timeout'], - $loop - ); - } - - $this->connectors['tls'] = $context['tls']; - } - - if ($context['unix'] !== false) { - if (!$context['unix'] instanceof ConnectorInterface) { - $context['unix'] = new UnixConnector($loop); - } - $this->connectors['unix'] = $context['unix']; - } - } - - public function connect($uri) - { - $scheme = 'tcp'; - if (\strpos($uri, '://') !== false) { - $scheme = (string)\substr($uri, 0, \strpos($uri, '://')); - } - - if (!isset($this->connectors[$scheme])) { - return \React\Promise\reject(new \RuntimeException( - 'No connector available for URI scheme "' . $scheme . '" (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - )); - } - - return $this->connectors[$scheme]->connect($uri); - } - - - /** - * [internal] Builds on URI from the given URI parts and ip address with original hostname as query - * - * @param array $parts - * @param string $host - * @param string $ip - * @return string - * @internal - */ - public static function uri(array $parts, $host, $ip) - { - $uri = ''; - - // prepend original scheme if known - if (isset($parts['scheme'])) { - $uri .= $parts['scheme'] . '://'; - } - - if (\strpos($ip, ':') !== false) { - // enclose IPv6 addresses in square brackets before appending port - $uri .= '[' . $ip . ']'; - } else { - $uri .= $ip; - } - - // append original port if known - if (isset($parts['port'])) { - $uri .= ':' . $parts['port']; - } - - // append orignal path if known - if (isset($parts['path'])) { - $uri .= $parts['path']; - } - - // append original query if known - if (isset($parts['query'])) { - $uri .= '?' . $parts['query']; - } - - // append original hostname as query if resolved via DNS and if - // destination URI does not contain "hostname" query param already - $args = array(); - \parse_str(isset($parts['query']) ? $parts['query'] : '', $args); - if ($host !== $ip && !isset($args['hostname'])) { - $uri .= (isset($parts['query']) ? '&' : '?') . 'hostname=' . \rawurlencode($host); - } - - // append original fragment if known - if (isset($parts['fragment'])) { - $uri .= '#' . $parts['fragment']; - } - - return $uri; - } -} diff --git a/vendor/react/socket/src/ConnectorInterface.php b/vendor/react/socket/src/ConnectorInterface.php deleted file mode 100644 index 1f07b75..0000000 --- a/vendor/react/socket/src/ConnectorInterface.php +++ /dev/null @@ -1,59 +0,0 @@ -connect('google.com:443')->then( - * function (React\Socket\ConnectionInterface $connection) { - * // connection successfully established - * }, - * function (Exception $error) { - * // failed to connect due to $error - * } - * ); - * ``` - * - * The returned Promise MUST be implemented in such a way that it can be - * cancelled when it is still pending. Cancelling a pending promise MUST - * reject its value with an Exception. It SHOULD clean up any underlying - * resources and references as applicable. - * - * ```php - * $promise = $connector->connect($uri); - * - * $promise->cancel(); - * ``` - * - * @param string $uri - * @return \React\Promise\PromiseInterface - * Resolves with a `ConnectionInterface` on success or rejects with an `Exception` on error. - * @see ConnectionInterface - */ - public function connect($uri); -} diff --git a/vendor/react/socket/src/DnsConnector.php b/vendor/react/socket/src/DnsConnector.php deleted file mode 100644 index d2fb2c7..0000000 --- a/vendor/react/socket/src/DnsConnector.php +++ /dev/null @@ -1,117 +0,0 @@ -connector = $connector; - $this->resolver = $resolver; - } - - public function connect($uri) - { - $original = $uri; - if (\strpos($uri, '://') === false) { - $uri = 'tcp://' . $uri; - $parts = \parse_url($uri); - if (isset($parts['scheme'])) { - unset($parts['scheme']); - } - } else { - $parts = \parse_url($uri); - } - - if (!$parts || !isset($parts['host'])) { - return Promise\reject(new \InvalidArgumentException( - 'Given URI "' . $original . '" is invalid (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - )); - } - - $host = \trim($parts['host'], '[]'); - $connector = $this->connector; - - // skip DNS lookup / URI manipulation if this URI already contains an IP - if (@\inet_pton($host) !== false) { - return $connector->connect($original); - } - - $promise = $this->resolver->resolve($host); - $resolved = null; - - return new Promise\Promise( - function ($resolve, $reject) use (&$promise, &$resolved, $uri, $connector, $host, $parts) { - // resolve/reject with result of DNS lookup - $promise->then(function ($ip) use (&$promise, &$resolved, $uri, $connector, $host, $parts) { - $resolved = $ip; - - return $promise = $connector->connect( - Connector::uri($parts, $host, $ip) - )->then(null, function (\Exception $e) use ($uri) { - if ($e instanceof \RuntimeException) { - $message = \preg_replace('/^(Connection to [^ ]+)[&?]hostname=[^ &]+/', '$1', $e->getMessage()); - $e = new \RuntimeException( - 'Connection to ' . $uri . ' failed: ' . $message, - $e->getCode(), - $e - ); - - // avoid garbage references by replacing all closures in call stack. - // what a lovely piece of code! - $r = new \ReflectionProperty('Exception', 'trace'); - $r->setAccessible(true); - $trace = $r->getValue($e); - - // Exception trace arguments are not available on some PHP 7.4 installs - // @codeCoverageIgnoreStart - foreach ($trace as $ti => $one) { - if (isset($one['args'])) { - foreach ($one['args'] as $ai => $arg) { - if ($arg instanceof \Closure) { - $trace[$ti]['args'][$ai] = 'Object(' . \get_class($arg) . ')'; - } - } - } - } - // @codeCoverageIgnoreEnd - $r->setValue($e, $trace); - } - - throw $e; - }); - }, function ($e) use ($uri, $reject) { - $reject(new \RuntimeException('Connection to ' . $uri .' failed during DNS lookup: ' . $e->getMessage(), 0, $e)); - })->then($resolve, $reject); - }, - function ($_, $reject) use (&$promise, &$resolved, $uri) { - // cancellation should reject connection attempt - // reject DNS resolution with custom reason, otherwise rely on connection cancellation below - if ($resolved === null) { - $reject(new \RuntimeException( - 'Connection to ' . $uri . ' cancelled during DNS lookup (ECONNABORTED)', - \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103 - )); - } - - // (try to) cancel pending DNS lookup / connection attempt - if ($promise instanceof PromiseInterface && \method_exists($promise, 'cancel')) { - // overwrite callback arguments for PHP7+ only, so they do not show - // up in the Exception trace and do not cause a possible cyclic reference. - $_ = $reject = null; - - $promise->cancel(); - $promise = null; - } - } - ); - } -} diff --git a/vendor/react/socket/src/FdServer.php b/vendor/react/socket/src/FdServer.php deleted file mode 100644 index 8e46719..0000000 --- a/vendor/react/socket/src/FdServer.php +++ /dev/null @@ -1,222 +0,0 @@ -on('connection', function (ConnectionInterface $connection) { - * echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL; - * $connection->write('hello there!' . PHP_EOL); - * … - * }); - * ``` - * - * See also the `ServerInterface` for more details. - * - * @see ServerInterface - * @see ConnectionInterface - * @internal - */ -final class FdServer extends EventEmitter implements ServerInterface -{ - private $master; - private $loop; - private $unix = false; - private $listening = false; - - /** - * Creates a socket server and starts listening on the given file descriptor - * - * This starts accepting new incoming connections on the given file descriptor. - * See also the `connection event` documented in the `ServerInterface` - * for more details. - * - * ```php - * $socket = new React\Socket\FdServer(3); - * ``` - * - * If the given FD is invalid or out of range, it will throw an `InvalidArgumentException`: - * - * ```php - * // throws InvalidArgumentException - * $socket = new React\Socket\FdServer(-1); - * ``` - * - * If the given FD appears to be valid, but listening on it fails (such as - * if the FD does not exist or does not refer to a socket server), it will - * throw a `RuntimeException`: - * - * ```php - * // throws RuntimeException because FD does not reference a socket server - * $socket = new React\Socket\FdServer(0, $loop); - * ``` - * - * Note that these error conditions may vary depending on your system and/or - * configuration. - * See the exception message and code for more details about the actual error - * condition. - * - * @param int|string $fd FD number such as `3` or as URL in the form of `php://fd/3` - * @param ?LoopInterface $loop - * @throws \InvalidArgumentException if the listening address is invalid - * @throws \RuntimeException if listening on this address fails (already in use etc.) - */ - public function __construct($fd, $loop = null) - { - if (\preg_match('#^php://fd/(\d+)$#', $fd, $m)) { - $fd = (int) $m[1]; - } - if (!\is_int($fd) || $fd < 0 || $fd >= \PHP_INT_MAX) { - throw new \InvalidArgumentException( - 'Invalid FD number given (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - ); - } - - if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 - throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); - } - - $this->loop = $loop ?: Loop::get(); - - $errno = 0; - $errstr = ''; - \set_error_handler(function ($_, $error) use (&$errno, &$errstr) { - // Match errstr from PHP's warning message. - // fopen(php://fd/3): Failed to open stream: Error duping file descriptor 3; possibly it doesn't exist: [9]: Bad file descriptor - \preg_match('/\[(\d+)\]: (.*)/', $error, $m); - $errno = isset($m[1]) ? (int) $m[1] : 0; - $errstr = isset($m[2]) ? $m[2] : $error; - }); - - $this->master = \fopen('php://fd/' . $fd, 'r+'); - - \restore_error_handler(); - - if (false === $this->master) { - throw new \RuntimeException( - 'Failed to listen on FD ' . $fd . ': ' . $errstr . SocketServer::errconst($errno), - $errno - ); - } - - $meta = \stream_get_meta_data($this->master); - if (!isset($meta['stream_type']) || $meta['stream_type'] !== 'tcp_socket') { - \fclose($this->master); - - $errno = \defined('SOCKET_ENOTSOCK') ? \SOCKET_ENOTSOCK : 88; - $errstr = \function_exists('socket_strerror') ? \socket_strerror($errno) : 'Not a socket'; - - throw new \RuntimeException( - 'Failed to listen on FD ' . $fd . ': ' . $errstr . ' (ENOTSOCK)', - $errno - ); - } - - // Socket should not have a peer address if this is a listening socket. - // Looks like this work-around is the closest we can get because PHP doesn't expose SO_ACCEPTCONN even with ext-sockets. - if (\stream_socket_get_name($this->master, true) !== false) { - \fclose($this->master); - - $errno = \defined('SOCKET_EISCONN') ? \SOCKET_EISCONN : 106; - $errstr = \function_exists('socket_strerror') ? \socket_strerror($errno) : 'Socket is connected'; - - throw new \RuntimeException( - 'Failed to listen on FD ' . $fd . ': ' . $errstr . ' (EISCONN)', - $errno - ); - } - - // Assume this is a Unix domain socket (UDS) when its listening address doesn't parse as a valid URL with a port. - // Looks like this work-around is the closest we can get because PHP doesn't expose SO_DOMAIN even with ext-sockets. - $this->unix = \parse_url($this->getAddress(), \PHP_URL_PORT) === false; - - \stream_set_blocking($this->master, false); - - $this->resume(); - } - - public function getAddress() - { - if (!\is_resource($this->master)) { - return null; - } - - $address = \stream_socket_get_name($this->master, false); - - if ($this->unix === true) { - return 'unix://' . $address; - } - - // check if this is an IPv6 address which includes multiple colons but no square brackets - $pos = \strrpos($address, ':'); - if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') { - $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore - } - - return 'tcp://' . $address; - } - - public function pause() - { - if (!$this->listening) { - return; - } - - $this->loop->removeReadStream($this->master); - $this->listening = false; - } - - public function resume() - { - if ($this->listening || !\is_resource($this->master)) { - return; - } - - $that = $this; - $this->loop->addReadStream($this->master, function ($master) use ($that) { - try { - $newSocket = SocketServer::accept($master); - } catch (\RuntimeException $e) { - $that->emit('error', array($e)); - return; - } - $that->handleConnection($newSocket); - }); - $this->listening = true; - } - - public function close() - { - if (!\is_resource($this->master)) { - return; - } - - $this->pause(); - \fclose($this->master); - $this->removeAllListeners(); - } - - /** @internal */ - public function handleConnection($socket) - { - $connection = new Connection($socket, $this->loop); - $connection->unix = $this->unix; - - $this->emit('connection', array($connection)); - } -} diff --git a/vendor/react/socket/src/FixedUriConnector.php b/vendor/react/socket/src/FixedUriConnector.php deleted file mode 100644 index f83241d..0000000 --- a/vendor/react/socket/src/FixedUriConnector.php +++ /dev/null @@ -1,41 +0,0 @@ -connect('localhost:80'); - * ``` - */ -class FixedUriConnector implements ConnectorInterface -{ - private $uri; - private $connector; - - /** - * @param string $uri - * @param ConnectorInterface $connector - */ - public function __construct($uri, ConnectorInterface $connector) - { - $this->uri = $uri; - $this->connector = $connector; - } - - public function connect($_) - { - return $this->connector->connect($this->uri); - } -} diff --git a/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php b/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php deleted file mode 100644 index d4f05e8..0000000 --- a/vendor/react/socket/src/HappyEyeBallsConnectionBuilder.php +++ /dev/null @@ -1,334 +0,0 @@ - false, - Message::TYPE_AAAA => false, - ); - public $resolverPromises = array(); - public $connectionPromises = array(); - public $connectQueue = array(); - public $nextAttemptTimer; - public $parts; - public $ipsCount = 0; - public $failureCount = 0; - public $resolve; - public $reject; - - public $lastErrorFamily; - public $lastError6; - public $lastError4; - - public function __construct(LoopInterface $loop, ConnectorInterface $connector, ResolverInterface $resolver, $uri, $host, $parts) - { - $this->loop = $loop; - $this->connector = $connector; - $this->resolver = $resolver; - $this->uri = $uri; - $this->host = $host; - $this->parts = $parts; - } - - public function connect() - { - $that = $this; - return new Promise\Promise(function ($resolve, $reject) use ($that) { - $lookupResolve = function ($type) use ($that, $resolve, $reject) { - return function (array $ips) use ($that, $type, $resolve, $reject) { - unset($that->resolverPromises[$type]); - $that->resolved[$type] = true; - - $that->mixIpsIntoConnectQueue($ips); - - // start next connection attempt if not already awaiting next - if ($that->nextAttemptTimer === null && $that->connectQueue) { - $that->check($resolve, $reject); - } - }; - }; - - $that->resolverPromises[Message::TYPE_AAAA] = $that->resolve(Message::TYPE_AAAA, $reject)->then($lookupResolve(Message::TYPE_AAAA)); - $that->resolverPromises[Message::TYPE_A] = $that->resolve(Message::TYPE_A, $reject)->then(function (array $ips) use ($that) { - // happy path: IPv6 has resolved already (or could not resolve), continue with IPv4 addresses - if ($that->resolved[Message::TYPE_AAAA] === true || !$ips) { - return $ips; - } - - // Otherwise delay processing IPv4 lookup until short timer passes or IPv6 resolves in the meantime - $deferred = new Promise\Deferred(function () use (&$ips) { - // discard all IPv4 addresses if cancelled - $ips = array(); - }); - $timer = $that->loop->addTimer($that::RESOLUTION_DELAY, function () use ($deferred, $ips) { - $deferred->resolve($ips); - }); - - $that->resolverPromises[Message::TYPE_AAAA]->then(function () use ($that, $timer, $deferred, &$ips) { - $that->loop->cancelTimer($timer); - $deferred->resolve($ips); - }); - - return $deferred->promise(); - })->then($lookupResolve(Message::TYPE_A)); - }, function ($_, $reject) use ($that) { - $reject(new \RuntimeException( - 'Connection to ' . $that->uri . ' cancelled' . (!$that->connectionPromises ? ' during DNS lookup' : '') . ' (ECONNABORTED)', - \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103 - )); - $_ = $reject = null; - - $that->cleanUp(); - }); - } - - /** - * @internal - * @param int $type DNS query type - * @param callable $reject - * @return \React\Promise\PromiseInterface Returns a promise that - * always resolves with a list of IP addresses on success or an empty - * list on error. - */ - public function resolve($type, $reject) - { - $that = $this; - return $that->resolver->resolveAll($that->host, $type)->then(null, function (\Exception $e) use ($type, $reject, $that) { - unset($that->resolverPromises[$type]); - $that->resolved[$type] = true; - - if ($type === Message::TYPE_A) { - $that->lastError4 = $e->getMessage(); - $that->lastErrorFamily = 4; - } else { - $that->lastError6 = $e->getMessage(); - $that->lastErrorFamily = 6; - } - - // cancel next attempt timer when there are no more IPs to connect to anymore - if ($that->nextAttemptTimer !== null && !$that->connectQueue) { - $that->loop->cancelTimer($that->nextAttemptTimer); - $that->nextAttemptTimer = null; - } - - if ($that->hasBeenResolved() && $that->ipsCount === 0) { - $reject(new \RuntimeException( - $that->error(), - 0, - $e - )); - } - - // Exception already handled above, so don't throw an unhandled rejection here - return array(); - }); - } - - /** - * @internal - */ - public function check($resolve, $reject) - { - $ip = \array_shift($this->connectQueue); - - // start connection attempt and remember array position to later unset again - $this->connectionPromises[] = $this->attemptConnection($ip); - \end($this->connectionPromises); - $index = \key($this->connectionPromises); - - $that = $this; - $that->connectionPromises[$index]->then(function ($connection) use ($that, $index, $resolve) { - unset($that->connectionPromises[$index]); - - $that->cleanUp(); - - $resolve($connection); - }, function (\Exception $e) use ($that, $index, $ip, $resolve, $reject) { - unset($that->connectionPromises[$index]); - - $that->failureCount++; - - $message = \preg_replace('/^(Connection to [^ ]+)[&?]hostname=[^ &]+/', '$1', $e->getMessage()); - if (\strpos($ip, ':') === false) { - $that->lastError4 = $message; - $that->lastErrorFamily = 4; - } else { - $that->lastError6 = $message; - $that->lastErrorFamily = 6; - } - - // start next connection attempt immediately on error - if ($that->connectQueue) { - if ($that->nextAttemptTimer !== null) { - $that->loop->cancelTimer($that->nextAttemptTimer); - $that->nextAttemptTimer = null; - } - - $that->check($resolve, $reject); - } - - if ($that->hasBeenResolved() === false) { - return; - } - - if ($that->ipsCount === $that->failureCount) { - $that->cleanUp(); - - $reject(new \RuntimeException( - $that->error(), - $e->getCode(), - $e - )); - } - }); - - // Allow next connection attempt in 100ms: https://tools.ietf.org/html/rfc8305#section-5 - // Only start timer when more IPs are queued or when DNS query is still pending (might add more IPs) - if ($this->nextAttemptTimer === null && (\count($this->connectQueue) > 0 || $this->resolved[Message::TYPE_A] === false || $this->resolved[Message::TYPE_AAAA] === false)) { - $this->nextAttemptTimer = $this->loop->addTimer(self::CONNECTION_ATTEMPT_DELAY, function () use ($that, $resolve, $reject) { - $that->nextAttemptTimer = null; - - if ($that->connectQueue) { - $that->check($resolve, $reject); - } - }); - } - } - - /** - * @internal - */ - public function attemptConnection($ip) - { - $uri = Connector::uri($this->parts, $this->host, $ip); - - return $this->connector->connect($uri); - } - - /** - * @internal - */ - public function cleanUp() - { - // clear list of outstanding IPs to avoid creating new connections - $this->connectQueue = array(); - - // cancel pending connection attempts - foreach ($this->connectionPromises as $connectionPromise) { - if ($connectionPromise instanceof PromiseInterface && \method_exists($connectionPromise, 'cancel')) { - $connectionPromise->cancel(); - } - } - - // cancel pending DNS resolution (cancel IPv4 first in case it is awaiting IPv6 resolution delay) - foreach (\array_reverse($this->resolverPromises) as $resolverPromise) { - if ($resolverPromise instanceof PromiseInterface && \method_exists($resolverPromise, 'cancel')) { - $resolverPromise->cancel(); - } - } - - if ($this->nextAttemptTimer instanceof TimerInterface) { - $this->loop->cancelTimer($this->nextAttemptTimer); - $this->nextAttemptTimer = null; - } - } - - /** - * @internal - */ - public function hasBeenResolved() - { - foreach ($this->resolved as $typeHasBeenResolved) { - if ($typeHasBeenResolved === false) { - return false; - } - } - - return true; - } - - /** - * Mixes an array of IP addresses into the connect queue in such a way they alternate when attempting to connect. - * The goal behind it is first attempt to connect to IPv6, then to IPv4, then to IPv6 again until one of those - * attempts succeeds. - * - * @link https://tools.ietf.org/html/rfc8305#section-4 - * - * @internal - */ - public function mixIpsIntoConnectQueue(array $ips) - { - \shuffle($ips); - $this->ipsCount += \count($ips); - $connectQueueStash = $this->connectQueue; - $this->connectQueue = array(); - while (\count($connectQueueStash) > 0 || \count($ips) > 0) { - if (\count($ips) > 0) { - $this->connectQueue[] = \array_shift($ips); - } - if (\count($connectQueueStash) > 0) { - $this->connectQueue[] = \array_shift($connectQueueStash); - } - } - } - - /** - * @internal - * @return string - */ - public function error() - { - if ($this->lastError4 === $this->lastError6) { - $message = $this->lastError6; - } elseif ($this->lastErrorFamily === 6) { - $message = 'Last error for IPv6: ' . $this->lastError6 . '. Previous error for IPv4: ' . $this->lastError4; - } else { - $message = 'Last error for IPv4: ' . $this->lastError4 . '. Previous error for IPv6: ' . $this->lastError6; - } - - if ($this->hasBeenResolved() && $this->ipsCount === 0) { - if ($this->lastError6 === $this->lastError4) { - $message = ' during DNS lookup: ' . $this->lastError6; - } else { - $message = ' during DNS lookup. ' . $message; - } - } else { - $message = ': ' . $message; - } - - return 'Connection to ' . $this->uri . ' failed' . $message; - } -} diff --git a/vendor/react/socket/src/HappyEyeBallsConnector.php b/vendor/react/socket/src/HappyEyeBallsConnector.php deleted file mode 100644 index a5511ac..0000000 --- a/vendor/react/socket/src/HappyEyeBallsConnector.php +++ /dev/null @@ -1,80 +0,0 @@ -loop = $loop ?: Loop::get(); - $this->connector = $connector; - $this->resolver = $resolver; - } - - public function connect($uri) - { - $original = $uri; - if (\strpos($uri, '://') === false) { - $uri = 'tcp://' . $uri; - $parts = \parse_url($uri); - if (isset($parts['scheme'])) { - unset($parts['scheme']); - } - } else { - $parts = \parse_url($uri); - } - - if (!$parts || !isset($parts['host'])) { - return Promise\reject(new \InvalidArgumentException( - 'Given URI "' . $original . '" is invalid (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - )); - } - - $host = \trim($parts['host'], '[]'); - - // skip DNS lookup / URI manipulation if this URI already contains an IP - if (@\inet_pton($host) !== false) { - return $this->connector->connect($original); - } - - $builder = new HappyEyeBallsConnectionBuilder( - $this->loop, - $this->connector, - $this->resolver, - $uri, - $host, - $parts - ); - return $builder->connect(); - } -} diff --git a/vendor/react/socket/src/LimitingServer.php b/vendor/react/socket/src/LimitingServer.php deleted file mode 100644 index d19000b..0000000 --- a/vendor/react/socket/src/LimitingServer.php +++ /dev/null @@ -1,203 +0,0 @@ -on('connection', function (React\Socket\ConnectionInterface $connection) { - * $connection->write('hello there!' . PHP_EOL); - * … - * }); - * ``` - * - * See also the `ServerInterface` for more details. - * - * @see ServerInterface - * @see ConnectionInterface - */ -class LimitingServer extends EventEmitter implements ServerInterface -{ - private $connections = array(); - private $server; - private $limit; - - private $pauseOnLimit = false; - private $autoPaused = false; - private $manuPaused = false; - - /** - * Instantiates a new LimitingServer. - * - * You have to pass a maximum number of open connections to ensure - * the server will automatically reject (close) connections once this limit - * is exceeded. In this case, it will emit an `error` event to inform about - * this and no `connection` event will be emitted. - * - * ```php - * $server = new React\Socket\LimitingServer($server, 100); - * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { - * $connection->write('hello there!' . PHP_EOL); - * … - * }); - * ``` - * - * You MAY pass a `null` limit in order to put no limit on the number of - * open connections and keep accepting new connection until you run out of - * operating system resources (such as open file handles). This may be - * useful if you do not want to take care of applying a limit but still want - * to use the `getConnections()` method. - * - * You can optionally configure the server to pause accepting new - * connections once the connection limit is reached. In this case, it will - * pause the underlying server and no longer process any new connections at - * all, thus also no longer closing any excessive connections. - * The underlying operating system is responsible for keeping a backlog of - * pending connections until its limit is reached, at which point it will - * start rejecting further connections. - * Once the server is below the connection limit, it will continue consuming - * connections from the backlog and will process any outstanding data on - * each connection. - * This mode may be useful for some protocols that are designed to wait for - * a response message (such as HTTP), but may be less useful for other - * protocols that demand immediate responses (such as a "welcome" message in - * an interactive chat). - * - * ```php - * $server = new React\Socket\LimitingServer($server, 100, true); - * $server->on('connection', function (React\Socket\ConnectionInterface $connection) { - * $connection->write('hello there!' . PHP_EOL); - * … - * }); - * ``` - * - * @param ServerInterface $server - * @param int|null $connectionLimit - * @param bool $pauseOnLimit - */ - public function __construct(ServerInterface $server, $connectionLimit, $pauseOnLimit = false) - { - $this->server = $server; - $this->limit = $connectionLimit; - if ($connectionLimit !== null) { - $this->pauseOnLimit = $pauseOnLimit; - } - - $this->server->on('connection', array($this, 'handleConnection')); - $this->server->on('error', array($this, 'handleError')); - } - - /** - * Returns an array with all currently active connections - * - * ```php - * foreach ($server->getConnection() as $connection) { - * $connection->write('Hi!'); - * } - * ``` - * - * @return ConnectionInterface[] - */ - public function getConnections() - { - return $this->connections; - } - - public function getAddress() - { - return $this->server->getAddress(); - } - - public function pause() - { - if (!$this->manuPaused) { - $this->manuPaused = true; - - if (!$this->autoPaused) { - $this->server->pause(); - } - } - } - - public function resume() - { - if ($this->manuPaused) { - $this->manuPaused = false; - - if (!$this->autoPaused) { - $this->server->resume(); - } - } - } - - public function close() - { - $this->server->close(); - } - - /** @internal */ - public function handleConnection(ConnectionInterface $connection) - { - // close connection if limit exceeded - if ($this->limit !== null && \count($this->connections) >= $this->limit) { - $this->handleError(new \OverflowException('Connection closed because server reached connection limit')); - $connection->close(); - return; - } - - $this->connections[] = $connection; - $that = $this; - $connection->on('close', function () use ($that, $connection) { - $that->handleDisconnection($connection); - }); - - // pause accepting new connections if limit exceeded - if ($this->pauseOnLimit && !$this->autoPaused && \count($this->connections) >= $this->limit) { - $this->autoPaused = true; - - if (!$this->manuPaused) { - $this->server->pause(); - } - } - - $this->emit('connection', array($connection)); - } - - /** @internal */ - public function handleDisconnection(ConnectionInterface $connection) - { - unset($this->connections[\array_search($connection, $this->connections)]); - - // continue accepting new connection if below limit - if ($this->autoPaused && \count($this->connections) < $this->limit) { - $this->autoPaused = false; - - if (!$this->manuPaused) { - $this->server->resume(); - } - } - } - - /** @internal */ - public function handleError(\Exception $error) - { - $this->emit('error', array($error)); - } -} diff --git a/vendor/react/socket/src/SecureConnector.php b/vendor/react/socket/src/SecureConnector.php deleted file mode 100644 index 08255ac..0000000 --- a/vendor/react/socket/src/SecureConnector.php +++ /dev/null @@ -1,132 +0,0 @@ -connector = $connector; - $this->streamEncryption = new StreamEncryption($loop ?: Loop::get(), false); - $this->context = $context; - } - - public function connect($uri) - { - if (!\function_exists('stream_socket_enable_crypto')) { - return Promise\reject(new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)')); // @codeCoverageIgnore - } - - if (\strpos($uri, '://') === false) { - $uri = 'tls://' . $uri; - } - - $parts = \parse_url($uri); - if (!$parts || !isset($parts['scheme']) || $parts['scheme'] !== 'tls') { - return Promise\reject(new \InvalidArgumentException( - 'Given URI "' . $uri . '" is invalid (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - )); - } - - $context = $this->context; - $encryption = $this->streamEncryption; - $connected = false; - /** @var \React\Promise\PromiseInterface $promise */ - $promise = $this->connector->connect( - \str_replace('tls://', '', $uri) - )->then(function (ConnectionInterface $connection) use ($context, $encryption, $uri, &$promise, &$connected) { - // (unencrypted) TCP/IP connection succeeded - $connected = true; - - if (!$connection instanceof Connection) { - $connection->close(); - throw new \UnexpectedValueException('Base connector does not use internal Connection class exposing stream resource'); - } - - // set required SSL/TLS context options - foreach ($context as $name => $value) { - \stream_context_set_option($connection->stream, 'ssl', $name, $value); - } - - // try to enable encryption - return $promise = $encryption->enable($connection)->then(null, function ($error) use ($connection, $uri) { - // establishing encryption failed => close invalid connection and return error - $connection->close(); - - throw new \RuntimeException( - 'Connection to ' . $uri . ' failed during TLS handshake: ' . $error->getMessage(), - $error->getCode() - ); - }); - }, function (\Exception $e) use ($uri) { - if ($e instanceof \RuntimeException) { - $message = \preg_replace('/^Connection to [^ ]+/', '', $e->getMessage()); - $e = new \RuntimeException( - 'Connection to ' . $uri . $message, - $e->getCode(), - $e - ); - - // avoid garbage references by replacing all closures in call stack. - // what a lovely piece of code! - $r = new \ReflectionProperty('Exception', 'trace'); - $r->setAccessible(true); - $trace = $r->getValue($e); - - // Exception trace arguments are not available on some PHP 7.4 installs - // @codeCoverageIgnoreStart - foreach ($trace as $ti => $one) { - if (isset($one['args'])) { - foreach ($one['args'] as $ai => $arg) { - if ($arg instanceof \Closure) { - $trace[$ti]['args'][$ai] = 'Object(' . \get_class($arg) . ')'; - } - } - } - } - // @codeCoverageIgnoreEnd - $r->setValue($e, $trace); - } - - throw $e; - }); - - return new \React\Promise\Promise( - function ($resolve, $reject) use ($promise) { - $promise->then($resolve, $reject); - }, - function ($_, $reject) use (&$promise, $uri, &$connected) { - if ($connected) { - $reject(new \RuntimeException( - 'Connection to ' . $uri . ' cancelled during TLS handshake (ECONNABORTED)', - \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103 - )); - } - - $promise->cancel(); - $promise = null; - } - ); - } -} diff --git a/vendor/react/socket/src/SecureServer.php b/vendor/react/socket/src/SecureServer.php deleted file mode 100644 index 5a202d2..0000000 --- a/vendor/react/socket/src/SecureServer.php +++ /dev/null @@ -1,210 +0,0 @@ -on('connection', function (React\Socket\ConnectionInterface $connection) { - * echo 'Secure connection from' . $connection->getRemoteAddress() . PHP_EOL; - * - * $connection->write('hello there!' . PHP_EOL); - * … - * }); - * ``` - * - * Whenever a client fails to perform a successful TLS handshake, it will emit an - * `error` event and then close the underlying TCP/IP connection: - * - * ```php - * $server->on('error', function (Exception $e) { - * echo 'Error' . $e->getMessage() . PHP_EOL; - * }); - * ``` - * - * See also the `ServerInterface` for more details. - * - * Note that the `SecureServer` class is a concrete implementation for TLS sockets. - * If you want to typehint in your higher-level protocol implementation, you SHOULD - * use the generic `ServerInterface` instead. - * - * @see ServerInterface - * @see ConnectionInterface - */ -final class SecureServer extends EventEmitter implements ServerInterface -{ - private $tcp; - private $encryption; - private $context; - - /** - * Creates a secure TLS server and starts waiting for incoming connections - * - * It does so by wrapping a `TcpServer` instance which waits for plaintext - * TCP/IP connections and then performs a TLS handshake for each connection. - * It thus requires valid [TLS context options], - * which in its most basic form may look something like this if you're using a - * PEM encoded certificate file: - * - * ```php - * $server = new React\Socket\TcpServer(8000); - * $server = new React\Socket\SecureServer($server, null, array( - * 'local_cert' => 'server.pem' - * )); - * ``` - * - * Note that the certificate file will not be loaded on instantiation but when an - * incoming connection initializes its TLS context. - * This implies that any invalid certificate file paths or contents will only cause - * an `error` event at a later time. - * - * If your private key is encrypted with a passphrase, you have to specify it - * like this: - * - * ```php - * $server = new React\Socket\TcpServer(8000); - * $server = new React\Socket\SecureServer($server, null, array( - * 'local_cert' => 'server.pem', - * 'passphrase' => 'secret' - * )); - * ``` - * - * Note that available [TLS context options], - * their defaults and effects of changing these may vary depending on your system - * and/or PHP version. - * Passing unknown context options has no effect. - * - * 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. - * - * Advanced usage: Despite allowing any `ServerInterface` as first parameter, - * you SHOULD pass a `TcpServer` instance as first parameter, unless you - * know what you're doing. - * Internally, the `SecureServer` has to set the required TLS context options on - * the underlying stream resources. - * These resources are not exposed through any of the interfaces defined in this - * package, but only through the internal `Connection` class. - * The `TcpServer` class is guaranteed to emit connections that implement - * the `ConnectionInterface` and uses the internal `Connection` class in order to - * expose these underlying resources. - * If you use a custom `ServerInterface` and its `connection` event does not - * meet this requirement, the `SecureServer` will emit an `error` event and - * then close the underlying connection. - * - * @param ServerInterface|TcpServer $tcp - * @param ?LoopInterface $loop - * @param array $context - * @throws BadMethodCallException for legacy HHVM < 3.8 due to lack of support - * @see TcpServer - * @link https://www.php.net/manual/en/context.ssl.php for TLS context options - */ - public function __construct(ServerInterface $tcp, $loop = null, array $context = array()) - { - if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 - throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); - } - - if (!\function_exists('stream_socket_enable_crypto')) { - throw new \BadMethodCallException('Encryption not supported on your platform (HHVM < 3.8?)'); // @codeCoverageIgnore - } - - // default to empty passphrase to suppress blocking passphrase prompt - $context += array( - 'passphrase' => '' - ); - - $this->tcp = $tcp; - $this->encryption = new StreamEncryption($loop ?: Loop::get()); - $this->context = $context; - - $that = $this; - $this->tcp->on('connection', function ($connection) use ($that) { - $that->handleConnection($connection); - }); - $this->tcp->on('error', function ($error) use ($that) { - $that->emit('error', array($error)); - }); - } - - public function getAddress() - { - $address = $this->tcp->getAddress(); - if ($address === null) { - return null; - } - - return \str_replace('tcp://' , 'tls://', $address); - } - - public function pause() - { - $this->tcp->pause(); - } - - public function resume() - { - $this->tcp->resume(); - } - - public function close() - { - return $this->tcp->close(); - } - - /** @internal */ - public function handleConnection(ConnectionInterface $connection) - { - if (!$connection instanceof Connection) { - $this->emit('error', array(new \UnexpectedValueException('Base server does not use internal Connection class exposing stream resource'))); - $connection->close(); - return; - } - - foreach ($this->context as $name => $value) { - \stream_context_set_option($connection->stream, 'ssl', $name, $value); - } - - // get remote address before starting TLS handshake in case connection closes during handshake - $remote = $connection->getRemoteAddress(); - $that = $this; - - $this->encryption->enable($connection)->then( - function ($conn) use ($that) { - $that->emit('connection', array($conn)); - }, - function ($error) use ($that, $connection, $remote) { - $error = new \RuntimeException( - 'Connection from ' . $remote . ' failed during TLS handshake: ' . $error->getMessage(), - $error->getCode() - ); - - $that->emit('error', array($error)); - $connection->close(); - } - ); - } -} diff --git a/vendor/react/socket/src/Server.php b/vendor/react/socket/src/Server.php deleted file mode 100644 index b24c556..0000000 --- a/vendor/react/socket/src/Server.php +++ /dev/null @@ -1,118 +0,0 @@ - $context); - } - - // apply default options if not explicitly given - $context += array( - 'tcp' => array(), - 'tls' => array(), - 'unix' => array() - ); - - $scheme = 'tcp'; - $pos = \strpos($uri, '://'); - if ($pos !== false) { - $scheme = \substr($uri, 0, $pos); - } - - if ($scheme === 'unix') { - $server = new UnixServer($uri, $loop, $context['unix']); - } else { - $server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']); - - if ($scheme === 'tls') { - $server = new SecureServer($server, $loop, $context['tls']); - } - } - - $this->server = $server; - - $that = $this; - $server->on('connection', function (ConnectionInterface $conn) use ($that) { - $that->emit('connection', array($conn)); - }); - $server->on('error', function (Exception $error) use ($that) { - $that->emit('error', array($error)); - }); - } - - public function getAddress() - { - return $this->server->getAddress(); - } - - public function pause() - { - $this->server->pause(); - } - - public function resume() - { - $this->server->resume(); - } - - public function close() - { - $this->server->close(); - } -} diff --git a/vendor/react/socket/src/ServerInterface.php b/vendor/react/socket/src/ServerInterface.php deleted file mode 100644 index aa79fa1..0000000 --- a/vendor/react/socket/src/ServerInterface.php +++ /dev/null @@ -1,151 +0,0 @@ -on('connection', function (React\Socket\ConnectionInterface $connection) { - * echo 'new connection' . PHP_EOL; - * }); - * ``` - * - * See also the `ConnectionInterface` for more details about handling the - * incoming connection. - * - * error event: - * The `error` event will be emitted whenever there's an error accepting a new - * connection from a client. - * - * ```php - * $socket->on('error', function (Exception $e) { - * echo 'error: ' . $e->getMessage() . PHP_EOL; - * }); - * ``` - * - * Note that this is not a fatal error event, i.e. the server keeps listening for - * new connections even after this event. - * - * @see ConnectionInterface - */ -interface ServerInterface extends EventEmitterInterface -{ - /** - * Returns the full address (URI) this server is currently listening on - * - * ```php - * $address = $socket->getAddress(); - * echo 'Server listening on ' . $address . PHP_EOL; - * ``` - * - * If the address can not be determined or is unknown at this time (such as - * after the socket has been closed), it MAY return a `NULL` value instead. - * - * Otherwise, it will return the full address (URI) as a string value, such - * as `tcp://127.0.0.1:8080`, `tcp://[::1]:80` or `tls://127.0.0.1:443`. - * Note that individual URI components are application specific and depend - * on the underlying transport protocol. - * - * If this is a TCP/IP based server and you only want the local port, you may - * use something like this: - * - * ```php - * $address = $socket->getAddress(); - * $port = parse_url($address, PHP_URL_PORT); - * echo 'Server listening on port ' . $port . PHP_EOL; - * ``` - * - * @return ?string the full listening address (URI) or NULL if it is unknown (not applicable to this server socket or already closed) - */ - public function getAddress(); - - /** - * Pauses accepting new incoming connections. - * - * Removes the socket resource from the EventLoop and thus stop accepting - * new connections. Note that the listening socket stays active and is not - * closed. - * - * This means that new incoming connections will stay pending in the - * operating system backlog until its configurable backlog is filled. - * Once the backlog is filled, the operating system may reject further - * incoming connections until the backlog is drained again by resuming - * to accept new connections. - * - * Once the server is paused, no futher `connection` events SHOULD - * be emitted. - * - * ```php - * $socket->pause(); - * - * $socket->on('connection', assertShouldNeverCalled()); - * ``` - * - * This method is advisory-only, though generally not recommended, the - * server MAY continue emitting `connection` events. - * - * Unless otherwise noted, a successfully opened server SHOULD NOT start - * in paused state. - * - * You can continue processing events by calling `resume()` again. - * - * Note that both methods can be called any number of times, in particular - * calling `pause()` more than once SHOULD NOT have any effect. - * Similarly, calling this after `close()` is a NO-OP. - * - * @see self::resume() - * @return void - */ - public function pause(); - - /** - * Resumes accepting new incoming connections. - * - * Re-attach the socket resource to the EventLoop after a previous `pause()`. - * - * ```php - * $socket->pause(); - * - * Loop::addTimer(1.0, function () use ($socket) { - * $socket->resume(); - * }); - * ``` - * - * Note that both methods can be called any number of times, in particular - * calling `resume()` without a prior `pause()` SHOULD NOT have any effect. - * Similarly, calling this after `close()` is a NO-OP. - * - * @see self::pause() - * @return void - */ - public function resume(); - - /** - * Shuts down this listening socket - * - * This will stop listening for new incoming connections on this socket. - * - * Calling this method more than once on the same instance is a NO-OP. - * - * @return void - */ - public function close(); -} diff --git a/vendor/react/socket/src/SocketServer.php b/vendor/react/socket/src/SocketServer.php deleted file mode 100644 index e987f5f..0000000 --- a/vendor/react/socket/src/SocketServer.php +++ /dev/null @@ -1,215 +0,0 @@ - array(), - 'tls' => array(), - 'unix' => array() - ); - - $scheme = 'tcp'; - $pos = \strpos($uri, '://'); - if ($pos !== false) { - $scheme = \substr($uri, 0, $pos); - } - - if ($scheme === 'unix') { - $server = new UnixServer($uri, $loop, $context['unix']); - } elseif ($scheme === 'php') { - $server = new FdServer($uri, $loop); - } else { - if (preg_match('#^(?:\w+://)?\d+$#', $uri)) { - throw new \InvalidArgumentException( - 'Invalid URI given (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - ); - } - - $server = new TcpServer(str_replace('tls://', '', $uri), $loop, $context['tcp']); - - if ($scheme === 'tls') { - $server = new SecureServer($server, $loop, $context['tls']); - } - } - - $this->server = $server; - - $that = $this; - $server->on('connection', function (ConnectionInterface $conn) use ($that) { - $that->emit('connection', array($conn)); - }); - $server->on('error', function (\Exception $error) use ($that) { - $that->emit('error', array($error)); - }); - } - - public function getAddress() - { - return $this->server->getAddress(); - } - - public function pause() - { - $this->server->pause(); - } - - public function resume() - { - $this->server->resume(); - } - - public function close() - { - $this->server->close(); - } - - /** - * [internal] Internal helper method to accept new connection from given server socket - * - * @param resource $socket server socket to accept connection from - * @return resource new client socket if any - * @throws \RuntimeException if accepting fails - * @internal - */ - public static function accept($socket) - { - $errno = 0; - $errstr = ''; - \set_error_handler(function ($_, $error) use (&$errno, &$errstr) { - // Match errstr from PHP's warning message. - // stream_socket_accept(): accept failed: Connection timed out - $errstr = \preg_replace('#.*: #', '', $error); - $errno = SocketServer::errno($errstr); - }); - - $newSocket = \stream_socket_accept($socket, 0); - - \restore_error_handler(); - - if (false === $newSocket) { - throw new \RuntimeException( - 'Unable to accept new connection: ' . $errstr . self::errconst($errno), - $errno - ); - } - - return $newSocket; - } - - /** - * [Internal] Returns errno value for given errstr - * - * The errno and errstr values describes the type of error that has been - * encountered. This method tries to look up the given errstr and find a - * matching errno value which can be useful to provide more context to error - * messages. It goes through the list of known errno constants when either - * `ext-sockets`, `ext-posix` or `ext-pcntl` is available to find an errno - * matching the given errstr. - * - * @param string $errstr - * @return int errno value (e.g. value of `SOCKET_ECONNREFUSED`) or 0 if not found - * @internal - * @copyright Copyright (c) 2023 Christian Lück, taken from https://github.com/clue/errno with permission - * @codeCoverageIgnore - */ - public static function errno($errstr) - { - // PHP defines the required `strerror()` function through either `ext-sockets`, `ext-posix` or `ext-pcntl` - $strerror = \function_exists('socket_strerror') ? 'socket_strerror' : (\function_exists('posix_strerror') ? 'posix_strerror' : (\function_exists('pcntl_strerror') ? 'pcntl_strerror' : null)); - if ($strerror !== null) { - assert(\is_string($strerror) && \is_callable($strerror)); - - // PHP defines most useful errno constants like `ECONNREFUSED` through constants in `ext-sockets` like `SOCKET_ECONNREFUSED` - // PHP also defines a hand full of errno constants like `EMFILE` through constants in `ext-pcntl` like `PCNTL_EMFILE` - // go through list of all defined constants like `SOCKET_E*` and `PCNTL_E*` and see if they match the given `$errstr` - foreach (\get_defined_constants(false) as $name => $value) { - if (\is_int($value) && (\strpos($name, 'SOCKET_E') === 0 || \strpos($name, 'PCNTL_E') === 0) && $strerror($value) === $errstr) { - return $value; - } - } - - // if we reach this, no matching errno constant could be found (unlikely when `ext-sockets` is available) - // go through list of all possible errno values from 1 to `MAX_ERRNO` and see if they match the given `$errstr` - for ($errno = 1, $max = \defined('MAX_ERRNO') ? \MAX_ERRNO : 4095; $errno <= $max; ++$errno) { - if ($strerror($errno) === $errstr) { - return $errno; - } - } - } - - // if we reach this, no matching errno value could be found (unlikely when either `ext-sockets`, `ext-posix` or `ext-pcntl` is available) - return 0; - } - - /** - * [Internal] Returns errno constant name for given errno value - * - * The errno value describes the type of error that has been encountered. - * This method tries to look up the given errno value and find a matching - * errno constant name which can be useful to provide more context and more - * descriptive error messages. It goes through the list of known errno - * constants when either `ext-sockets` or `ext-pcntl` is available to find - * the matching errno constant name. - * - * Because this method is used to append more context to error messages, the - * constant name will be prefixed with a space and put between parenthesis - * when found. - * - * @param int $errno - * @return string e.g. ` (ECONNREFUSED)` or empty string if no matching const for the given errno could be found - * @internal - * @copyright Copyright (c) 2023 Christian Lück, taken from https://github.com/clue/errno with permission - * @codeCoverageIgnore - */ - public static function errconst($errno) - { - // PHP defines most useful errno constants like `ECONNREFUSED` through constants in `ext-sockets` like `SOCKET_ECONNREFUSED` - // PHP also defines a hand full of errno constants like `EMFILE` through constants in `ext-pcntl` like `PCNTL_EMFILE` - // go through list of all defined constants like `SOCKET_E*` and `PCNTL_E*` and see if they match the given `$errno` - foreach (\get_defined_constants(false) as $name => $value) { - if ($value === $errno && (\strpos($name, 'SOCKET_E') === 0 || \strpos($name, 'PCNTL_E') === 0)) { - return ' (' . \substr($name, \strpos($name, '_') + 1) . ')'; - } - } - - // if we reach this, no matching errno constant could be found (unlikely when `ext-sockets` is available) - return ''; - } -} diff --git a/vendor/react/socket/src/StreamEncryption.php b/vendor/react/socket/src/StreamEncryption.php deleted file mode 100644 index f91a359..0000000 --- a/vendor/react/socket/src/StreamEncryption.php +++ /dev/null @@ -1,158 +0,0 @@ -loop = $loop; - $this->server = $server; - - // support TLSv1.0+ by default and exclude legacy SSLv2/SSLv3. - // As of PHP 7.2+ the main crypto method constant includes all TLS versions. - // As of PHP 5.6+ the crypto method is a bitmask, so we explicitly include all TLS versions. - // For legacy PHP < 5.6 the crypto method is a single value only and this constant includes all TLS versions. - // @link https://3v4l.org/9PSST - if ($server) { - $this->method = \STREAM_CRYPTO_METHOD_TLS_SERVER; - - if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) { - $this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_1_SERVER | \STREAM_CRYPTO_METHOD_TLSv1_2_SERVER; // @codeCoverageIgnore - } - } else { - $this->method = \STREAM_CRYPTO_METHOD_TLS_CLIENT; - - if (\PHP_VERSION_ID < 70200 && \PHP_VERSION_ID >= 50600) { - $this->method |= \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; // @codeCoverageIgnore - } - } - } - - /** - * @param Connection $stream - * @return \React\Promise\PromiseInterface - */ - public function enable(Connection $stream) - { - return $this->toggle($stream, true); - } - - /** - * @param Connection $stream - * @param bool $toggle - * @return \React\Promise\PromiseInterface - */ - public function toggle(Connection $stream, $toggle) - { - // pause actual stream instance to continue operation on raw stream socket - $stream->pause(); - - // TODO: add write() event to make sure we're not sending any excessive data - - // cancelling this leaves this stream in an inconsistent state… - $deferred = new Deferred(function () { - throw new \RuntimeException(); - }); - - // get actual stream socket from stream instance - $socket = $stream->stream; - - // get crypto method from context options or use global setting from constructor - $method = $this->method; - $context = \stream_context_get_options($socket); - if (isset($context['ssl']['crypto_method'])) { - $method = $context['ssl']['crypto_method']; - } - - $that = $this; - $toggleCrypto = function () use ($socket, $deferred, $toggle, $method, $that) { - $that->toggleCrypto($socket, $deferred, $toggle, $method); - }; - - $this->loop->addReadStream($socket, $toggleCrypto); - - if (!$this->server) { - $toggleCrypto(); - } - - $loop = $this->loop; - - return $deferred->promise()->then(function () use ($stream, $socket, $loop, $toggle) { - $loop->removeReadStream($socket); - - $stream->encryptionEnabled = $toggle; - $stream->resume(); - - return $stream; - }, function($error) use ($stream, $socket, $loop) { - $loop->removeReadStream($socket); - $stream->resume(); - throw $error; - }); - } - - /** - * @internal - * @param resource $socket - * @param Deferred $deferred - * @param bool $toggle - * @param int $method - * @return void - */ - public function toggleCrypto($socket, Deferred $deferred, $toggle, $method) - { - $error = null; - \set_error_handler(function ($_, $errstr) use (&$error) { - $error = \str_replace(array("\r", "\n"), ' ', $errstr); - - // remove useless function name from error message - if (($pos = \strpos($error, "): ")) !== false) { - $error = \substr($error, $pos + 3); - } - }); - - $result = \stream_socket_enable_crypto($socket, $toggle, $method); - - \restore_error_handler(); - - if (true === $result) { - $deferred->resolve(null); - } else if (false === $result) { - // overwrite callback arguments for PHP7+ only, so they do not show - // up in the Exception trace and do not cause a possible cyclic reference. - $d = $deferred; - $deferred = null; - - if (\feof($socket) || $error === null) { - // EOF or failed without error => connection closed during handshake - $d->reject(new \UnexpectedValueException( - 'Connection lost during TLS handshake (ECONNRESET)', - \defined('SOCKET_ECONNRESET') ? \SOCKET_ECONNRESET : 104 - )); - } else { - // handshake failed with error message - $d->reject(new \UnexpectedValueException( - $error - )); - } - } else { - // need more data, will retry - } - } -} diff --git a/vendor/react/socket/src/TcpConnector.php b/vendor/react/socket/src/TcpConnector.php deleted file mode 100644 index 9d2599e..0000000 --- a/vendor/react/socket/src/TcpConnector.php +++ /dev/null @@ -1,173 +0,0 @@ -loop = $loop ?: Loop::get(); - $this->context = $context; - } - - public function connect($uri) - { - if (\strpos($uri, '://') === false) { - $uri = 'tcp://' . $uri; - } - - $parts = \parse_url($uri); - if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') { - return Promise\reject(new \InvalidArgumentException( - 'Given URI "' . $uri . '" is invalid (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - )); - } - - $ip = \trim($parts['host'], '[]'); - if (@\inet_pton($ip) === false) { - return Promise\reject(new \InvalidArgumentException( - 'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - )); - } - - // use context given in constructor - $context = array( - 'socket' => $this->context - ); - - // parse arguments from query component of URI - $args = array(); - if (isset($parts['query'])) { - \parse_str($parts['query'], $args); - } - - // If an original hostname has been given, use this for TLS setup. - // This can happen due to layers of nested connectors, such as a - // DnsConnector reporting its original hostname. - // These context options are here in case TLS is enabled later on this stream. - // If TLS is not enabled later, this doesn't hurt either. - if (isset($args['hostname'])) { - $context['ssl'] = array( - 'SNI_enabled' => true, - 'peer_name' => $args['hostname'] - ); - - // Legacy PHP < 5.6 ignores peer_name and requires legacy context options instead. - // The SNI_server_name context option has to be set here during construction, - // as legacy PHP ignores any values set later. - // @codeCoverageIgnoreStart - if (\PHP_VERSION_ID < 50600) { - $context['ssl'] += array( - 'SNI_server_name' => $args['hostname'], - 'CN_match' => $args['hostname'] - ); - } - // @codeCoverageIgnoreEnd - } - - // latest versions of PHP no longer accept any other URI components and - // HHVM fails to parse URIs with a query but no path, so let's simplify our URI here - $remote = 'tcp://' . $parts['host'] . ':' . $parts['port']; - - $stream = @\stream_socket_client( - $remote, - $errno, - $errstr, - 0, - \STREAM_CLIENT_CONNECT | \STREAM_CLIENT_ASYNC_CONNECT, - \stream_context_create($context) - ); - - if (false === $stream) { - return Promise\reject(new \RuntimeException( - 'Connection to ' . $uri . ' failed: ' . $errstr . SocketServer::errconst($errno), - $errno - )); - } - - // wait for connection - $loop = $this->loop; - return new Promise\Promise(function ($resolve, $reject) use ($loop, $stream, $uri) { - $loop->addWriteStream($stream, function ($stream) use ($loop, $resolve, $reject, $uri) { - $loop->removeWriteStream($stream); - - // The following hack looks like the only way to - // detect connection refused errors with PHP's stream sockets. - if (false === \stream_socket_get_name($stream, true)) { - // If we reach this point, we know the connection is dead, but we don't know the underlying error condition. - // @codeCoverageIgnoreStart - if (\function_exists('socket_import_stream')) { - // actual socket errno and errstr can be retrieved with ext-sockets on PHP 5.4+ - $socket = \socket_import_stream($stream); - $errno = \socket_get_option($socket, \SOL_SOCKET, \SO_ERROR); - $errstr = \socket_strerror($errno); - } elseif (\PHP_OS === 'Linux') { - // Linux reports socket errno and errstr again when trying to write to the dead socket. - // Suppress error reporting to get error message below and close dead socket before rejecting. - // This is only known to work on Linux, Mac and Windows are known to not support this. - $errno = 0; - $errstr = ''; - \set_error_handler(function ($_, $error) use (&$errno, &$errstr) { - // Match errstr from PHP's warning message. - // fwrite(): send of 1 bytes failed with errno=111 Connection refused - \preg_match('/errno=(\d+) (.+)/', $error, $m); - $errno = isset($m[1]) ? (int) $m[1] : 0; - $errstr = isset($m[2]) ? $m[2] : $error; - }); - - \fwrite($stream, \PHP_EOL); - - \restore_error_handler(); - } else { - // Not on Linux and ext-sockets not available? Too bad. - $errno = \defined('SOCKET_ECONNREFUSED') ? \SOCKET_ECONNREFUSED : 111; - $errstr = 'Connection refused?'; - } - // @codeCoverageIgnoreEnd - - \fclose($stream); - $reject(new \RuntimeException( - 'Connection to ' . $uri . ' failed: ' . $errstr . SocketServer::errconst($errno), - $errno - )); - } else { - $resolve(new Connection($stream, $loop)); - } - }); - }, function () use ($loop, $stream, $uri) { - $loop->removeWriteStream($stream); - \fclose($stream); - - // @codeCoverageIgnoreStart - // legacy PHP 5.3 sometimes requires a second close call (see tests) - if (\PHP_VERSION_ID < 50400 && \is_resource($stream)) { - \fclose($stream); - } - // @codeCoverageIgnoreEnd - - throw new \RuntimeException( - 'Connection to ' . $uri . ' cancelled during TCP/IP handshake (ECONNABORTED)', - \defined('SOCKET_ECONNABORTED') ? \SOCKET_ECONNABORTED : 103 - ); - }); - } -} diff --git a/vendor/react/socket/src/TcpServer.php b/vendor/react/socket/src/TcpServer.php deleted file mode 100644 index 01b2b46..0000000 --- a/vendor/react/socket/src/TcpServer.php +++ /dev/null @@ -1,262 +0,0 @@ -on('connection', function (React\Socket\ConnectionInterface $connection) { - * echo 'Plaintext connection from ' . $connection->getRemoteAddress() . PHP_EOL; - * $connection->write('hello there!' . PHP_EOL); - * … - * }); - * ``` - * - * See also the `ServerInterface` for more details. - * - * @see ServerInterface - * @see ConnectionInterface - */ -final class TcpServer extends EventEmitter implements ServerInterface -{ - private $master; - private $loop; - private $listening = false; - - /** - * Creates a plaintext TCP/IP socket server and starts listening on the given address - * - * This starts accepting new incoming connections on the given address. - * See also the `connection event` documented in the `ServerInterface` - * for more details. - * - * ```php - * $server = new React\Socket\TcpServer(8080); - * ``` - * - * As above, the `$uri` parameter can consist of only a port, in which case the - * server will default to listening on the localhost address `127.0.0.1`, - * which means it will not be reachable from outside of this system. - * - * In order to use a random port assignment, you can use the port `0`: - * - * ```php - * $server = new React\Socket\TcpServer(0); - * $address = $server->getAddress(); - * ``` - * - * In order to change the host the socket is listening on, you can provide an IP - * address through the first parameter provided to the constructor, optionally - * preceded by the `tcp://` scheme: - * - * ```php - * $server = new React\Socket\TcpServer('192.168.0.1:8080'); - * ``` - * - * If you want to listen on an IPv6 address, you MUST enclose the host in square - * brackets: - * - * ```php - * $server = new React\Socket\TcpServer('[::1]:8080'); - * ``` - * - * If the given URI is invalid, does not contain a port, any other scheme or if it - * contains a hostname, it will throw an `InvalidArgumentException`: - * - * ```php - * // throws InvalidArgumentException due to missing port - * $server = new React\Socket\TcpServer('127.0.0.1'); - * ``` - * - * If the given URI appears to be valid, but listening on it fails (such as if port - * is already in use or port below 1024 may require root access etc.), it will - * throw a `RuntimeException`: - * - * ```php - * $first = new React\Socket\TcpServer(8080); - * - * // throws RuntimeException because port is already in use - * $second = new React\Socket\TcpServer(8080); - * ``` - * - * Note that these error conditions may vary depending on your system and/or - * configuration. - * See the exception message and code for more details about the actual error - * condition. - * - * 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. - * - * Optionally, you can specify [socket context options](https://www.php.net/manual/en/context.socket.php) - * for the underlying stream socket resource like this: - * - * ```php - * $server = new React\Socket\TcpServer('[::1]:8080', null, array( - * 'backlog' => 200, - * 'so_reuseport' => true, - * 'ipv6_v6only' => true - * )); - * ``` - * - * Note that available [socket context options](https://www.php.net/manual/en/context.socket.php), - * their defaults and effects of changing these may vary depending on your system - * and/or PHP version. - * Passing unknown context options has no effect. - * The `backlog` context option defaults to `511` unless given explicitly. - * - * @param string|int $uri - * @param ?LoopInterface $loop - * @param array $context - * @throws InvalidArgumentException if the listening address is invalid - * @throws RuntimeException if listening on this address fails (already in use etc.) - */ - public function __construct($uri, $loop = null, array $context = array()) - { - if ($loop !== null && !$loop instanceof LoopInterface) { // manual type check to support legacy PHP < 7.1 - throw new \InvalidArgumentException('Argument #2 ($loop) expected null|React\EventLoop\LoopInterface'); - } - - $this->loop = $loop ?: Loop::get(); - - // a single port has been given => assume localhost - if ((string)(int)$uri === (string)$uri) { - $uri = '127.0.0.1:' . $uri; - } - - // assume default scheme if none has been given - if (\strpos($uri, '://') === false) { - $uri = 'tcp://' . $uri; - } - - // parse_url() does not accept null ports (random port assignment) => manually remove - if (\substr($uri, -2) === ':0') { - $parts = \parse_url(\substr($uri, 0, -2)); - if ($parts) { - $parts['port'] = 0; - } - } else { - $parts = \parse_url($uri); - } - - // ensure URI contains TCP scheme, host and port - if (!$parts || !isset($parts['scheme'], $parts['host'], $parts['port']) || $parts['scheme'] !== 'tcp') { - throw new \InvalidArgumentException( - 'Invalid URI "' . $uri . '" given (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - ); - } - - if (@\inet_pton(\trim($parts['host'], '[]')) === false) { - throw new \InvalidArgumentException( - 'Given URI "' . $uri . '" does not contain a valid host IP (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - ); - } - - $this->master = @\stream_socket_server( - $uri, - $errno, - $errstr, - \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN, - \stream_context_create(array('socket' => $context + array('backlog' => 511))) - ); - if (false === $this->master) { - if ($errno === 0) { - // PHP does not seem to report errno, so match errno from errstr - // @link https://3v4l.org/3qOBl - $errno = SocketServer::errno($errstr); - } - - throw new \RuntimeException( - 'Failed to listen on "' . $uri . '": ' . $errstr . SocketServer::errconst($errno), - $errno - ); - } - \stream_set_blocking($this->master, false); - - $this->resume(); - } - - public function getAddress() - { - if (!\is_resource($this->master)) { - return null; - } - - $address = \stream_socket_get_name($this->master, false); - - // check if this is an IPv6 address which includes multiple colons but no square brackets - $pos = \strrpos($address, ':'); - if ($pos !== false && \strpos($address, ':') < $pos && \substr($address, 0, 1) !== '[') { - $address = '[' . \substr($address, 0, $pos) . ']:' . \substr($address, $pos + 1); // @codeCoverageIgnore - } - - return 'tcp://' . $address; - } - - public function pause() - { - if (!$this->listening) { - return; - } - - $this->loop->removeReadStream($this->master); - $this->listening = false; - } - - public function resume() - { - if ($this->listening || !\is_resource($this->master)) { - return; - } - - $that = $this; - $this->loop->addReadStream($this->master, function ($master) use ($that) { - try { - $newSocket = SocketServer::accept($master); - } catch (\RuntimeException $e) { - $that->emit('error', array($e)); - return; - } - $that->handleConnection($newSocket); - }); - $this->listening = true; - } - - public function close() - { - if (!\is_resource($this->master)) { - return; - } - - $this->pause(); - \fclose($this->master); - $this->removeAllListeners(); - } - - /** @internal */ - public function handleConnection($socket) - { - $this->emit('connection', array( - new Connection($socket, $this->loop) - )); - } -} diff --git a/vendor/react/socket/src/TimeoutConnector.php b/vendor/react/socket/src/TimeoutConnector.php deleted file mode 100644 index 9ef252f..0000000 --- a/vendor/react/socket/src/TimeoutConnector.php +++ /dev/null @@ -1,79 +0,0 @@ -connector = $connector; - $this->timeout = $timeout; - $this->loop = $loop ?: Loop::get(); - } - - public function connect($uri) - { - $promise = $this->connector->connect($uri); - - $loop = $this->loop; - $time = $this->timeout; - return new Promise(function ($resolve, $reject) use ($loop, $time, $promise, $uri) { - $timer = null; - $promise = $promise->then(function ($v) use (&$timer, $loop, $resolve) { - if ($timer) { - $loop->cancelTimer($timer); - } - $timer = false; - $resolve($v); - }, function ($v) use (&$timer, $loop, $reject) { - if ($timer) { - $loop->cancelTimer($timer); - } - $timer = false; - $reject($v); - }); - - // promise already resolved => no need to start timer - if ($timer === false) { - return; - } - - // start timeout timer which will cancel the pending promise - $timer = $loop->addTimer($time, function () use ($time, &$promise, $reject, $uri) { - $reject(new \RuntimeException( - 'Connection to ' . $uri . ' timed out after ' . $time . ' seconds (ETIMEDOUT)', - \defined('SOCKET_ETIMEDOUT') ? \SOCKET_ETIMEDOUT : 110 - )); - - // Cancel pending connection to clean up any underlying resources and references. - // Avoid garbage references in call stack by passing pending promise by reference. - assert(\method_exists($promise, 'cancel')); - $promise->cancel(); - $promise = null; - }); - }, function () use (&$promise) { - // Cancelling this promise will cancel the pending connection, thus triggering the rejection logic above. - // Avoid garbage references in call stack by passing pending promise by reference. - assert(\method_exists($promise, 'cancel')); - $promise->cancel(); - $promise = null; - }); - } -} diff --git a/vendor/react/socket/src/UnixConnector.php b/vendor/react/socket/src/UnixConnector.php deleted file mode 100644 index 95f932c..0000000 --- a/vendor/react/socket/src/UnixConnector.php +++ /dev/null @@ -1,58 +0,0 @@ -loop = $loop ?: Loop::get(); - } - - public function connect($path) - { - if (\strpos($path, '://') === false) { - $path = 'unix://' . $path; - } elseif (\substr($path, 0, 7) !== 'unix://') { - return Promise\reject(new \InvalidArgumentException( - 'Given URI "' . $path . '" is invalid (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - )); - } - - $resource = @\stream_socket_client($path, $errno, $errstr, 1.0); - - if (!$resource) { - return Promise\reject(new \RuntimeException( - 'Unable to connect to unix domain socket "' . $path . '": ' . $errstr . SocketServer::errconst($errno), - $errno - )); - } - - $connection = new Connection($resource, $this->loop); - $connection->unix = true; - - return Promise\resolve($connection); - } -} diff --git a/vendor/react/socket/src/UnixServer.php b/vendor/react/socket/src/UnixServer.php deleted file mode 100644 index 27b014d..0000000 --- a/vendor/react/socket/src/UnixServer.php +++ /dev/null @@ -1,162 +0,0 @@ -loop = $loop ?: Loop::get(); - - if (\strpos($path, '://') === false) { - $path = 'unix://' . $path; - } elseif (\substr($path, 0, 7) !== 'unix://') { - throw new \InvalidArgumentException( - 'Given URI "' . $path . '" is invalid (EINVAL)', - \defined('SOCKET_EINVAL') ? \SOCKET_EINVAL : (\defined('PCNTL_EINVAL') ? \PCNTL_EINVAL : 22) - ); - } - - $errno = 0; - $errstr = ''; - \set_error_handler(function ($_, $error) use (&$errno, &$errstr) { - // PHP does not seem to report errno/errstr for Unix domain sockets (UDS) right now. - // This only applies to UDS server sockets, see also https://3v4l.org/NAhpr. - // Parse PHP warning message containing unknown error, HHVM reports proper info at least. - if (\preg_match('/\(([^\)]+)\)|\[(\d+)\]: (.*)/', $error, $match)) { - $errstr = isset($match[3]) ? $match['3'] : $match[1]; - $errno = isset($match[2]) ? (int)$match[2] : 0; - } - }); - - $this->master = \stream_socket_server( - $path, - $errno, - $errstr, - \STREAM_SERVER_BIND | \STREAM_SERVER_LISTEN, - \stream_context_create(array('socket' => $context)) - ); - - \restore_error_handler(); - - if (false === $this->master) { - throw new \RuntimeException( - 'Failed to listen on Unix domain socket "' . $path . '": ' . $errstr . SocketServer::errconst($errno), - $errno - ); - } - \stream_set_blocking($this->master, 0); - - $this->resume(); - } - - public function getAddress() - { - if (!\is_resource($this->master)) { - return null; - } - - return 'unix://' . \stream_socket_get_name($this->master, false); - } - - public function pause() - { - if (!$this->listening) { - return; - } - - $this->loop->removeReadStream($this->master); - $this->listening = false; - } - - public function resume() - { - if ($this->listening || !is_resource($this->master)) { - return; - } - - $that = $this; - $this->loop->addReadStream($this->master, function ($master) use ($that) { - try { - $newSocket = SocketServer::accept($master); - } catch (\RuntimeException $e) { - $that->emit('error', array($e)); - return; - } - $that->handleConnection($newSocket); - }); - $this->listening = true; - } - - public function close() - { - if (!\is_resource($this->master)) { - return; - } - - $this->pause(); - \fclose($this->master); - $this->removeAllListeners(); - } - - /** @internal */ - public function handleConnection($socket) - { - $connection = new Connection($socket, $this->loop); - $connection->unix = true; - - $this->emit('connection', array( - $connection - )); - } -}