Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
4
core/vendor/guzzlehttp/ringphp/.gitignore
vendored
Normal file
4
core/vendor/guzzlehttp/ringphp/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
vendor
|
||||
build/artifacts/
|
||||
composer.lock
|
||||
docs/_build/
|
22
core/vendor/guzzlehttp/ringphp/.travis.yml
vendored
Normal file
22
core/vendor/guzzlehttp/ringphp/.travis.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
language: php
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
- 5.6
|
||||
- 7.0
|
||||
- hhvm
|
||||
|
||||
before_script:
|
||||
- composer self-update
|
||||
- composer install --no-interaction --prefer-source --dev
|
||||
- ~/.nvm/nvm.sh install v0.6.14
|
||||
- ~/.nvm/nvm.sh run v0.6.14
|
||||
|
||||
script:
|
||||
- make test
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- php: hhvm
|
||||
fast_finish: true
|
44
core/vendor/guzzlehttp/ringphp/CHANGELOG.md
vendored
Normal file
44
core/vendor/guzzlehttp/ringphp/CHANGELOG.md
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
# CHANGELOG
|
||||
|
||||
## 1.0.7 - 2015-03-29
|
||||
|
||||
* PHP 7 fixes.
|
||||
|
||||
## 1.0.6 - 2015-02-26
|
||||
|
||||
* Bug fix: futures now extend from React's PromiseInterface to ensure that they
|
||||
are properly forwarded down the promise chain.
|
||||
* The multi handle of the CurlMultiHandler is now created lazily.
|
||||
|
||||
## 1.0.5 - 2014-12-10
|
||||
|
||||
* Adding more error information to PHP stream wrapper exceptions.
|
||||
* Added digest auth integration test support to test server.
|
||||
|
||||
## 1.0.4 - 2014-12-01
|
||||
|
||||
* Added support for older versions of cURL that do not have CURLOPT_TIMEOUT_MS.
|
||||
* Setting debug to `false` does not enable debug output.
|
||||
* Added a fix to the StreamHandler to return a `FutureArrayInterface` when an
|
||||
error occurs.
|
||||
|
||||
## 1.0.3 - 2014-11-03
|
||||
|
||||
* Setting the `header` stream option as a string to be compatible with GAE.
|
||||
* Header parsing now ensures that header order is maintained in the parsed
|
||||
message.
|
||||
|
||||
## 1.0.2 - 2014-10-28
|
||||
|
||||
* Now correctly honoring a `version` option is supplied in a request.
|
||||
See https://github.com/guzzle/RingPHP/pull/8
|
||||
|
||||
## 1.0.1 - 2014-10-26
|
||||
|
||||
* Fixed a header parsing issue with the `CurlHandler` and `CurlMultiHandler`
|
||||
that caused cURL requests with multiple responses to merge repsonses together
|
||||
(e.g., requests with digest authentication).
|
||||
|
||||
## 1.0.0 - 2014-10-12
|
||||
|
||||
* Initial release.
|
19
core/vendor/guzzlehttp/ringphp/LICENSE
vendored
Normal file
19
core/vendor/guzzlehttp/ringphp/LICENSE
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
|
||||
|
||||
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.
|
46
core/vendor/guzzlehttp/ringphp/Makefile
vendored
Normal file
46
core/vendor/guzzlehttp/ringphp/Makefile
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
all: clean coverage docs
|
||||
|
||||
docs:
|
||||
cd docs && make html
|
||||
|
||||
view-docs:
|
||||
open docs/_build/html/index.html
|
||||
|
||||
start-server: stop-server
|
||||
node tests/Client/server.js &> /dev/null &
|
||||
|
||||
stop-server:
|
||||
@PID=$(shell ps axo pid,command \
|
||||
| grep 'tests/Client/server.js' \
|
||||
| grep -v grep \
|
||||
| cut -f 1 -d " "\
|
||||
) && [ -n "$$PID" ] && kill $$PID || true
|
||||
|
||||
test: start-server
|
||||
vendor/bin/phpunit $(TEST)
|
||||
$(MAKE) stop-server
|
||||
|
||||
coverage: start-server
|
||||
vendor/bin/phpunit --coverage-html=build/artifacts/coverage $(TEST)
|
||||
$(MAKE) stop-server
|
||||
|
||||
view-coverage:
|
||||
open build/artifacts/coverage/index.html
|
||||
|
||||
clean:
|
||||
rm -rf build/artifacts/*
|
||||
cd docs && make clean
|
||||
|
||||
tag:
|
||||
$(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=4.2.1"))
|
||||
@echo Tagging $(TAG)
|
||||
chag update -m '$(TAG) ()'
|
||||
git add -A
|
||||
git commit -m '$(TAG) release'
|
||||
chag tag
|
||||
|
||||
perf: start-server
|
||||
php tests/perf.php
|
||||
$(MAKE) stop-server
|
||||
|
||||
.PHONY: docs
|
46
core/vendor/guzzlehttp/ringphp/README.rst
vendored
Normal file
46
core/vendor/guzzlehttp/ringphp/README.rst
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
=======
|
||||
RingPHP
|
||||
=======
|
||||
|
||||
Provides a simple API and specification that abstracts away the details of HTTP
|
||||
into a single PHP function. RingPHP be used to power HTTP clients and servers
|
||||
through a PHP function that accepts a request hash and returns a response hash
|
||||
that is fulfilled using a `promise <https://github.com/reactphp/promise>`_,
|
||||
allowing RingPHP to support both synchronous and asynchronous workflows.
|
||||
|
||||
By abstracting the implementation details of different HTTP clients and
|
||||
servers, RingPHP allows you to utilize pluggable HTTP clients and servers
|
||||
without tying your application to a specific implementation.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
|
||||
$handler = new CurlHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => [
|
||||
'host' => ['www.google.com'],
|
||||
'x-foo' => ['baz']
|
||||
]
|
||||
]);
|
||||
|
||||
$response->then(function (array $response) {
|
||||
echo $response['status'];
|
||||
});
|
||||
|
||||
$response->wait();
|
||||
|
||||
RingPHP is inspired by Clojure's `Ring <https://github.com/ring-clojure/ring>`_,
|
||||
which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is
|
||||
utilized as the handler layer in `Guzzle <http://guzzlephp.org>`_ 5.0+ to send
|
||||
HTTP requests.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
See http://ringphp.readthedocs.org/ for the full online documentation.
|
39
core/vendor/guzzlehttp/ringphp/composer.json
vendored
Normal file
39
core/vendor/guzzlehttp/ringphp/composer.json
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "guzzlehttp/ringphp",
|
||||
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"guzzlehttp/streams": "~3.0",
|
||||
"react/promise": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Guzzle will use specific adapters if cURL is present"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Ring\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Tests\\Ring\\": "tests/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
153
core/vendor/guzzlehttp/ringphp/docs/Makefile
vendored
Normal file
153
core/vendor/guzzlehttp/ringphp/docs/Makefile
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/GuzzleRing.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/GuzzleRing.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/GuzzleRing"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/GuzzleRing"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
173
core/vendor/guzzlehttp/ringphp/docs/client_handlers.rst
vendored
Normal file
173
core/vendor/guzzlehttp/ringphp/docs/client_handlers.rst
vendored
Normal file
|
@ -0,0 +1,173 @@
|
|||
===============
|
||||
Client Handlers
|
||||
===============
|
||||
|
||||
Client handlers accept a request array and return a future response array that
|
||||
can be used synchronously as an array or asynchronously using a promise.
|
||||
|
||||
Built-In Handlers
|
||||
-----------------
|
||||
|
||||
RingPHP comes with three built-in client handlers.
|
||||
|
||||
Stream Handler
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The ``GuzzleHttp\Ring\Client\StreamHandler`` uses PHP's
|
||||
`http stream wrapper <http://php.net/manual/en/wrappers.http.php>`_ to send
|
||||
requests.
|
||||
|
||||
.. note::
|
||||
|
||||
This handler cannot send requests concurrently.
|
||||
|
||||
You can provide an associative array of custom stream context options to the
|
||||
StreamHandler using the ``stream_context`` key of the ``client`` request
|
||||
option.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\StreamHandler;
|
||||
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => ['httpbin.org']],
|
||||
'client' => [
|
||||
'stream_context' => [
|
||||
'http' => [
|
||||
'request_fulluri' => true,
|
||||
'method' => 'HEAD'
|
||||
],
|
||||
'socket' => [
|
||||
'bindto' => '127.0.0.1:0'
|
||||
],
|
||||
'ssl' => [
|
||||
'verify_peer' => false
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
// Even though it's already completed, you can still use a promise
|
||||
$response->then(function ($response) {
|
||||
echo $response['status']; // 200
|
||||
});
|
||||
|
||||
// Or access the response using the future interface
|
||||
echo $response['status']; // 200
|
||||
|
||||
cURL Handler
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The ``GuzzleHttp\Ring\Client\CurlHandler`` can be used with PHP 5.5+ to send
|
||||
requests using cURL easy handles. This handler is great for sending requests
|
||||
one at a time because the execute and select loop is implemented in C code
|
||||
which executes faster and consumes less memory than using PHP's
|
||||
``curl_multi_*`` interface.
|
||||
|
||||
.. note::
|
||||
|
||||
This handler cannot send requests concurrently.
|
||||
|
||||
When using the CurlHandler, custom curl options can be specified as an
|
||||
associative array of `cURL option constants <http://php.net/manual/en/curl.constants.php>`_
|
||||
mapping to values in the ``client`` option of a requst using the **curl** key.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
|
||||
$handler = new CurlHandler();
|
||||
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]]
|
||||
];
|
||||
|
||||
$response = $handler($request);
|
||||
|
||||
// The response can be used directly as an array.
|
||||
echo $response['status']; // 200
|
||||
|
||||
// Or, it can be used as a promise (that has already fulfilled).
|
||||
$response->then(function ($response) {
|
||||
echo $response['status']; // 200
|
||||
});
|
||||
|
||||
cURL Multi Handler
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``GuzzleHttp\Ring\Client\CurlMultiHandler`` transfers requests using
|
||||
cURL's `multi API <http://curl.haxx.se/libcurl/c/libcurl-multi.html>`_. The
|
||||
``CurlMultiHandler`` is great for sending requests concurrently.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlMultiHandler;
|
||||
|
||||
$handler = new CurlMultiHandler();
|
||||
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]]
|
||||
];
|
||||
|
||||
// this call returns a future array immediately.
|
||||
$response = $handler($request);
|
||||
|
||||
// Ideally, you should use the promise API to not block.
|
||||
$response
|
||||
->then(function ($response) {
|
||||
// Got the response at some point in the future
|
||||
echo $response['status']; // 200
|
||||
// Don't break the chain
|
||||
return $response;
|
||||
})->then(function ($response) {
|
||||
// ...
|
||||
});
|
||||
|
||||
// If you really need to block, then you can use the response as an
|
||||
// associative array. This will block until it has completed.
|
||||
echo $response['status']; // 200
|
||||
|
||||
Just like the ``CurlHandler``, the ``CurlMultiHandler`` accepts custom curl
|
||||
option in the ``curl`` key of the ``client`` request option.
|
||||
|
||||
Mock Handler
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The ``GuzzleHttp\Ring\Client\MockHandler`` is used to return mock responses.
|
||||
When constructed, the handler can be configured to return the same response
|
||||
array over and over, a future response, or a the evaluation of a callback
|
||||
function.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\MockHandler;
|
||||
|
||||
// Return a canned response.
|
||||
$mock = new MockHandler(['status' => 200]);
|
||||
$response = $mock([]);
|
||||
assert(200 == $response['status']);
|
||||
assert([] == $response['headers']);
|
||||
|
||||
Implementing Handlers
|
||||
---------------------
|
||||
|
||||
Client handlers are just PHP callables (functions or classes that have the
|
||||
``__invoke`` magic method). The callable accepts a request array and MUST
|
||||
return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface`` so that
|
||||
the response can be used by both blocking and non-blocking consumers.
|
||||
|
||||
Handlers need to follow a few simple rules:
|
||||
|
||||
1. Do not throw exceptions. If an error is encountered, return an array that
|
||||
contains the ``error`` key that maps to an ``\Exception`` value.
|
||||
2. If the request has a ``delay`` client option, then the handler should only
|
||||
send the request after the specified delay time in seconds. Blocking
|
||||
handlers may find it convenient to just let the
|
||||
``GuzzleHttp\Ring\Core::doSleep($request)`` function handle this for them.
|
||||
3. Always return an instance of ``GuzzleHttp\Ring\Future\FutureArrayInterface``.
|
||||
4. Complete any outstanding requests when the handler is destructed.
|
165
core/vendor/guzzlehttp/ringphp/docs/client_middleware.rst
vendored
Normal file
165
core/vendor/guzzlehttp/ringphp/docs/client_middleware.rst
vendored
Normal file
|
@ -0,0 +1,165 @@
|
|||
=================
|
||||
Client Middleware
|
||||
=================
|
||||
|
||||
Middleware intercepts requests before they are sent over the wire and can be
|
||||
used to add functionality to handlers.
|
||||
|
||||
Modifying Requests
|
||||
------------------
|
||||
|
||||
Let's say you wanted to modify requests before they are sent over the wire
|
||||
so that they always add specific headers. This can be accomplished by creating
|
||||
a function that accepts a handler and returns a new function that adds the
|
||||
composed behavior.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
|
||||
$handler = new CurlHandler();
|
||||
|
||||
$addHeaderHandler = function (callable $handler, array $headers = []) {
|
||||
return function (array $request) use ($handler, $headers) {
|
||||
// Add our custom headers
|
||||
foreach ($headers as $key => $value) {
|
||||
$request['headers'][$key] = $value;
|
||||
}
|
||||
|
||||
// Send the request using the handler and return the response.
|
||||
return $handler($request);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new handler that adds headers to each request.
|
||||
$handler = $addHeaderHandler($handler, [
|
||||
'X-AddMe' => 'hello',
|
||||
'Authorization' => 'Basic xyz'
|
||||
]);
|
||||
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => ['httpbin.org']
|
||||
]);
|
||||
|
||||
Modifying Responses
|
||||
-------------------
|
||||
|
||||
You can change a response as it's returned from a middleware. Remember that
|
||||
responses returned from an handler (including middleware) must implement
|
||||
``GuzzleHttp\Ring\Future\FutureArrayInterface``. In order to be a good citizen,
|
||||
you should not expect that the responses returned through your middleware will
|
||||
be completed synchronously. Instead, you should use the
|
||||
``GuzzleHttp\Ring\Core::proxy()`` function to modify the response when the
|
||||
underlying promise is resolved. This function is a helper function that makes it
|
||||
easy to create a new instance of ``FutureArrayInterface`` that wraps an existing
|
||||
``FutureArrayInterface`` object.
|
||||
|
||||
Let's say you wanted to add headers to a response as they are returned from
|
||||
your middleware, but you want to make sure you aren't causing future
|
||||
responses to be dereferenced right away. You can achieve this by modifying the
|
||||
incoming request and using the ``Core::proxy`` function.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
|
||||
$handler = new CurlHandler();
|
||||
|
||||
$responseHeaderHandler = function (callable $handler, array $headers) {
|
||||
return function (array $request) use ($handler, $headers) {
|
||||
// Send the request using the wrapped handler.
|
||||
return Core::proxy($handler($request), function ($response) use ($headers) {
|
||||
// Add the headers to the response when it is available.
|
||||
foreach ($headers as $key => $value) {
|
||||
$response['headers'][$key] = (array) $value;
|
||||
}
|
||||
// Note that you can return a regular response array when using
|
||||
// the proxy method.
|
||||
return $response;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new handler that adds headers to each response.
|
||||
$handler = $responseHeaderHandler($handler, ['X-Header' => 'hello!']);
|
||||
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => ['httpbin.org']
|
||||
]);
|
||||
|
||||
assert($response['headers']['X-Header'] == 'hello!');
|
||||
|
||||
Built-In Middleware
|
||||
-------------------
|
||||
|
||||
RingPHP comes with a few basic client middlewares that modify requests
|
||||
and responses.
|
||||
|
||||
Streaming Middleware
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to send all requests with the ``streaming`` option to a specific
|
||||
handler but other requests to a different handler, then use the streaming
|
||||
middleware.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
use GuzzleHttp\Ring\Client\StreamHandler;
|
||||
use GuzzleHttp\Ring\Client\Middleware;
|
||||
|
||||
$defaultHandler = new CurlHandler();
|
||||
$streamingHandler = new StreamHandler();
|
||||
$streamingHandler = Middleware::wrapStreaming(
|
||||
$defaultHandler,
|
||||
$streamingHandler
|
||||
);
|
||||
|
||||
// Send the request using the streaming handler.
|
||||
$response = $streamingHandler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => ['www.google.com'],
|
||||
'stream' => true
|
||||
]);
|
||||
|
||||
// Send the request using the default handler.
|
||||
$response = $streamingHandler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => ['www.google.com']
|
||||
]);
|
||||
|
||||
Future Middleware
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to send all requests with the ``future`` option to a specific
|
||||
handler but other requests to a different handler, then use the future
|
||||
middleware.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
use GuzzleHttp\Ring\Client\CurlMultiHandler;
|
||||
use GuzzleHttp\Ring\Client\Middleware;
|
||||
|
||||
$defaultHandler = new CurlHandler();
|
||||
$futureHandler = new CurlMultiHandler();
|
||||
$futureHandler = Middleware::wrapFuture(
|
||||
$defaultHandler,
|
||||
$futureHandler
|
||||
);
|
||||
|
||||
// Send the request using the blocking CurlHandler.
|
||||
$response = $futureHandler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => ['www.google.com']
|
||||
]);
|
||||
|
||||
// Send the request using the non-blocking CurlMultiHandler.
|
||||
$response = $futureHandler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => ['www.google.com'],
|
||||
'future' => true
|
||||
]);
|
23
core/vendor/guzzlehttp/ringphp/docs/conf.py
vendored
Normal file
23
core/vendor/guzzlehttp/ringphp/docs/conf.py
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
import sys, os
|
||||
import sphinx_rtd_theme
|
||||
from sphinx.highlighting import lexers
|
||||
from pygments.lexers.web import PhpLexer
|
||||
|
||||
|
||||
lexers['php'] = PhpLexer(startinline=True, linenos=1)
|
||||
lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1)
|
||||
primary_domain = 'php'
|
||||
|
||||
extensions = []
|
||||
templates_path = ['_templates']
|
||||
source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
project = u'RingPHP'
|
||||
copyright = u'2014, Michael Dowling'
|
||||
version = '1.0.0-alpha'
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
html_title = "RingPHP"
|
||||
html_short_title = "RingPHP"
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
164
core/vendor/guzzlehttp/ringphp/docs/futures.rst
vendored
Normal file
164
core/vendor/guzzlehttp/ringphp/docs/futures.rst
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
=======
|
||||
Futures
|
||||
=======
|
||||
|
||||
Futures represent a computation that may have not yet completed. RingPHP
|
||||
uses hybrid of futures and promises to provide a consistent API that can be
|
||||
used for both blocking and non-blocking consumers.
|
||||
|
||||
Promises
|
||||
--------
|
||||
|
||||
You can get the result of a future when it is ready using the promise interface
|
||||
of a future. Futures expose a promise API via a ``then()`` method that utilizes
|
||||
`React's promise library <https://github.com/reactphp/promise>`_. You should
|
||||
use this API when you do not wish to block.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlMultiHandler;
|
||||
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => ['httpbin.org']]
|
||||
];
|
||||
|
||||
$response = $handler($request);
|
||||
|
||||
// Use the then() method to use the promise API of the future.
|
||||
$response->then(function ($response) {
|
||||
echo $response['status'];
|
||||
});
|
||||
|
||||
You can get the promise used by a future, an instance of
|
||||
``React\Promise\PromiseInterface``, by calling the ``promise()`` method.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$response = $handler($request);
|
||||
$promise = $response->promise();
|
||||
$promise->then(function ($response) {
|
||||
echo $response['status'];
|
||||
});
|
||||
|
||||
This promise value can be used with React's
|
||||
`aggregate promise functions <https://github.com/reactphp/promise#functions>`_.
|
||||
|
||||
Waiting
|
||||
-------
|
||||
|
||||
You can wait on a future to complete and retrieve the value, or *dereference*
|
||||
the future, using the ``wait()`` method. Calling the ``wait()`` method of a
|
||||
future will block until the result is available. The result is then returned or
|
||||
an exception is thrown if and exception was encountered while waiting on the
|
||||
the result. Subsequent calls to dereference a future will return the previously
|
||||
completed result or throw the previously encountered exception. Futures can be
|
||||
cancelled, which stops the computation if possible.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlMultiHandler;
|
||||
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => ['httpbin.org']]
|
||||
]);
|
||||
|
||||
// You can explicitly call block to wait on a result.
|
||||
$realizedResponse = $response->wait();
|
||||
|
||||
// Future responses can be used like a regular PHP array.
|
||||
echo $response['status'];
|
||||
|
||||
In addition to explicitly calling the ``wait()`` function, using a future like
|
||||
a normal value will implicitly trigger the ``wait()`` function.
|
||||
|
||||
Future Responses
|
||||
----------------
|
||||
|
||||
RingPHP uses futures to return asynchronous responses immediately. Client
|
||||
handlers always return future responses that implement
|
||||
``GuzzleHttp\Ring\Future\ArrayFutureInterface``. These future responses act
|
||||
just like normal PHP associative arrays for blocking access and provide a
|
||||
promise interface for non-blocking access.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlMultiHandler;
|
||||
|
||||
$handler = new CurlMultiHandler();
|
||||
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['Host' => ['www.google.com']]
|
||||
];
|
||||
|
||||
$response = $handler($request);
|
||||
|
||||
// Use the promise API for non-blocking access to the response. The actual
|
||||
// response value will be delivered to the promise.
|
||||
$response->then(function ($response) {
|
||||
echo $response['status'];
|
||||
});
|
||||
|
||||
// You can wait (block) until the future is completed.
|
||||
$response->wait();
|
||||
|
||||
// This will implicitly call wait(), and will block too!
|
||||
$response['status'];
|
||||
|
||||
.. important::
|
||||
|
||||
Futures that are not completed by the time the underlying handler is
|
||||
destructed will be completed when the handler is shutting down.
|
||||
|
||||
Cancelling
|
||||
----------
|
||||
|
||||
Futures can be cancelled if they have not already been dereferenced.
|
||||
|
||||
RingPHP futures are typically implemented with the
|
||||
``GuzzleHttp\Ring\Future\BaseFutureTrait``. This trait provides the cancellation
|
||||
functionality that should be common to most implementations. Cancelling a
|
||||
future response will try to prevent the request from sending over the wire.
|
||||
|
||||
When a future is cancelled, the cancellation function is invoked and performs
|
||||
the actual work needed to cancel the request from sending if possible
|
||||
(e.g., telling an event loop to stop sending a request or to close a socket).
|
||||
If no cancellation function is provided, then a request cannot be cancelled. If
|
||||
a cancel function is provided, then it should accept the future as an argument
|
||||
and return true if the future was successfully cancelled or false if it could
|
||||
not be cancelled.
|
||||
|
||||
Wrapping an existing Promise
|
||||
----------------------------
|
||||
|
||||
You can easily create a future from any existing promise using the
|
||||
``GuzzleHttp\Ring\Future\FutureValue`` class. This class's constructor
|
||||
accepts a promise as the first argument, a wait function as the second
|
||||
argument, and a cancellation function as the third argument. The dereference
|
||||
function is used to force the promise to resolve (for example, manually ticking
|
||||
an event loop). The cancel function is optional and is used to tell the thing
|
||||
that created the promise that it can stop computing the result (for example,
|
||||
telling an event loop to stop transferring a request).
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Future\FutureValue;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
$deferred = new Deferred();
|
||||
$promise = $deferred->promise();
|
||||
|
||||
$f = new FutureValue(
|
||||
$promise,
|
||||
function () use ($deferred) {
|
||||
// This function is responsible for blocking and resolving the
|
||||
// promise. Here we pass in a reference to the deferred so that
|
||||
// it can be resolved or rejected.
|
||||
$deferred->resolve('foo');
|
||||
}
|
||||
);
|
50
core/vendor/guzzlehttp/ringphp/docs/index.rst
vendored
Normal file
50
core/vendor/guzzlehttp/ringphp/docs/index.rst
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
=======
|
||||
RingPHP
|
||||
=======
|
||||
|
||||
Provides a simple API and specification that abstracts away the details of HTTP
|
||||
into a single PHP function. RingPHP be used to power HTTP clients and servers
|
||||
through a PHP function that accepts a request hash and returns a response hash
|
||||
that is fulfilled using a `promise <https://github.com/reactphp/promise>`_,
|
||||
allowing RingPHP to support both synchronous and asynchronous workflows.
|
||||
|
||||
By abstracting the implementation details of different HTTP clients and
|
||||
servers, RingPHP allows you to utilize pluggable HTTP clients and servers
|
||||
without tying your application to a specific implementation.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
spec
|
||||
futures
|
||||
client_middleware
|
||||
client_handlers
|
||||
testing
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
|
||||
$handler = new CurlHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => [
|
||||
'host' => ['www.google.com'],
|
||||
'x-foo' => ['baz']
|
||||
]
|
||||
]);
|
||||
|
||||
$response->then(function (array $response) {
|
||||
echo $response['status'];
|
||||
});
|
||||
|
||||
$response->wait();
|
||||
|
||||
RingPHP is inspired by Clojure's `Ring <https://github.com/ring-clojure/ring>`_,
|
||||
which, in turn, was inspired by Python's WSGI and Ruby's Rack. RingPHP is
|
||||
utilized as the handler layer in `Guzzle <http://guzzlephp.org>`_ 5.0+ to send
|
||||
HTTP requests.
|
1
core/vendor/guzzlehttp/ringphp/docs/requirements.txt
vendored
Normal file
1
core/vendor/guzzlehttp/ringphp/docs/requirements.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
sphinx_rtd_theme
|
311
core/vendor/guzzlehttp/ringphp/docs/spec.rst
vendored
Normal file
311
core/vendor/guzzlehttp/ringphp/docs/spec.rst
vendored
Normal file
|
@ -0,0 +1,311 @@
|
|||
=============
|
||||
Specification
|
||||
=============
|
||||
|
||||
RingPHP applications consist of handlers, requests, responses, and
|
||||
middleware.
|
||||
|
||||
Handlers
|
||||
--------
|
||||
|
||||
Handlers are implemented as a PHP ``callable`` that accept a request array
|
||||
and return a response array (``GuzzleHttp\Ring\Future\FutureArrayInterface``).
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
|
||||
$mockHandler = function (array $request) {
|
||||
return new CompletedFutureArray([
|
||||
'status' => 200,
|
||||
'headers' => ['X-Foo' => ['Bar']],
|
||||
'body' => 'Hello!'
|
||||
]);
|
||||
};
|
||||
|
||||
This handler returns the same response each time it is invoked. All RingPHP
|
||||
handlers must return a ``GuzzleHttp\Ring\Future\FutureArrayInterface``. Use
|
||||
``GuzzleHttp\Ring\Future\CompletedFutureArray`` when returning a response that
|
||||
has already completed.
|
||||
|
||||
Requests
|
||||
--------
|
||||
|
||||
A request array is a PHP associative array that contains the configuration
|
||||
settings need to send a request.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'scheme' => 'http',
|
||||
'uri' => '/',
|
||||
'body' => 'hello!',
|
||||
'client' => ['timeout' => 1.0],
|
||||
'headers' => [
|
||||
'host' => ['httpbin.org'],
|
||||
'X-Foo' => ['baz', 'bar']
|
||||
]
|
||||
];
|
||||
|
||||
The request array contains the following key value pairs:
|
||||
|
||||
request_method
|
||||
(string, required) The HTTP request method, must be all caps corresponding
|
||||
to a HTTP request method, such as ``GET`` or ``POST``.
|
||||
|
||||
scheme
|
||||
(string) The transport protocol, must be one of ``http`` or ``https``.
|
||||
Defaults to ``http``.
|
||||
|
||||
uri
|
||||
(string, required) The request URI excluding the query string. Must
|
||||
start with "/".
|
||||
|
||||
query_string
|
||||
(string) The query string, if present (e.g., ``foo=bar``).
|
||||
|
||||
version
|
||||
(string) HTTP protocol version. Defaults to ``1.1``.
|
||||
|
||||
headers
|
||||
(required, array) Associative array of headers. Each key represents the
|
||||
header name. Each value contains an array of strings where each entry of
|
||||
the array SHOULD be sent over the wire on a separate header line.
|
||||
|
||||
body
|
||||
(string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``)
|
||||
The body of the request, if present. Can be a string, resource returned
|
||||
from fopen, an ``Iterator`` that yields chunks of data, an object that
|
||||
implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``.
|
||||
|
||||
future
|
||||
(bool, string) Controls the asynchronous behavior of a response.
|
||||
|
||||
Set to ``true`` or omit the ``future`` option to *request* that a request
|
||||
will be completed asynchronously. Keep in mind that your request might not
|
||||
necessarily be completed asynchronously based on the handler you are using.
|
||||
Set the ``future`` option to ``false`` to request that a synchronous
|
||||
response be provided.
|
||||
|
||||
You can provide a string value to specify fine-tuned future behaviors that
|
||||
may be specific to the underlying handlers you are using. There are,
|
||||
however, some common future options that handlers should implement if
|
||||
possible.
|
||||
|
||||
lazy
|
||||
Requests that the handler does not open and send the request
|
||||
immediately, but rather only opens and sends the request once the
|
||||
future is dereferenced. This option is often useful for sending a large
|
||||
number of requests concurrently to allow handlers to take better
|
||||
advantage of non-blocking transfers by first building up a pool of
|
||||
requests.
|
||||
|
||||
If an handler does not implement or understand a provided string value,
|
||||
then the request MUST be treated as if the user provided ``true`` rather
|
||||
than the string value.
|
||||
|
||||
Future responses created by asynchronous handlers MUST attempt to complete
|
||||
any outstanding future responses when they are destructed. Asynchronous
|
||||
handlers MAY choose to automatically complete responses when the number
|
||||
of outstanding requests reaches an handler-specific threshold.
|
||||
|
||||
Client Specific Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following options are only used in ring client handlers.
|
||||
|
||||
.. _client-options:
|
||||
|
||||
client
|
||||
(array) Associative array of client specific transfer options. The
|
||||
``client`` request key value pair can contain the following keys:
|
||||
|
||||
cert
|
||||
(string, array) Set to a string to specify the path to a file
|
||||
containing a PEM formatted SSL client side certificate. If a password
|
||||
is required, then set ``cert`` to an array containing the path to the
|
||||
PEM file in the first array element followed by the certificate
|
||||
password in the second array element.
|
||||
|
||||
connect_timeout
|
||||
(float) Float describing the number of seconds to wait while trying to
|
||||
connect to a server. Use ``0`` to wait indefinitely (the default
|
||||
behavior).
|
||||
|
||||
debug
|
||||
(bool, fopen() resource) Set to true or set to a PHP stream returned by
|
||||
fopen() to enable debug output with the handler used to send a request.
|
||||
If set to ``true``, the output is written to PHP's STDOUT. If a PHP
|
||||
``fopen`` resource handle is provided, the output is written to the
|
||||
stream.
|
||||
|
||||
"Debug output" is handler specific: different handlers will yield
|
||||
different output and various various level of detail. For example, when
|
||||
using cURL to transfer requests, cURL's `CURLOPT_VERBOSE <http://curl.haxx.se/libcurl/c/CURLOPT_VERBOSE.html>`_
|
||||
will be used. When using the PHP stream wrapper, `stream notifications <http://php.net/manual/en/function.stream-notification-callback.php>`_
|
||||
will be emitted.
|
||||
|
||||
decode_content
|
||||
(bool) Specify whether or not ``Content-Encoding`` responses
|
||||
(gzip, deflate, etc.) are automatically decoded. Set to ``true`` to
|
||||
automatically decode encoded responses. Set to ``false`` to not decode
|
||||
responses. By default, content is *not* decoded automatically.
|
||||
|
||||
delay
|
||||
(int) The number of milliseconds to delay before sending the request.
|
||||
This is often used for delaying before retrying a request. Handlers
|
||||
SHOULD implement this if possible, but it is not a strict requirement.
|
||||
|
||||
progress
|
||||
(function) Defines a function to invoke when transfer progress is made.
|
||||
The function accepts the following arguments:
|
||||
|
||||
1. The total number of bytes expected to be downloaded
|
||||
2. The number of bytes downloaded so far
|
||||
3. The number of bytes expected to be uploaded
|
||||
4. The number of bytes uploaded so far
|
||||
|
||||
proxy
|
||||
(string, array) Pass a string to specify an HTTP proxy, or an
|
||||
associative array to specify different proxies for different protocols
|
||||
where the scheme is the key and the value is the proxy address.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['httpbin.org']],
|
||||
'client' => [
|
||||
// Use different proxies for different URI schemes.
|
||||
'proxy' => [
|
||||
'http' => 'http://proxy.example.com:5100',
|
||||
'https' => 'https://proxy.example.com:6100'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
ssl_key
|
||||
(string, array) Specify the path to a file containing a private SSL key
|
||||
in PEM format. If a password is required, then set to an array
|
||||
containing the path to the SSL key in the first array element followed
|
||||
by the password required for the certificate in the second element.
|
||||
|
||||
save_to
|
||||
(string, fopen resource, ``GuzzleHttp\Stream\StreamInterface``)
|
||||
Specifies where the body of the response is downloaded. Pass a string to
|
||||
open a local file on disk and save the output to the file. Pass an fopen
|
||||
resource to save the output to a PHP stream resource. Pass a
|
||||
``GuzzleHttp\Stream\StreamInterface`` to save the output to a Guzzle
|
||||
StreamInterface. Omitting this option will typically save the body of a
|
||||
response to a PHP temp stream.
|
||||
|
||||
stream
|
||||
(bool) Set to true to stream a response rather than download it all
|
||||
up-front. This option will only be utilized when the corresponding
|
||||
handler supports it.
|
||||
|
||||
timeout
|
||||
(float) Float describing the timeout of the request in seconds. Use 0 to
|
||||
wait indefinitely (the default behavior).
|
||||
|
||||
verify
|
||||
(bool, string) Describes the SSL certificate verification behavior of a
|
||||
request. Set to true to enable SSL certificate verification using the
|
||||
system CA bundle when available (the default). Set to false to disable
|
||||
certificate verification (this is insecure!). Set to a string to provide
|
||||
the path to a CA bundle on disk to enable verification using a custom
|
||||
certificate.
|
||||
|
||||
version
|
||||
(string) HTTP protocol version to use with the request.
|
||||
|
||||
Server Specific Options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following options are only used in ring server handlers.
|
||||
|
||||
server_port
|
||||
(integer) The port on which the request is being handled. This is only
|
||||
used with ring servers, and is required.
|
||||
|
||||
server_name
|
||||
(string) The resolved server name, or the server IP address. Required when
|
||||
using a Ring server.
|
||||
|
||||
remote_addr
|
||||
(string) The IP address of the client or the last proxy that sent the
|
||||
request. Required when using a Ring server.
|
||||
|
||||
Responses
|
||||
---------
|
||||
|
||||
A response is an array-like object that implements
|
||||
``GuzzleHttp\Ring\Future\FutureArrayInterface``. Responses contain the
|
||||
following key value pairs:
|
||||
|
||||
body
|
||||
(string, fopen resource, ``Iterator``, ``GuzzleHttp\Stream\StreamInterface``)
|
||||
The body of the response, if present. Can be a string, resource returned
|
||||
from fopen, an ``Iterator`` that yields chunks of data, an object that
|
||||
implemented ``__toString``, or a ``GuzzleHttp\Stream\StreamInterface``.
|
||||
|
||||
effective_url
|
||||
(string) The URL that returned the resulting response.
|
||||
|
||||
error
|
||||
(``\Exception``) Contains an exception describing any errors that were
|
||||
encountered during the transfer.
|
||||
|
||||
headers
|
||||
(Required, array) Associative array of headers. Each key represents the
|
||||
header name. Each value contains an array of strings where each entry of
|
||||
the array is a header line. The headers array MAY be an empty array in the
|
||||
event an error occurred before a response was received.
|
||||
|
||||
reason
|
||||
(string) Optional reason phrase. This option should be provided when the
|
||||
reason phrase does not match the typical reason phrase associated with the
|
||||
``status`` code. See `RFC 7231 <http://tools.ietf.org/html/rfc7231#section-6.1>`_
|
||||
for a list of HTTP reason phrases mapped to status codes.
|
||||
|
||||
status
|
||||
(Required, integer) The HTTP status code. The status code MAY be set to
|
||||
``null`` in the event an error occurred before a response was received
|
||||
(e.g., a networking error).
|
||||
|
||||
transfer_stats
|
||||
(array) Provides an associative array of arbitrary transfer statistics if
|
||||
provided by the underlying handler.
|
||||
|
||||
version
|
||||
(string) HTTP protocol version. Defaults to ``1.1``.
|
||||
|
||||
Middleware
|
||||
----------
|
||||
|
||||
Ring middleware augments the functionality of handlers by invoking them in the
|
||||
process of generating responses. Middleware is typically implemented as a
|
||||
higher-order function that takes one or more handlers as arguments followed by
|
||||
an optional associative array of options as the last argument, returning a new
|
||||
handler with the desired compound behavior.
|
||||
|
||||
Here's an example of a middleware that adds a Content-Type header to each
|
||||
request.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
use GuzzleHttp\Ring\Core;
|
||||
|
||||
$contentTypeHandler = function(callable $handler, $contentType) {
|
||||
return function (array $request) use ($handler, $contentType) {
|
||||
return $handler(Core::setHeader('Content-Type', $contentType));
|
||||
};
|
||||
};
|
||||
|
||||
$baseHandler = new CurlHandler();
|
||||
$wrappedHandler = $contentTypeHandler($baseHandler, 'text/html');
|
||||
$response = $wrappedHandler([/** request hash **/]);
|
74
core/vendor/guzzlehttp/ringphp/docs/testing.rst
vendored
Normal file
74
core/vendor/guzzlehttp/ringphp/docs/testing.rst
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
=======
|
||||
Testing
|
||||
=======
|
||||
|
||||
RingPHP tests client handlers using `PHPUnit <https://phpunit.de/>`_ and a
|
||||
built-in node.js web server.
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
First, install the dependencies using `Composer <https://getcomposer.org>`_.
|
||||
|
||||
composer.phar install
|
||||
|
||||
Next, run the unit tests using ``Make``.
|
||||
|
||||
make test
|
||||
|
||||
The tests are also run on Travis-CI on each commit: https://travis-ci.org/guzzle/guzzle-ring
|
||||
|
||||
Test Server
|
||||
-----------
|
||||
|
||||
Testing client handlers usually involves actually sending HTTP requests.
|
||||
RingPHP provides a node.js web server that returns canned responses and
|
||||
keep a list of the requests that have been received. The server can then
|
||||
be queried to get a list of the requests that were sent by the client so that
|
||||
you can ensure that the client serialized and transferred requests as intended.
|
||||
|
||||
The server keeps a list of queued responses and returns responses that are
|
||||
popped off of the queue as HTTP requests are received. When there are not
|
||||
more responses to serve, the server returns a 500 error response.
|
||||
|
||||
The test server uses the ``GuzzleHttp\Tests\Ring\Client\Server`` class to
|
||||
control the server.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Ring\Client\StreamHandler;
|
||||
use GuzzleHttp\Tests\Ring\Client\Server;
|
||||
|
||||
// First return a 200 followed by a 404 response.
|
||||
Server::enqueue([
|
||||
['status' => 200],
|
||||
['status' => 404]
|
||||
]);
|
||||
|
||||
$handler = new StreamHandler();
|
||||
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'uri' => '/'
|
||||
]);
|
||||
|
||||
assert(200 == $response['status']);
|
||||
|
||||
$response = $handler([
|
||||
'http_method' => 'HEAD',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'uri' => '/'
|
||||
]);
|
||||
|
||||
assert(404 == $response['status']);
|
||||
|
||||
After requests have been sent, you can get a list of the requests as they
|
||||
were sent over the wire to ensure they were sent correctly.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$received = Server::received();
|
||||
|
||||
assert('GET' == $received[0]['http_method']);
|
||||
assert('HEAD' == $received[1]['http_method']);
|
14
core/vendor/guzzlehttp/ringphp/phpunit.xml.dist
vendored
Normal file
14
core/vendor/guzzlehttp/ringphp/phpunit.xml.dist
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="./tests/bootstrap.php"
|
||||
colors="true">
|
||||
<testsuites>
|
||||
<testsuite>
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
74
core/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php
vendored
Normal file
74
core/vendor/guzzlehttp/ringphp/src/Client/ClientUtils.php
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
/**
|
||||
* Client specific utility functions.
|
||||
*/
|
||||
class ClientUtils
|
||||
{
|
||||
/**
|
||||
* Returns the default cacert bundle for the current system.
|
||||
*
|
||||
* First, the openssl.cafile and curl.cainfo php.ini settings are checked.
|
||||
* If those settings are not configured, then the common locations for
|
||||
* bundles found on Red Hat, CentOS, Fedora, Ubuntu, Debian, FreeBSD, OS X
|
||||
* and Windows are checked. If any of these file locations are found on
|
||||
* disk, they will be utilized.
|
||||
*
|
||||
* Note: the result of this function is cached for subsequent calls.
|
||||
*
|
||||
* @return string
|
||||
* @throws \RuntimeException if no bundle can be found.
|
||||
*/
|
||||
public static function getDefaultCaBundle()
|
||||
{
|
||||
static $cached = null;
|
||||
static $cafiles = [
|
||||
// Red Hat, CentOS, Fedora (provided by the ca-certificates package)
|
||||
'/etc/pki/tls/certs/ca-bundle.crt',
|
||||
// Ubuntu, Debian (provided by the ca-certificates package)
|
||||
'/etc/ssl/certs/ca-certificates.crt',
|
||||
// FreeBSD (provided by the ca_root_nss package)
|
||||
'/usr/local/share/certs/ca-root-nss.crt',
|
||||
// OS X provided by homebrew (using the default path)
|
||||
'/usr/local/etc/openssl/cert.pem',
|
||||
// Windows?
|
||||
'C:\\windows\\system32\\curl-ca-bundle.crt',
|
||||
'C:\\windows\\curl-ca-bundle.crt',
|
||||
];
|
||||
|
||||
if ($cached) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
if ($ca = ini_get('openssl.cafile')) {
|
||||
return $cached = $ca;
|
||||
}
|
||||
|
||||
if ($ca = ini_get('curl.cainfo')) {
|
||||
return $cached = $ca;
|
||||
}
|
||||
|
||||
foreach ($cafiles as $filename) {
|
||||
if (file_exists($filename)) {
|
||||
return $cached = $filename;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException(self::CA_ERR);
|
||||
}
|
||||
|
||||
const CA_ERR = "
|
||||
No system CA bundle could be found in any of the the common system locations.
|
||||
PHP versions earlier than 5.6 are not properly configured to use the system's
|
||||
CA bundle by default. In order to verify peer certificates, you will need to
|
||||
supply the path on disk to a certificate bundle to the 'verify' request
|
||||
option: http://docs.guzzlephp.org/en/latest/clients.html#verify. If you do not
|
||||
need a specific certificate bundle, then Mozilla provides a commonly used CA
|
||||
bundle which can be downloaded here (provided by the maintainer of cURL):
|
||||
https://raw.githubusercontent.com/bagder/ca-bundle/master/ca-bundle.crt. Once
|
||||
you have a CA bundle available on disk, you can set the 'openssl.cafile' PHP
|
||||
ini setting to point to the path to the file, allowing you to omit the 'verify'
|
||||
request option. See http://curl.haxx.se/docs/sslcerts.html for more
|
||||
information.";
|
||||
}
|
546
core/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php
vendored
Normal file
546
core/vendor/guzzlehttp/ringphp/src/Client/CurlFactory.php
vendored
Normal file
|
@ -0,0 +1,546 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Exception\ConnectException;
|
||||
use GuzzleHttp\Ring\Exception\RingException;
|
||||
use GuzzleHttp\Stream\LazyOpenStream;
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
|
||||
/**
|
||||
* Creates curl resources from a request
|
||||
*/
|
||||
class CurlFactory
|
||||
{
|
||||
/**
|
||||
* Creates a cURL handle, header resource, and body resource based on a
|
||||
* transaction.
|
||||
*
|
||||
* @param array $request Request hash
|
||||
* @param null|resource $handle Optionally provide a curl handle to modify
|
||||
*
|
||||
* @return array Returns an array of the curl handle, headers array, and
|
||||
* response body handle.
|
||||
* @throws \RuntimeException when an option cannot be applied
|
||||
*/
|
||||
public function __invoke(array $request, $handle = null)
|
||||
{
|
||||
$headers = [];
|
||||
$options = $this->getDefaultOptions($request, $headers);
|
||||
$this->applyMethod($request, $options);
|
||||
|
||||
if (isset($request['client'])) {
|
||||
$this->applyHandlerOptions($request, $options);
|
||||
}
|
||||
|
||||
$this->applyHeaders($request, $options);
|
||||
unset($options['_headers']);
|
||||
|
||||
// Add handler options from the request's configuration options
|
||||
if (isset($request['client']['curl'])) {
|
||||
$options = $this->applyCustomCurlOptions(
|
||||
$request['client']['curl'],
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
if (!$handle) {
|
||||
$handle = curl_init();
|
||||
}
|
||||
|
||||
$body = $this->getOutputBody($request, $options);
|
||||
curl_setopt_array($handle, $options);
|
||||
|
||||
return [$handle, &$headers, $body];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a response hash from a cURL result.
|
||||
*
|
||||
* @param callable $handler Handler that was used.
|
||||
* @param array $request Request that sent.
|
||||
* @param array $response Response hash to update.
|
||||
* @param array $headers Headers received during transfer.
|
||||
* @param resource $body Body fopen response.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function createResponse(
|
||||
callable $handler,
|
||||
array $request,
|
||||
array $response,
|
||||
array $headers,
|
||||
$body
|
||||
) {
|
||||
if (isset($response['transfer_stats']['url'])) {
|
||||
$response['effective_url'] = $response['transfer_stats']['url'];
|
||||
}
|
||||
|
||||
if (!empty($headers)) {
|
||||
$startLine = explode(' ', array_shift($headers), 3);
|
||||
$headerList = Core::headersFromLines($headers);
|
||||
$response['headers'] = $headerList;
|
||||
$response['status'] = isset($startLine[1]) ? (int) $startLine[1] : null;
|
||||
$response['reason'] = isset($startLine[2]) ? $startLine[2] : null;
|
||||
$response['body'] = $body;
|
||||
Core::rewindBody($response);
|
||||
}
|
||||
|
||||
return !empty($response['curl']['errno']) || !isset($response['status'])
|
||||
? self::createErrorResponse($handler, $request, $response)
|
||||
: $response;
|
||||
}
|
||||
|
||||
private static function createErrorResponse(
|
||||
callable $handler,
|
||||
array $request,
|
||||
array $response
|
||||
) {
|
||||
static $connectionErrors = [
|
||||
CURLE_OPERATION_TIMEOUTED => true,
|
||||
CURLE_COULDNT_RESOLVE_HOST => true,
|
||||
CURLE_COULDNT_CONNECT => true,
|
||||
CURLE_SSL_CONNECT_ERROR => true,
|
||||
CURLE_GOT_NOTHING => true,
|
||||
];
|
||||
|
||||
// Retry when nothing is present or when curl failed to rewind.
|
||||
if (!isset($response['err_message'])
|
||||
&& (empty($response['curl']['errno'])
|
||||
|| $response['curl']['errno'] == 65)
|
||||
) {
|
||||
return self::retryFailedRewind($handler, $request, $response);
|
||||
}
|
||||
|
||||
$message = isset($response['err_message'])
|
||||
? $response['err_message']
|
||||
: sprintf('cURL error %s: %s',
|
||||
$response['curl']['errno'],
|
||||
isset($response['curl']['error'])
|
||||
? $response['curl']['error']
|
||||
: 'See http://curl.haxx.se/libcurl/c/libcurl-errors.html');
|
||||
|
||||
$error = isset($response['curl']['errno'])
|
||||
&& isset($connectionErrors[$response['curl']['errno']])
|
||||
? new ConnectException($message)
|
||||
: new RingException($message);
|
||||
|
||||
return $response + [
|
||||
'status' => null,
|
||||
'reason' => null,
|
||||
'body' => null,
|
||||
'headers' => [],
|
||||
'error' => $error,
|
||||
];
|
||||
}
|
||||
|
||||
private function getOutputBody(array $request, array &$options)
|
||||
{
|
||||
// Determine where the body of the response (if any) will be streamed.
|
||||
if (isset($options[CURLOPT_WRITEFUNCTION])) {
|
||||
return $request['client']['save_to'];
|
||||
}
|
||||
|
||||
if (isset($options[CURLOPT_FILE])) {
|
||||
return $options[CURLOPT_FILE];
|
||||
}
|
||||
|
||||
if ($request['http_method'] != 'HEAD') {
|
||||
// Create a default body if one was not provided
|
||||
return $options[CURLOPT_FILE] = fopen('php://temp', 'w+');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getDefaultOptions(array $request, array &$headers)
|
||||
{
|
||||
$url = Core::url($request);
|
||||
$startingResponse = false;
|
||||
|
||||
$options = [
|
||||
'_headers' => $request['headers'],
|
||||
CURLOPT_CUSTOMREQUEST => $request['http_method'],
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => false,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_CONNECTTIMEOUT => 150,
|
||||
CURLOPT_HEADERFUNCTION => function ($ch, $h) use (&$headers, &$startingResponse) {
|
||||
$value = trim($h);
|
||||
if ($value === '') {
|
||||
$startingResponse = true;
|
||||
} elseif ($startingResponse) {
|
||||
$startingResponse = false;
|
||||
$headers = [$value];
|
||||
} else {
|
||||
$headers[] = $value;
|
||||
}
|
||||
return strlen($h);
|
||||
},
|
||||
];
|
||||
|
||||
if (isset($request['version'])) {
|
||||
$options[CURLOPT_HTTP_VERSION] = $request['version'] == 1.1 ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0;
|
||||
}
|
||||
|
||||
if (defined('CURLOPT_PROTOCOLS')) {
|
||||
$options[CURLOPT_PROTOCOLS] = CURLPROTO_HTTP | CURLPROTO_HTTPS;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function applyMethod(array $request, array &$options)
|
||||
{
|
||||
if (isset($request['body'])) {
|
||||
$this->applyBody($request, $options);
|
||||
return;
|
||||
}
|
||||
|
||||
switch ($request['http_method']) {
|
||||
case 'PUT':
|
||||
case 'POST':
|
||||
// See http://tools.ietf.org/html/rfc7230#section-3.3.2
|
||||
if (!Core::hasHeader($request, 'Content-Length')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Content-Length: 0';
|
||||
}
|
||||
break;
|
||||
case 'HEAD':
|
||||
$options[CURLOPT_NOBODY] = true;
|
||||
unset(
|
||||
$options[CURLOPT_WRITEFUNCTION],
|
||||
$options[CURLOPT_READFUNCTION],
|
||||
$options[CURLOPT_FILE],
|
||||
$options[CURLOPT_INFILE]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function applyBody(array $request, array &$options)
|
||||
{
|
||||
$contentLength = Core::firstHeader($request, 'Content-Length');
|
||||
$size = $contentLength !== null ? (int) $contentLength : null;
|
||||
|
||||
// Send the body as a string if the size is less than 1MB OR if the
|
||||
// [client][curl][body_as_string] request value is set.
|
||||
if (($size !== null && $size < 1000000) ||
|
||||
isset($request['client']['curl']['body_as_string']) ||
|
||||
is_string($request['body'])
|
||||
) {
|
||||
$options[CURLOPT_POSTFIELDS] = Core::body($request);
|
||||
// Don't duplicate the Content-Length header
|
||||
$this->removeHeader('Content-Length', $options);
|
||||
$this->removeHeader('Transfer-Encoding', $options);
|
||||
} else {
|
||||
$options[CURLOPT_UPLOAD] = true;
|
||||
if ($size !== null) {
|
||||
// Let cURL handle setting the Content-Length header
|
||||
$options[CURLOPT_INFILESIZE] = $size;
|
||||
$this->removeHeader('Content-Length', $options);
|
||||
}
|
||||
$this->addStreamingBody($request, $options);
|
||||
}
|
||||
|
||||
// If the Expect header is not present, prevent curl from adding it
|
||||
if (!Core::hasHeader($request, 'Expect')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Expect:';
|
||||
}
|
||||
|
||||
// cURL sometimes adds a content-type by default. Prevent this.
|
||||
if (!Core::hasHeader($request, 'Content-Type')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Content-Type:';
|
||||
}
|
||||
}
|
||||
|
||||
private function addStreamingBody(array $request, array &$options)
|
||||
{
|
||||
$body = $request['body'];
|
||||
|
||||
if ($body instanceof StreamInterface) {
|
||||
$options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body) {
|
||||
return (string) $body->read($length);
|
||||
};
|
||||
if (!isset($options[CURLOPT_INFILESIZE])) {
|
||||
if ($size = $body->getSize()) {
|
||||
$options[CURLOPT_INFILESIZE] = $size;
|
||||
}
|
||||
}
|
||||
} elseif (is_resource($body)) {
|
||||
$options[CURLOPT_INFILE] = $body;
|
||||
} elseif ($body instanceof \Iterator) {
|
||||
$buf = '';
|
||||
$options[CURLOPT_READFUNCTION] = function ($ch, $fd, $length) use ($body, &$buf) {
|
||||
if ($body->valid()) {
|
||||
$buf .= $body->current();
|
||||
$body->next();
|
||||
}
|
||||
$result = (string) substr($buf, 0, $length);
|
||||
$buf = substr($buf, $length);
|
||||
return $result;
|
||||
};
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid request body provided');
|
||||
}
|
||||
}
|
||||
|
||||
private function applyHeaders(array $request, array &$options)
|
||||
{
|
||||
foreach ($options['_headers'] as $name => $values) {
|
||||
foreach ($values as $value) {
|
||||
$options[CURLOPT_HTTPHEADER][] = "$name: $value";
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the Accept header if one was not set
|
||||
if (!Core::hasHeader($request, 'Accept')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Accept:';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an array of curl options specified in the 'curl' option of a
|
||||
* request's configuration array and maps them to CURLOPT_* options.
|
||||
*
|
||||
* This method is only called when a request has a 'curl' config setting.
|
||||
*
|
||||
* @param array $config Configuration array of custom curl option
|
||||
* @param array $options Array of existing curl options
|
||||
*
|
||||
* @return array Returns a new array of curl options
|
||||
*/
|
||||
private function applyCustomCurlOptions(array $config, array $options)
|
||||
{
|
||||
$curlOptions = [];
|
||||
foreach ($config as $key => $value) {
|
||||
if (is_int($key)) {
|
||||
$curlOptions[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $curlOptions + $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a header from the options array.
|
||||
*
|
||||
* @param string $name Case-insensitive header to remove
|
||||
* @param array $options Array of options to modify
|
||||
*/
|
||||
private function removeHeader($name, array &$options)
|
||||
{
|
||||
foreach (array_keys($options['_headers']) as $key) {
|
||||
if (!strcasecmp($key, $name)) {
|
||||
unset($options['_headers'][$key]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies an array of request client options to a the options array.
|
||||
*
|
||||
* This method uses a large switch rather than double-dispatch to save on
|
||||
* high overhead of calling functions in PHP.
|
||||
*/
|
||||
private function applyHandlerOptions(array $request, array &$options)
|
||||
{
|
||||
foreach ($request['client'] as $key => $value) {
|
||||
switch ($key) {
|
||||
// Violating PSR-4 to provide more room.
|
||||
case 'verify':
|
||||
|
||||
if ($value === false) {
|
||||
unset($options[CURLOPT_CAINFO]);
|
||||
$options[CURLOPT_SSL_VERIFYHOST] = 0;
|
||||
$options[CURLOPT_SSL_VERIFYPEER] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSL_VERIFYHOST] = 2;
|
||||
$options[CURLOPT_SSL_VERIFYPEER] = true;
|
||||
|
||||
if (is_string($value)) {
|
||||
$options[CURLOPT_CAINFO] = $value;
|
||||
if (!file_exists($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL CA bundle not found: $value"
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'decode_content':
|
||||
|
||||
if ($value === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$accept = Core::firstHeader($request, 'Accept-Encoding');
|
||||
if ($accept) {
|
||||
$options[CURLOPT_ENCODING] = $accept;
|
||||
} else {
|
||||
$options[CURLOPT_ENCODING] = '';
|
||||
// Don't let curl send the header over the wire
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Accept-Encoding:';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'save_to':
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = new LazyOpenStream($value, 'w+');
|
||||
}
|
||||
|
||||
if ($value instanceof StreamInterface) {
|
||||
$options[CURLOPT_WRITEFUNCTION] =
|
||||
function ($ch, $write) use ($value) {
|
||||
return $value->write($write);
|
||||
};
|
||||
} elseif (is_resource($value)) {
|
||||
$options[CURLOPT_FILE] = $value;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('save_to must be a '
|
||||
. 'GuzzleHttp\Stream\StreamInterface or resource');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'timeout':
|
||||
|
||||
if (defined('CURLOPT_TIMEOUT_MS')) {
|
||||
$options[CURLOPT_TIMEOUT_MS] = $value * 1000;
|
||||
} else {
|
||||
$options[CURLOPT_TIMEOUT] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'connect_timeout':
|
||||
|
||||
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
|
||||
$options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000;
|
||||
} else {
|
||||
$options[CURLOPT_CONNECTTIMEOUT] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'proxy':
|
||||
|
||||
if (!is_array($value)) {
|
||||
$options[CURLOPT_PROXY] = $value;
|
||||
} elseif (isset($request['scheme'])) {
|
||||
$scheme = $request['scheme'];
|
||||
if (isset($value[$scheme])) {
|
||||
$options[CURLOPT_PROXY] = $value[$scheme];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cert':
|
||||
|
||||
if (is_array($value)) {
|
||||
$options[CURLOPT_SSLCERTPASSWD] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL certificate not found: {$value}"
|
||||
);
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSLCERT] = $value;
|
||||
break;
|
||||
|
||||
case 'ssl_key':
|
||||
|
||||
if (is_array($value)) {
|
||||
$options[CURLOPT_SSLKEYPASSWD] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
"SSL private key not found: {$value}"
|
||||
);
|
||||
}
|
||||
|
||||
$options[CURLOPT_SSLKEY] = $value;
|
||||
break;
|
||||
|
||||
case 'progress':
|
||||
|
||||
if (!is_callable($value)) {
|
||||
throw new \InvalidArgumentException(
|
||||
'progress client option must be callable'
|
||||
);
|
||||
}
|
||||
|
||||
$options[CURLOPT_NOPROGRESS] = false;
|
||||
$options[CURLOPT_PROGRESSFUNCTION] =
|
||||
function () use ($value) {
|
||||
$args = func_get_args();
|
||||
// PHP 5.5 pushed the handle onto the start of the args
|
||||
if (is_resource($args[0])) {
|
||||
array_shift($args);
|
||||
}
|
||||
call_user_func_array($value, $args);
|
||||
};
|
||||
break;
|
||||
|
||||
case 'debug':
|
||||
|
||||
if ($value) {
|
||||
$options[CURLOPT_STDERR] = Core::getDebugResource($value);
|
||||
$options[CURLOPT_VERBOSE] = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function ensures that a response was set on a transaction. If one
|
||||
* was not set, then the request is retried if possible. This error
|
||||
* typically means you are sending a payload, curl encountered a
|
||||
* "Connection died, retrying a fresh connect" error, tried to rewind the
|
||||
* stream, and then encountered a "necessary data rewind wasn't possible"
|
||||
* error, causing the request to be sent through curl_multi_info_read()
|
||||
* without an error status.
|
||||
*/
|
||||
private static function retryFailedRewind(
|
||||
callable $handler,
|
||||
array $request,
|
||||
array $response
|
||||
) {
|
||||
// If there is no body, then there is some other kind of issue. This
|
||||
// is weird and should probably never happen.
|
||||
if (!isset($request['body'])) {
|
||||
$response['err_message'] = 'No response was received for a request '
|
||||
. 'with no body. This could mean that you are saturating your '
|
||||
. 'network.';
|
||||
return self::createErrorResponse($handler, $request, $response);
|
||||
}
|
||||
|
||||
if (!Core::rewindBody($request)) {
|
||||
$response['err_message'] = 'The connection unexpectedly failed '
|
||||
. 'without providing an error. The request would have been '
|
||||
. 'retried, but attempting to rewind the request body failed.';
|
||||
return self::createErrorResponse($handler, $request, $response);
|
||||
}
|
||||
|
||||
// Retry no more than 3 times before giving up.
|
||||
if (!isset($request['curl']['retries'])) {
|
||||
$request['curl']['retries'] = 1;
|
||||
} elseif ($request['curl']['retries'] == 2) {
|
||||
$response['err_message'] = 'The cURL request was retried 3 times '
|
||||
. 'and did no succeed. cURL was unable to rewind the body of '
|
||||
. 'the request and subsequent retries resulted in the same '
|
||||
. 'error. Turn on the debug option to see what went wrong. '
|
||||
. 'See https://bugs.php.net/bug.php?id=47204 for more information.';
|
||||
return self::createErrorResponse($handler, $request, $response);
|
||||
} else {
|
||||
$request['curl']['retries']++;
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
}
|
118
core/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php
vendored
Normal file
118
core/vendor/guzzlehttp/ringphp/src/Client/CurlHandler.php
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
use GuzzleHttp\Ring\Core;
|
||||
|
||||
/**
|
||||
* HTTP handler that uses cURL easy handles as a transport layer.
|
||||
*
|
||||
* Requires PHP 5.5+
|
||||
*
|
||||
* When using the CurlHandler, custom curl options can be specified as an
|
||||
* associative array of curl option constants mapping to values in the
|
||||
* **curl** key of the "client" key of the request.
|
||||
*/
|
||||
class CurlHandler
|
||||
{
|
||||
/** @var callable */
|
||||
private $factory;
|
||||
|
||||
/** @var array Array of curl easy handles */
|
||||
private $handles = [];
|
||||
|
||||
/** @var array Array of owned curl easy handles */
|
||||
private $ownedHandles = [];
|
||||
|
||||
/** @var int Total number of idle handles to keep in cache */
|
||||
private $maxHandles;
|
||||
|
||||
/**
|
||||
* Accepts an associative array of options:
|
||||
*
|
||||
* - factory: Optional callable factory used to create cURL handles.
|
||||
* The callable is passed a request hash when invoked, and returns an
|
||||
* array of the curl handle, headers resource, and body resource.
|
||||
* - max_handles: Maximum number of idle handles (defaults to 5).
|
||||
*
|
||||
* @param array $options Array of options to use with the handler
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->handles = $this->ownedHandles = [];
|
||||
$this->factory = isset($options['handle_factory'])
|
||||
? $options['handle_factory']
|
||||
: new CurlFactory();
|
||||
$this->maxHandles = isset($options['max_handles'])
|
||||
? $options['max_handles']
|
||||
: 5;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
foreach ($this->handles as $handle) {
|
||||
if (is_resource($handle)) {
|
||||
curl_close($handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
$factory = $this->factory;
|
||||
|
||||
// Ensure headers are by reference. They're updated elsewhere.
|
||||
$result = $factory($request, $this->checkoutEasyHandle());
|
||||
$h = $result[0];
|
||||
$hd =& $result[1];
|
||||
$bd = $result[2];
|
||||
Core::doSleep($request);
|
||||
curl_exec($h);
|
||||
$response = ['transfer_stats' => curl_getinfo($h)];
|
||||
$response['curl']['error'] = curl_error($h);
|
||||
$response['curl']['errno'] = curl_errno($h);
|
||||
$response['transfer_stats'] = array_merge($response['transfer_stats'], $response['curl']);
|
||||
$this->releaseEasyHandle($h);
|
||||
|
||||
return new CompletedFutureArray(
|
||||
CurlFactory::createResponse($this, $request, $response, $hd, $bd)
|
||||
);
|
||||
}
|
||||
|
||||
private function checkoutEasyHandle()
|
||||
{
|
||||
// Find an unused handle in the cache
|
||||
if (false !== ($key = array_search(false, $this->ownedHandles, true))) {
|
||||
$this->ownedHandles[$key] = true;
|
||||
return $this->handles[$key];
|
||||
}
|
||||
|
||||
// Add a new handle
|
||||
$handle = curl_init();
|
||||
$id = (int) $handle;
|
||||
$this->handles[$id] = $handle;
|
||||
$this->ownedHandles[$id] = true;
|
||||
|
||||
return $handle;
|
||||
}
|
||||
|
||||
private function releaseEasyHandle($handle)
|
||||
{
|
||||
$id = (int) $handle;
|
||||
if (count($this->ownedHandles) > $this->maxHandles) {
|
||||
curl_close($this->handles[$id]);
|
||||
unset($this->handles[$id], $this->ownedHandles[$id]);
|
||||
} else {
|
||||
// curl_reset doesn't clear these out for some reason
|
||||
static $unsetValues = [
|
||||
CURLOPT_HEADERFUNCTION => null,
|
||||
CURLOPT_WRITEFUNCTION => null,
|
||||
CURLOPT_READFUNCTION => null,
|
||||
CURLOPT_PROGRESSFUNCTION => null,
|
||||
];
|
||||
curl_setopt_array($handle, $unsetValues);
|
||||
curl_reset($handle);
|
||||
$this->ownedHandles[$id] = false;
|
||||
}
|
||||
}
|
||||
}
|
250
core/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php
vendored
Normal file
250
core/vendor/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Future\FutureArray;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
/**
|
||||
* Returns an asynchronous response using curl_multi_* functions.
|
||||
*
|
||||
* This handler supports future responses and the "delay" request client
|
||||
* option that can be used to delay before sending a request.
|
||||
*
|
||||
* When using the CurlMultiHandler, custom curl options can be specified as an
|
||||
* associative array of curl option constants mapping to values in the
|
||||
* **curl** key of the "client" key of the request.
|
||||
*
|
||||
* @property resource $_mh Internal use only. Lazy loaded multi-handle.
|
||||
*/
|
||||
class CurlMultiHandler
|
||||
{
|
||||
/** @var callable */
|
||||
private $factory;
|
||||
private $selectTimeout;
|
||||
private $active;
|
||||
private $handles = [];
|
||||
private $delays = [];
|
||||
private $maxHandles;
|
||||
|
||||
/**
|
||||
* This handler accepts the following options:
|
||||
*
|
||||
* - mh: An optional curl_multi resource
|
||||
* - handle_factory: An optional callable used to generate curl handle
|
||||
* resources. the callable accepts a request hash and returns an array
|
||||
* of the handle, headers file resource, and the body resource.
|
||||
* - select_timeout: Optional timeout (in seconds) to block before timing
|
||||
* out while selecting curl handles. Defaults to 1 second.
|
||||
* - max_handles: Optional integer representing the maximum number of
|
||||
* open requests. When this number is reached, the queued futures are
|
||||
* flushed.
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
if (isset($options['mh'])) {
|
||||
$this->_mh = $options['mh'];
|
||||
}
|
||||
$this->factory = isset($options['handle_factory'])
|
||||
? $options['handle_factory'] : new CurlFactory();
|
||||
$this->selectTimeout = isset($options['select_timeout'])
|
||||
? $options['select_timeout'] : 1;
|
||||
$this->maxHandles = isset($options['max_handles'])
|
||||
? $options['max_handles'] : 100;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name === '_mh') {
|
||||
return $this->_mh = curl_multi_init();
|
||||
}
|
||||
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Finish any open connections before terminating the script.
|
||||
if ($this->handles) {
|
||||
$this->execute();
|
||||
}
|
||||
|
||||
if (isset($this->_mh)) {
|
||||
curl_multi_close($this->_mh);
|
||||
unset($this->_mh);
|
||||
}
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
$factory = $this->factory;
|
||||
$result = $factory($request);
|
||||
$entry = [
|
||||
'request' => $request,
|
||||
'response' => [],
|
||||
'handle' => $result[0],
|
||||
'headers' => &$result[1],
|
||||
'body' => $result[2],
|
||||
'deferred' => new Deferred(),
|
||||
];
|
||||
|
||||
$id = (int) $result[0];
|
||||
|
||||
$future = new FutureArray(
|
||||
$entry['deferred']->promise(),
|
||||
[$this, 'execute'],
|
||||
function () use ($id) {
|
||||
return $this->cancel($id);
|
||||
}
|
||||
);
|
||||
|
||||
$this->addRequest($entry);
|
||||
|
||||
// Transfer outstanding requests if there are too many open handles.
|
||||
if (count($this->handles) >= $this->maxHandles) {
|
||||
$this->execute();
|
||||
}
|
||||
|
||||
return $future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs until all outstanding connections have completed.
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
do {
|
||||
|
||||
if ($this->active &&
|
||||
curl_multi_select($this->_mh, $this->selectTimeout) === -1
|
||||
) {
|
||||
// Perform a usleep if a select returns -1.
|
||||
// See: https://bugs.php.net/bug.php?id=61141
|
||||
usleep(250);
|
||||
}
|
||||
|
||||
// Add any delayed futures if needed.
|
||||
if ($this->delays) {
|
||||
$this->addDelays();
|
||||
}
|
||||
|
||||
do {
|
||||
$mrc = curl_multi_exec($this->_mh, $this->active);
|
||||
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
||||
|
||||
$this->processMessages();
|
||||
|
||||
// If there are delays but no transfers, then sleep for a bit.
|
||||
if (!$this->active && $this->delays) {
|
||||
usleep(500);
|
||||
}
|
||||
|
||||
} while ($this->active || $this->handles);
|
||||
}
|
||||
|
||||
private function addRequest(array &$entry)
|
||||
{
|
||||
$id = (int) $entry['handle'];
|
||||
$this->handles[$id] = $entry;
|
||||
|
||||
// If the request is a delay, then add the reques to the curl multi
|
||||
// pool only after the specified delay.
|
||||
if (isset($entry['request']['client']['delay'])) {
|
||||
$this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000);
|
||||
} elseif (empty($entry['request']['future'])) {
|
||||
curl_multi_add_handle($this->_mh, $entry['handle']);
|
||||
} else {
|
||||
curl_multi_add_handle($this->_mh, $entry['handle']);
|
||||
// "lazy" futures are only sent once the pool has many requests.
|
||||
if ($entry['request']['future'] !== 'lazy') {
|
||||
do {
|
||||
$mrc = curl_multi_exec($this->_mh, $this->active);
|
||||
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
||||
$this->processMessages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function removeProcessed($id)
|
||||
{
|
||||
if (isset($this->handles[$id])) {
|
||||
curl_multi_remove_handle(
|
||||
$this->_mh,
|
||||
$this->handles[$id]['handle']
|
||||
);
|
||||
curl_close($this->handles[$id]['handle']);
|
||||
unset($this->handles[$id], $this->delays[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a handle from sending and removes references to it.
|
||||
*
|
||||
* @param int $id Handle ID to cancel and remove.
|
||||
*
|
||||
* @return bool True on success, false on failure.
|
||||
*/
|
||||
private function cancel($id)
|
||||
{
|
||||
// Cannot cancel if it has been processed.
|
||||
if (!isset($this->handles[$id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$handle = $this->handles[$id]['handle'];
|
||||
unset($this->delays[$id], $this->handles[$id]);
|
||||
curl_multi_remove_handle($this->_mh, $handle);
|
||||
curl_close($handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function addDelays()
|
||||
{
|
||||
$currentTime = microtime(true);
|
||||
|
||||
foreach ($this->delays as $id => $delay) {
|
||||
if ($currentTime >= $delay) {
|
||||
unset($this->delays[$id]);
|
||||
curl_multi_add_handle(
|
||||
$this->_mh,
|
||||
$this->handles[$id]['handle']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processMessages()
|
||||
{
|
||||
while ($done = curl_multi_info_read($this->_mh)) {
|
||||
$id = (int) $done['handle'];
|
||||
|
||||
if (!isset($this->handles[$id])) {
|
||||
// Probably was cancelled.
|
||||
continue;
|
||||
}
|
||||
|
||||
$entry = $this->handles[$id];
|
||||
$entry['response']['transfer_stats'] = curl_getinfo($done['handle']);
|
||||
|
||||
if ($done['result'] !== CURLM_OK) {
|
||||
$entry['response']['curl']['errno'] = $done['result'];
|
||||
if (function_exists('curl_strerror')) {
|
||||
$entry['response']['curl']['error'] = curl_strerror($done['result']);
|
||||
}
|
||||
}
|
||||
|
||||
$result = CurlFactory::createResponse(
|
||||
$this,
|
||||
$entry['request'],
|
||||
$entry['response'],
|
||||
$entry['headers'],
|
||||
$entry['body']
|
||||
);
|
||||
|
||||
$this->removeProcessed($id);
|
||||
$entry['deferred']->resolve($result);
|
||||
}
|
||||
}
|
||||
}
|
58
core/vendor/guzzlehttp/ringphp/src/Client/Middleware.php
vendored
Normal file
58
core/vendor/guzzlehttp/ringphp/src/Client/Middleware.php
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
/**
|
||||
* Provides basic middleware wrappers.
|
||||
*
|
||||
* If a middleware is more complex than a few lines of code, then it should
|
||||
* be implemented in a class rather than a static method.
|
||||
*/
|
||||
class Middleware
|
||||
{
|
||||
/**
|
||||
* Sends future requests to a future compatible handler while sending all
|
||||
* other requests to a default handler.
|
||||
*
|
||||
* When the "future" option is not provided on a request, any future responses
|
||||
* are automatically converted to synchronous responses and block.
|
||||
*
|
||||
* @param callable $default Handler used for non-streaming responses
|
||||
* @param callable $future Handler used for future responses
|
||||
*
|
||||
* @return callable Returns the composed handler.
|
||||
*/
|
||||
public static function wrapFuture(
|
||||
callable $default,
|
||||
callable $future
|
||||
) {
|
||||
return function (array $request) use ($default, $future) {
|
||||
return empty($request['client']['future'])
|
||||
? $default($request)
|
||||
: $future($request);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends streaming requests to a streaming compatible handler while sendin
|
||||
* all other requests to a default handler.
|
||||
*
|
||||
* This, for example, could be useful for taking advantage of the
|
||||
* performance benefits of curl while still supporting true streaming
|
||||
* through the StreamHandler.
|
||||
*
|
||||
* @param callable $default Handler used for non-streaming responses
|
||||
* @param callable $streaming Handler used for streaming responses
|
||||
*
|
||||
* @return callable Returns the composed handler.
|
||||
*/
|
||||
public static function wrapStreaming(
|
||||
callable $default,
|
||||
callable $streaming
|
||||
) {
|
||||
return function (array $request) use ($default, $streaming) {
|
||||
return empty($request['client']['stream'])
|
||||
? $default($request)
|
||||
: $streaming($request);
|
||||
};
|
||||
}
|
||||
}
|
52
core/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php
vendored
Normal file
52
core/vendor/guzzlehttp/ringphp/src/Client/MockHandler.php
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
use GuzzleHttp\Ring\Future\FutureArrayInterface;
|
||||
|
||||
/**
|
||||
* Ring handler that returns a canned response or evaluated function result.
|
||||
*/
|
||||
class MockHandler
|
||||
{
|
||||
/** @var callable|array|FutureArrayInterface */
|
||||
private $result;
|
||||
|
||||
/**
|
||||
* Provide an array or future to always return the same value. Provide a
|
||||
* callable that accepts a request object and returns an array or future
|
||||
* to dynamically create a response.
|
||||
*
|
||||
* @param array|FutureArrayInterface|callable $result Mock return value.
|
||||
*/
|
||||
public function __construct($result)
|
||||
{
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
Core::doSleep($request);
|
||||
$response = is_callable($this->result)
|
||||
? call_user_func($this->result, $request)
|
||||
: $this->result;
|
||||
|
||||
if (is_array($response)) {
|
||||
$response = new CompletedFutureArray($response + [
|
||||
'status' => null,
|
||||
'body' => null,
|
||||
'headers' => [],
|
||||
'reason' => null,
|
||||
'effective_url' => null,
|
||||
]);
|
||||
} elseif (!$response instanceof FutureArrayInterface) {
|
||||
throw new \InvalidArgumentException(
|
||||
'Response must be an array or FutureArrayInterface. Found '
|
||||
. Core::describeType($request)
|
||||
);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
412
core/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php
vendored
Normal file
412
core/vendor/guzzlehttp/ringphp/src/Client/StreamHandler.php
vendored
Normal file
|
@ -0,0 +1,412 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Exception\ConnectException;
|
||||
use GuzzleHttp\Ring\Exception\RingException;
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
use GuzzleHttp\Stream\InflateStream;
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
use GuzzleHttp\Stream\Utils;
|
||||
|
||||
/**
|
||||
* RingPHP client handler that uses PHP's HTTP stream wrapper.
|
||||
*/
|
||||
class StreamHandler
|
||||
{
|
||||
private $options;
|
||||
private $lastHeaders;
|
||||
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function __invoke(array $request)
|
||||
{
|
||||
$url = Core::url($request);
|
||||
Core::doSleep($request);
|
||||
|
||||
try {
|
||||
// Does not support the expect header.
|
||||
$request = Core::removeHeader($request, 'Expect');
|
||||
$stream = $this->createStream($url, $request);
|
||||
return $this->createResponse($request, $url, $stream);
|
||||
} catch (RingException $e) {
|
||||
return $this->createErrorResponse($url, $e);
|
||||
}
|
||||
}
|
||||
|
||||
private function createResponse(array $request, $url, $stream)
|
||||
{
|
||||
$hdrs = $this->lastHeaders;
|
||||
$this->lastHeaders = null;
|
||||
$parts = explode(' ', array_shift($hdrs), 3);
|
||||
$response = [
|
||||
'status' => $parts[1],
|
||||
'reason' => isset($parts[2]) ? $parts[2] : null,
|
||||
'headers' => Core::headersFromLines($hdrs),
|
||||
'effective_url' => $url,
|
||||
];
|
||||
|
||||
$stream = $this->checkDecode($request, $response, $stream);
|
||||
|
||||
// If not streaming, then drain the response into a stream.
|
||||
if (empty($request['client']['stream'])) {
|
||||
$dest = isset($request['client']['save_to'])
|
||||
? $request['client']['save_to']
|
||||
: fopen('php://temp', 'r+');
|
||||
$stream = $this->drain($stream, $dest);
|
||||
}
|
||||
|
||||
$response['body'] = $stream;
|
||||
|
||||
return new CompletedFutureArray($response);
|
||||
}
|
||||
|
||||
private function checkDecode(array $request, array $response, $stream)
|
||||
{
|
||||
// Automatically decode responses when instructed.
|
||||
if (!empty($request['client']['decode_content'])) {
|
||||
switch (Core::firstHeader($response, 'Content-Encoding', true)) {
|
||||
case 'gzip':
|
||||
case 'deflate':
|
||||
$stream = new InflateStream(Stream::factory($stream));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drains the stream into the "save_to" client option.
|
||||
*
|
||||
* @param resource $stream
|
||||
* @param string|resource|StreamInterface $dest
|
||||
*
|
||||
* @return Stream
|
||||
* @throws \RuntimeException when the save_to option is invalid.
|
||||
*/
|
||||
private function drain($stream, $dest)
|
||||
{
|
||||
if (is_resource($stream)) {
|
||||
if (!is_resource($dest)) {
|
||||
$stream = Stream::factory($stream);
|
||||
} else {
|
||||
stream_copy_to_stream($stream, $dest);
|
||||
fclose($stream);
|
||||
rewind($dest);
|
||||
return $dest;
|
||||
}
|
||||
}
|
||||
|
||||
// Stream the response into the destination stream
|
||||
$dest = is_string($dest)
|
||||
? new Stream(Utils::open($dest, 'r+'))
|
||||
: Stream::factory($dest);
|
||||
|
||||
Utils::copyToStream($stream, $dest);
|
||||
$dest->seek(0);
|
||||
$stream->close();
|
||||
|
||||
return $dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an error response for the given stream.
|
||||
*
|
||||
* @param string $url
|
||||
* @param RingException $e
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function createErrorResponse($url, RingException $e)
|
||||
{
|
||||
// Determine if the error was a networking error.
|
||||
$message = $e->getMessage();
|
||||
|
||||
// This list can probably get more comprehensive.
|
||||
if (strpos($message, 'getaddrinfo') // DNS lookup failed
|
||||
|| strpos($message, 'Connection refused')
|
||||
) {
|
||||
$e = new ConnectException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
return new CompletedFutureArray([
|
||||
'status' => null,
|
||||
'body' => null,
|
||||
'headers' => [],
|
||||
'effective_url' => $url,
|
||||
'error' => $e
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a resource and check to ensure it was created successfully
|
||||
*
|
||||
* @param callable $callback Callable that returns stream resource
|
||||
*
|
||||
* @return resource
|
||||
* @throws \RuntimeException on error
|
||||
*/
|
||||
private function createResource(callable $callback)
|
||||
{
|
||||
$errors = null;
|
||||
set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
|
||||
$errors[] = [
|
||||
'message' => $msg,
|
||||
'file' => $file,
|
||||
'line' => $line
|
||||
];
|
||||
return true;
|
||||
});
|
||||
|
||||
$resource = $callback();
|
||||
restore_error_handler();
|
||||
|
||||
if (!$resource) {
|
||||
$message = 'Error creating resource: ';
|
||||
foreach ($errors as $err) {
|
||||
foreach ($err as $key => $value) {
|
||||
$message .= "[$key] $value" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
throw new RingException(trim($message));
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
|
||||
private function createStream($url, array $request)
|
||||
{
|
||||
static $methods;
|
||||
if (!$methods) {
|
||||
$methods = array_flip(get_class_methods(__CLASS__));
|
||||
}
|
||||
|
||||
// HTTP/1.1 streams using the PHP stream wrapper require a
|
||||
// Connection: close header
|
||||
if ((!isset($request['version']) || $request['version'] == '1.1')
|
||||
&& !Core::hasHeader($request, 'Connection')
|
||||
) {
|
||||
$request['headers']['Connection'] = ['close'];
|
||||
}
|
||||
|
||||
// Ensure SSL is verified by default
|
||||
if (!isset($request['client']['verify'])) {
|
||||
$request['client']['verify'] = true;
|
||||
}
|
||||
|
||||
$params = [];
|
||||
$options = $this->getDefaultOptions($request);
|
||||
|
||||
if (isset($request['client'])) {
|
||||
foreach ($request['client'] as $key => $value) {
|
||||
$method = "add_{$key}";
|
||||
if (isset($methods[$method])) {
|
||||
$this->{$method}($request, $options, $value, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->createStreamResource(
|
||||
$url,
|
||||
$request,
|
||||
$options,
|
||||
$this->createContext($request, $options, $params)
|
||||
);
|
||||
}
|
||||
|
||||
private function getDefaultOptions(array $request)
|
||||
{
|
||||
$headers = "";
|
||||
foreach ($request['headers'] as $name => $value) {
|
||||
foreach ((array) $value as $val) {
|
||||
$headers .= "$name: $val\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$context = [
|
||||
'http' => [
|
||||
'method' => $request['http_method'],
|
||||
'header' => $headers,
|
||||
'protocol_version' => isset($request['version']) ? $request['version'] : 1.1,
|
||||
'ignore_errors' => true,
|
||||
'follow_location' => 0,
|
||||
],
|
||||
];
|
||||
|
||||
$body = Core::body($request);
|
||||
if (isset($body)) {
|
||||
$context['http']['content'] = $body;
|
||||
// Prevent the HTTP handler from adding a Content-Type header.
|
||||
if (!Core::hasHeader($request, 'Content-Type')) {
|
||||
$context['http']['header'] .= "Content-Type:\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
$context['http']['header'] = rtrim($context['http']['header']);
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
private function add_proxy(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if (!is_array($value)) {
|
||||
$options['http']['proxy'] = $value;
|
||||
} else {
|
||||
$scheme = isset($request['scheme']) ? $request['scheme'] : 'http';
|
||||
if (isset($value[$scheme])) {
|
||||
$options['http']['proxy'] = $value[$scheme];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add_timeout(array $request, &$options, $value, &$params)
|
||||
{
|
||||
$options['http']['timeout'] = $value;
|
||||
}
|
||||
|
||||
private function add_verify(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if ($value === true) {
|
||||
// PHP 5.6 or greater will find the system cert by default. When
|
||||
// < 5.6, use the Guzzle bundled cacert.
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
$options['ssl']['cafile'] = ClientUtils::getDefaultCaBundle();
|
||||
}
|
||||
} elseif (is_string($value)) {
|
||||
$options['ssl']['cafile'] = $value;
|
||||
if (!file_exists($value)) {
|
||||
throw new RingException("SSL CA bundle not found: $value");
|
||||
}
|
||||
} elseif ($value === false) {
|
||||
$options['ssl']['verify_peer'] = false;
|
||||
return;
|
||||
} else {
|
||||
throw new RingException('Invalid verify request option');
|
||||
}
|
||||
|
||||
$options['ssl']['verify_peer'] = true;
|
||||
$options['ssl']['allow_self_signed'] = true;
|
||||
}
|
||||
|
||||
private function add_cert(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$options['ssl']['passphrase'] = $value[1];
|
||||
$value = $value[0];
|
||||
}
|
||||
|
||||
if (!file_exists($value)) {
|
||||
throw new RingException("SSL certificate not found: {$value}");
|
||||
}
|
||||
|
||||
$options['ssl']['local_cert'] = $value;
|
||||
}
|
||||
|
||||
private function add_progress(array $request, &$options, $value, &$params)
|
||||
{
|
||||
$fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) {
|
||||
if ($code == STREAM_NOTIFY_PROGRESS) {
|
||||
$value($total, $transferred, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
// Wrap the existing function if needed.
|
||||
$params['notification'] = isset($params['notification'])
|
||||
? Core::callArray([$params['notification'], $fn])
|
||||
: $fn;
|
||||
}
|
||||
|
||||
private function add_debug(array $request, &$options, $value, &$params)
|
||||
{
|
||||
if ($value === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
static $map = [
|
||||
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
||||
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
||||
STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT',
|
||||
STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS',
|
||||
STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS',
|
||||
STREAM_NOTIFY_REDIRECTED => 'REDIRECTED',
|
||||
STREAM_NOTIFY_PROGRESS => 'PROGRESS',
|
||||
STREAM_NOTIFY_FAILURE => 'FAILURE',
|
||||
STREAM_NOTIFY_COMPLETED => 'COMPLETED',
|
||||
STREAM_NOTIFY_RESOLVE => 'RESOLVE',
|
||||
];
|
||||
|
||||
static $args = ['severity', 'message', 'message_code',
|
||||
'bytes_transferred', 'bytes_max'];
|
||||
|
||||
$value = Core::getDebugResource($value);
|
||||
$ident = $request['http_method'] . ' ' . Core::url($request);
|
||||
$fn = function () use ($ident, $value, $map, $args) {
|
||||
$passed = func_get_args();
|
||||
$code = array_shift($passed);
|
||||
fprintf($value, '<%s> [%s] ', $ident, $map[$code]);
|
||||
foreach (array_filter($passed) as $i => $v) {
|
||||
fwrite($value, $args[$i] . ': "' . $v . '" ');
|
||||
}
|
||||
fwrite($value, "\n");
|
||||
};
|
||||
|
||||
// Wrap the existing function if needed.
|
||||
$params['notification'] = isset($params['notification'])
|
||||
? Core::callArray([$params['notification'], $fn])
|
||||
: $fn;
|
||||
}
|
||||
|
||||
private function applyCustomOptions(array $request, array &$options)
|
||||
{
|
||||
if (!isset($request['client']['stream_context'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_array($request['client']['stream_context'])) {
|
||||
throw new RingException('stream_context must be an array');
|
||||
}
|
||||
|
||||
$options = array_replace_recursive(
|
||||
$options,
|
||||
$request['client']['stream_context']
|
||||
);
|
||||
}
|
||||
|
||||
private function createContext(array $request, array $options, array $params)
|
||||
{
|
||||
$this->applyCustomOptions($request, $options);
|
||||
return $this->createResource(
|
||||
function () use ($request, $options, $params) {
|
||||
return stream_context_create($options, $params);
|
||||
},
|
||||
$request,
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
private function createStreamResource(
|
||||
$url,
|
||||
array $request,
|
||||
array $options,
|
||||
$context
|
||||
) {
|
||||
return $this->createResource(
|
||||
function () use ($url, $context) {
|
||||
if (false === strpos($url, 'http')) {
|
||||
trigger_error("URL is invalid: {$url}", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
$resource = fopen($url, 'r', null, $context);
|
||||
$this->lastHeaders = $http_response_header;
|
||||
return $resource;
|
||||
},
|
||||
$request,
|
||||
$options
|
||||
);
|
||||
}
|
||||
}
|
364
core/vendor/guzzlehttp/ringphp/src/Core.php
vendored
Normal file
364
core/vendor/guzzlehttp/ringphp/src/Core.php
vendored
Normal file
|
@ -0,0 +1,364 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring;
|
||||
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
use GuzzleHttp\Ring\Future\FutureArrayInterface;
|
||||
use GuzzleHttp\Ring\Future\FutureArray;
|
||||
|
||||
/**
|
||||
* Provides core functionality of Ring handlers and middleware.
|
||||
*/
|
||||
class Core
|
||||
{
|
||||
/**
|
||||
* Returns a function that calls all of the provided functions, in order,
|
||||
* passing the arguments provided to the composed function to each function.
|
||||
*
|
||||
* @param callable[] $functions Array of functions to proxy to.
|
||||
*
|
||||
* @return callable
|
||||
*/
|
||||
public static function callArray(array $functions)
|
||||
{
|
||||
return function () use ($functions) {
|
||||
$args = func_get_args();
|
||||
foreach ($functions as $fn) {
|
||||
call_user_func_array($fn, $args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of header line values from a message for a specific header
|
||||
*
|
||||
* This method searches through the "headers" key of a message for a header
|
||||
* using a case-insensitive search.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to retrieve
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function headerLines($message, $header)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if (!empty($message['headers'])) {
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
if (!strcasecmp($name, $header)) {
|
||||
$result = array_merge($result, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a header value from a message as a string or null
|
||||
*
|
||||
* This method searches through the "headers" key of a message for a header
|
||||
* using a case-insensitive search. The lines of the header are imploded
|
||||
* using commas into a single string return value.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to retrieve
|
||||
*
|
||||
* @return string|null Returns the header string if found, or null if not.
|
||||
*/
|
||||
public static function header($message, $header)
|
||||
{
|
||||
$match = self::headerLines($message, $header);
|
||||
return $match ? implode(', ', $match) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first header value from a message as a string or null. If
|
||||
* a header line contains multiple values separated by a comma, then this
|
||||
* function will return the first value in the list.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to retrieve
|
||||
*
|
||||
* @return string|null Returns the value as a string if found.
|
||||
*/
|
||||
public static function firstHeader($message, $header)
|
||||
{
|
||||
if (!empty($message['headers'])) {
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
if (!strcasecmp($name, $header)) {
|
||||
// Return the match itself if it is a single value.
|
||||
$pos = strpos($value[0], ',');
|
||||
return $pos ? substr($value[0], 0, $pos) : $value[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a message has the provided case-insensitive header.
|
||||
*
|
||||
* @param array $message Request or response hash.
|
||||
* @param string $header Header to check
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasHeader($message, $header)
|
||||
{
|
||||
if (!empty($message['headers'])) {
|
||||
foreach ($message['headers'] as $name => $value) {
|
||||
if (!strcasecmp($name, $header)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an array of header lines into an associative array of headers.
|
||||
*
|
||||
* @param array $lines Header lines array of strings in the following
|
||||
* format: "Name: Value"
|
||||
* @return array
|
||||
*/
|
||||
public static function headersFromLines($lines)
|
||||
{
|
||||
$headers = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$parts = explode(':', $line, 2);
|
||||
$headers[trim($parts[0])][] = isset($parts[1])
|
||||
? trim($parts[1])
|
||||
: null;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a header from a message using a case-insensitive comparison.
|
||||
*
|
||||
* @param array $message Message that contains 'headers'
|
||||
* @param string $header Header to remove
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function removeHeader(array $message, $header)
|
||||
{
|
||||
if (isset($message['headers'])) {
|
||||
foreach (array_keys($message['headers']) as $key) {
|
||||
if (!strcasecmp($header, $key)) {
|
||||
unset($message['headers'][$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces any existing case insensitive headers with the given value.
|
||||
*
|
||||
* @param array $message Message that contains 'headers'
|
||||
* @param string $header Header to set.
|
||||
* @param array $value Value to set.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function setHeader(array $message, $header, array $value)
|
||||
{
|
||||
$message = self::removeHeader($message, $header);
|
||||
$message['headers'][$header] = $value;
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a URL string from a request.
|
||||
*
|
||||
* If the "url" key is present on the request, it is returned, otherwise
|
||||
* the url is built up based on the scheme, host, uri, and query_string
|
||||
* request values.
|
||||
*
|
||||
* @param array $request Request to get the URL from
|
||||
*
|
||||
* @return string Returns the request URL as a string.
|
||||
* @throws \InvalidArgumentException if no Host header is present.
|
||||
*/
|
||||
public static function url(array $request)
|
||||
{
|
||||
if (isset($request['url'])) {
|
||||
return $request['url'];
|
||||
}
|
||||
|
||||
$uri = (isset($request['scheme'])
|
||||
? $request['scheme'] : 'http') . '://';
|
||||
|
||||
if ($host = self::header($request, 'host')) {
|
||||
$uri .= $host;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('No Host header was provided');
|
||||
}
|
||||
|
||||
if (isset($request['uri'])) {
|
||||
$uri .= $request['uri'];
|
||||
}
|
||||
|
||||
if (isset($request['query_string'])) {
|
||||
$uri .= '?' . $request['query_string'];
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the body of a message into a string.
|
||||
*
|
||||
* @param array|FutureArrayInterface $message Array containing a "body" key
|
||||
*
|
||||
* @return null|string Returns the body as a string or null if not set.
|
||||
* @throws \InvalidArgumentException if a request body is invalid.
|
||||
*/
|
||||
public static function body($message)
|
||||
{
|
||||
if (!isset($message['body'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($message['body'] instanceof StreamInterface) {
|
||||
return (string) $message['body'];
|
||||
}
|
||||
|
||||
switch (gettype($message['body'])) {
|
||||
case 'string':
|
||||
return $message['body'];
|
||||
case 'resource':
|
||||
return stream_get_contents($message['body']);
|
||||
case 'object':
|
||||
if ($message['body'] instanceof \Iterator) {
|
||||
return implode('', iterator_to_array($message['body']));
|
||||
} elseif (method_exists($message['body'], '__toString')) {
|
||||
return (string) $message['body'];
|
||||
}
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid request body: '
|
||||
. self::describeType($message['body']));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the body of the provided message if possible.
|
||||
*
|
||||
* @param array $message Message that contains a 'body' field.
|
||||
*
|
||||
* @return bool Returns true on success, false on failure
|
||||
*/
|
||||
public static function rewindBody($message)
|
||||
{
|
||||
if ($message['body'] instanceof StreamInterface) {
|
||||
return $message['body']->seek(0);
|
||||
}
|
||||
|
||||
if ($message['body'] instanceof \Generator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($message['body'] instanceof \Iterator) {
|
||||
$message['body']->rewind();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_resource($message['body'])) {
|
||||
return rewind($message['body']);
|
||||
}
|
||||
|
||||
return is_string($message['body'])
|
||||
|| (is_object($message['body'])
|
||||
&& method_exists($message['body'], '__toString'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug function used to describe the provided value type and class.
|
||||
*
|
||||
* @param mixed $input
|
||||
*
|
||||
* @return string Returns a string containing the type of the variable and
|
||||
* if a class is provided, the class name.
|
||||
*/
|
||||
public static function describeType($input)
|
||||
{
|
||||
switch (gettype($input)) {
|
||||
case 'object':
|
||||
return 'object(' . get_class($input) . ')';
|
||||
case 'array':
|
||||
return 'array(' . count($input) . ')';
|
||||
default:
|
||||
ob_start();
|
||||
var_dump($input);
|
||||
// normalize float vs double
|
||||
return str_replace('double(', 'float(', rtrim(ob_get_clean()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep for the specified amount of time specified in the request's
|
||||
* ['client']['delay'] option if present.
|
||||
*
|
||||
* This function should only be used when a non-blocking sleep is not
|
||||
* possible.
|
||||
*
|
||||
* @param array $request Request to sleep
|
||||
*/
|
||||
public static function doSleep(array $request)
|
||||
{
|
||||
if (isset($request['client']['delay'])) {
|
||||
usleep($request['client']['delay'] * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxied future that modifies the dereferenced value of another
|
||||
* future using a promise.
|
||||
*
|
||||
* @param FutureArrayInterface $future Future to wrap with a new future
|
||||
* @param callable $onFulfilled Invoked when the future fulfilled
|
||||
* @param callable $onRejected Invoked when the future rejected
|
||||
* @param callable $onProgress Invoked when the future progresses
|
||||
*
|
||||
* @return FutureArray
|
||||
*/
|
||||
public static function proxy(
|
||||
FutureArrayInterface $future,
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null,
|
||||
callable $onProgress = null
|
||||
) {
|
||||
return new FutureArray(
|
||||
$future->then($onFulfilled, $onRejected, $onProgress),
|
||||
[$future, 'wait'],
|
||||
[$future, 'cancel']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a debug stream based on the provided variable.
|
||||
*
|
||||
* @param mixed $value Optional value
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
public static function getDebugResource($value = null)
|
||||
{
|
||||
if (is_resource($value)) {
|
||||
return $value;
|
||||
} elseif (defined('STDOUT')) {
|
||||
return STDOUT;
|
||||
} else {
|
||||
return fopen('php://output', 'w');
|
||||
}
|
||||
}
|
||||
}
|
7
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php
vendored
Normal file
7
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledException.php
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
/**
|
||||
* Marker interface for cancelled exceptions.
|
||||
*/
|
||||
interface CancelledException {}
|
4
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php
vendored
Normal file
4
core/vendor/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
class CancelledFutureAccessException extends RingException implements CancelledException {}
|
7
core/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php
vendored
Normal file
7
core/vendor/guzzlehttp/ringphp/src/Exception/ConnectException.php
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
/**
|
||||
* Occurs when the connection failed.
|
||||
*/
|
||||
class ConnectException extends RingException {}
|
4
core/vendor/guzzlehttp/ringphp/src/Exception/RingException.php
vendored
Normal file
4
core/vendor/guzzlehttp/ringphp/src/Exception/RingException.php
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Exception;
|
||||
|
||||
class RingException extends \RuntimeException {};
|
125
core/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php
vendored
Normal file
125
core/vendor/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
use GuzzleHttp\Ring\Exception\CancelledFutureAccessException;
|
||||
use GuzzleHttp\Ring\Exception\RingException;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Implements common future functionality built on top of promises.
|
||||
*/
|
||||
trait BaseFutureTrait
|
||||
{
|
||||
/** @var callable */
|
||||
private $waitfn;
|
||||
|
||||
/** @var callable */
|
||||
private $cancelfn;
|
||||
|
||||
/** @var PromiseInterface */
|
||||
private $wrappedPromise;
|
||||
|
||||
/** @var \Exception Error encountered. */
|
||||
private $error;
|
||||
|
||||
/** @var mixed Result of the future */
|
||||
private $result;
|
||||
|
||||
private $isRealized = false;
|
||||
|
||||
/**
|
||||
* @param PromiseInterface $promise Promise to shadow with the future.
|
||||
* @param callable $wait Function that blocks until the deferred
|
||||
* computation has been resolved. This
|
||||
* function MUST resolve the deferred value
|
||||
* associated with the supplied promise.
|
||||
* @param callable $cancel If possible and reasonable, provide a
|
||||
* function that can be used to cancel the
|
||||
* future from completing.
|
||||
*/
|
||||
public function __construct(
|
||||
PromiseInterface $promise,
|
||||
callable $wait = null,
|
||||
callable $cancel = null
|
||||
) {
|
||||
$this->wrappedPromise = $promise;
|
||||
$this->waitfn = $wait;
|
||||
$this->cancelfn = $cancel;
|
||||
}
|
||||
|
||||
public function wait()
|
||||
{
|
||||
if (!$this->isRealized) {
|
||||
$this->addShadow();
|
||||
if (!$this->isRealized && $this->waitfn) {
|
||||
$this->invokeWait();
|
||||
}
|
||||
if (!$this->isRealized) {
|
||||
$this->error = new RingException('Waiting did not resolve future');
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->error) {
|
||||
throw $this->error;
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function promise()
|
||||
{
|
||||
return $this->wrappedPromise;
|
||||
}
|
||||
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null,
|
||||
callable $onProgress = null
|
||||
) {
|
||||
return $this->wrappedPromise->then($onFulfilled, $onRejected, $onProgress);
|
||||
}
|
||||
|
||||
public function cancel()
|
||||
{
|
||||
if (!$this->isRealized) {
|
||||
$cancelfn = $this->cancelfn;
|
||||
$this->waitfn = $this->cancelfn = null;
|
||||
$this->isRealized = true;
|
||||
$this->error = new CancelledFutureAccessException();
|
||||
if ($cancelfn) {
|
||||
$cancelfn($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function addShadow()
|
||||
{
|
||||
// Get the result and error when the promise is resolved. Note that
|
||||
// calling this function might trigger the resolution immediately.
|
||||
$this->wrappedPromise->then(
|
||||
function ($value) {
|
||||
$this->isRealized = true;
|
||||
$this->result = $value;
|
||||
$this->waitfn = $this->cancelfn = null;
|
||||
},
|
||||
function ($error) {
|
||||
$this->isRealized = true;
|
||||
$this->error = $error;
|
||||
$this->waitfn = $this->cancelfn = null;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function invokeWait()
|
||||
{
|
||||
try {
|
||||
$wait = $this->waitfn;
|
||||
$this->waitfn = null;
|
||||
$wait();
|
||||
} catch (\Exception $e) {
|
||||
// Defer can throw to reject.
|
||||
$this->error = $e;
|
||||
$this->isRealized = true;
|
||||
}
|
||||
}
|
||||
}
|
43
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php
vendored
Normal file
43
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Represents a future array that has been completed successfully.
|
||||
*/
|
||||
class CompletedFutureArray extends CompletedFutureValue implements FutureArrayInterface
|
||||
{
|
||||
public function __construct(array $result)
|
||||
{
|
||||
parent::__construct($result);
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->result[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->result[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->result[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->result[$offset]);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->result);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->result);
|
||||
}
|
||||
}
|
57
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php
vendored
Normal file
57
core/vendor/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
use React\Promise\FulfilledPromise;
|
||||
use React\Promise\RejectedPromise;
|
||||
|
||||
/**
|
||||
* Represents a future value that has been resolved or rejected.
|
||||
*/
|
||||
class CompletedFutureValue implements FutureInterface
|
||||
{
|
||||
protected $result;
|
||||
protected $error;
|
||||
|
||||
private $cachedPromise;
|
||||
|
||||
/**
|
||||
* @param mixed $result Resolved result
|
||||
* @param \Exception $e Error. Pass a GuzzleHttp\Ring\Exception\CancelledFutureAccessException
|
||||
* to mark the future as cancelled.
|
||||
*/
|
||||
public function __construct($result, \Exception $e = null)
|
||||
{
|
||||
$this->result = $result;
|
||||
$this->error = $e;
|
||||
}
|
||||
|
||||
public function wait()
|
||||
{
|
||||
if ($this->error) {
|
||||
throw $this->error;
|
||||
}
|
||||
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function cancel() {}
|
||||
|
||||
public function promise()
|
||||
{
|
||||
if (!$this->cachedPromise) {
|
||||
$this->cachedPromise = $this->error
|
||||
? new RejectedPromise($this->error)
|
||||
: new FulfilledPromise($this->result);
|
||||
}
|
||||
|
||||
return $this->cachedPromise;
|
||||
}
|
||||
|
||||
public function then(
|
||||
callable $onFulfilled = null,
|
||||
callable $onRejected = null,
|
||||
callable $onProgress = null
|
||||
) {
|
||||
return $this->promise()->then($onFulfilled, $onRejected, $onProgress);
|
||||
}
|
||||
}
|
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php
vendored
Normal file
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureArray.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Represents a future array value that when dereferenced returns an array.
|
||||
*/
|
||||
class FutureArray implements FutureArrayInterface
|
||||
{
|
||||
use MagicFutureTrait;
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->_value[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->_value[$offset];
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
$this->_value[$offset] = $value;
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
unset($this->_value[$offset]);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->_value);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->_value);
|
||||
}
|
||||
}
|
11
core/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php
vendored
Normal file
11
core/vendor/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Future that provides array-like access.
|
||||
*/
|
||||
interface FutureArrayInterface extends
|
||||
FutureInterface,
|
||||
\ArrayAccess,
|
||||
\Countable,
|
||||
\IteratorAggregate {};
|
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php
vendored
Normal file
40
core/vendor/guzzlehttp/ringphp/src/Future/FutureInterface.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Promise\PromisorInterface;
|
||||
|
||||
/**
|
||||
* Represents the result of a computation that may not have completed yet.
|
||||
*
|
||||
* You can use the future in a blocking manner using the wait() function, or
|
||||
* you can use a promise from the future to receive the result when the future
|
||||
* has been resolved.
|
||||
*
|
||||
* When the future is dereferenced using wait(), the result of the computation
|
||||
* is cached and returned for subsequent calls to wait(). If the result of the
|
||||
* computation has not yet completed when wait() is called, the call to wait()
|
||||
* will block until the future has completed.
|
||||
*/
|
||||
interface FutureInterface extends PromiseInterface, PromisorInterface
|
||||
{
|
||||
/**
|
||||
* Returns the result of the future either from cache or by blocking until
|
||||
* it is complete.
|
||||
*
|
||||
* This method must block until the future has a result or is cancelled.
|
||||
* Throwing an exception in the wait() method will mark the future as
|
||||
* realized and will throw the exception each time wait() is called.
|
||||
* Throwing an instance of GuzzleHttp\Ring\CancelledException will mark
|
||||
* the future as realized, will not throw immediately, but will throw the
|
||||
* exception if the future's wait() method is called again.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function wait();
|
||||
|
||||
/**
|
||||
* Cancels the future, if possible.
|
||||
*/
|
||||
public function cancel();
|
||||
}
|
12
core/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php
vendored
Normal file
12
core/vendor/guzzlehttp/ringphp/src/Future/FutureValue.php
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Represents a future value that responds to wait() to retrieve the promised
|
||||
* value, but can also return promises that are delivered the value when it is
|
||||
* available.
|
||||
*/
|
||||
class FutureValue implements FutureInterface
|
||||
{
|
||||
use BaseFutureTrait;
|
||||
}
|
32
core/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php
vendored
Normal file
32
core/vendor/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Ring\Future;
|
||||
|
||||
/**
|
||||
* Implements common future functionality that is triggered when the result
|
||||
* property is accessed via a magic __get method.
|
||||
*
|
||||
* @property mixed $_value Actual data used by the future. Accessing this
|
||||
* property will cause the future to block if needed.
|
||||
*/
|
||||
trait MagicFutureTrait
|
||||
{
|
||||
use BaseFutureTrait;
|
||||
|
||||
/**
|
||||
* This function handles retrieving the dereferenced result when requested.
|
||||
*
|
||||
* @param string $name Should always be "data" or an exception is thrown.
|
||||
*
|
||||
* @return mixed Returns the dereferenced data.
|
||||
* @throws \RuntimeException
|
||||
* @throws \GuzzleHttp\Ring\Exception\CancelledException
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if ($name !== '_value') {
|
||||
throw new \RuntimeException("Class has no {$name} property");
|
||||
}
|
||||
|
||||
return $this->_value = $this->wait();
|
||||
}
|
||||
}
|
792
core/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php
vendored
Normal file
792
core/vendor/guzzlehttp/ringphp/tests/Client/CurlFactoryTest.php
vendored
Normal file
|
@ -0,0 +1,792 @@
|
|||
<?php
|
||||
// Override curl_setopt_array() to get the last set curl options
|
||||
namespace GuzzleHttp\Ring\Client {
|
||||
function curl_setopt_array($handle, array $options) {
|
||||
if (!empty($_SERVER['curl_test'])) {
|
||||
$_SERVER['_curl'] = $options;
|
||||
} else {
|
||||
unset($_SERVER['_curl']);
|
||||
}
|
||||
\curl_setopt_array($handle, $options);
|
||||
}
|
||||
}
|
||||
|
||||
namespace GuzzleHttp\Tests\Ring\Client {
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlFactory;
|
||||
use GuzzleHttp\Ring\Client\CurlMultiHandler;
|
||||
use GuzzleHttp\Ring\Client\MockHandler;
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Stream\FnStream;
|
||||
use GuzzleHttp\Stream\NoSeekStream;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
|
||||
class CurlFactoryTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
$_SERVER['curl_test'] = true;
|
||||
unset($_SERVER['_curl']);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass()
|
||||
{
|
||||
unset($_SERVER['_curl'], $_SERVER['curl_test']);
|
||||
}
|
||||
|
||||
public function testCreatesCurlHandle()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([[
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Foo' => ['Bar'],
|
||||
'Baz' => ['bam'],
|
||||
'Content-Length' => [2],
|
||||
],
|
||||
'body' => 'hi',
|
||||
]]);
|
||||
|
||||
$stream = Stream::factory();
|
||||
|
||||
$request = [
|
||||
'http_method' => 'PUT',
|
||||
'headers' => [
|
||||
'host' => [Server::$url],
|
||||
'Hi' => [' 123'],
|
||||
],
|
||||
'body' => 'testing',
|
||||
'client' => ['save_to' => $stream],
|
||||
];
|
||||
|
||||
$f = new CurlFactory();
|
||||
$result = $f($request);
|
||||
$this->assertInternalType('array', $result);
|
||||
$this->assertCount(3, $result);
|
||||
$this->assertInternalType('resource', $result[0]);
|
||||
$this->assertInternalType('array', $result[1]);
|
||||
$this->assertSame($stream, $result[2]);
|
||||
curl_close($result[0]);
|
||||
|
||||
$this->assertEquals('PUT', $_SERVER['_curl'][CURLOPT_CUSTOMREQUEST]);
|
||||
$this->assertEquals(
|
||||
'http://http://127.0.0.1:8125/',
|
||||
$_SERVER['_curl'][CURLOPT_URL]
|
||||
);
|
||||
// Sends via post fields when the request is small enough
|
||||
$this->assertEquals('testing', $_SERVER['_curl'][CURLOPT_POSTFIELDS]);
|
||||
$this->assertEquals(0, $_SERVER['_curl'][CURLOPT_RETURNTRANSFER]);
|
||||
$this->assertEquals(0, $_SERVER['_curl'][CURLOPT_HEADER]);
|
||||
$this->assertEquals(150, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT]);
|
||||
$this->assertInstanceOf('Closure', $_SERVER['_curl'][CURLOPT_HEADERFUNCTION]);
|
||||
|
||||
if (defined('CURLOPT_PROTOCOLS')) {
|
||||
$this->assertEquals(
|
||||
CURLPROTO_HTTP | CURLPROTO_HTTPS,
|
||||
$_SERVER['_curl'][CURLOPT_PROTOCOLS]
|
||||
);
|
||||
}
|
||||
|
||||
$this->assertContains('Expect:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
|
||||
$this->assertContains('Accept:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
|
||||
$this->assertContains('Content-Type:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
|
||||
$this->assertContains('Hi: 123', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
|
||||
$this->assertContains('host: http://127.0.0.1:8125/', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
|
||||
}
|
||||
|
||||
public function testSendsHeadRequests()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'HEAD',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
]);
|
||||
$response->wait();
|
||||
$this->assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]);
|
||||
$checks = [CURLOPT_WRITEFUNCTION, CURLOPT_READFUNCTION, CURLOPT_FILE, CURLOPT_INFILE];
|
||||
foreach ($checks as $check) {
|
||||
$this->assertArrayNotHasKey($check, $_SERVER['_curl']);
|
||||
}
|
||||
$this->assertEquals('HEAD', Server::received()[0]['http_method']);
|
||||
}
|
||||
|
||||
public function testCanAddCustomCurlOptions()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]],
|
||||
]);
|
||||
$this->assertEquals(10, $_SERVER['_curl'][CURLOPT_LOW_SPEED_LIMIT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage SSL CA bundle not found: /does/not/exist
|
||||
*/
|
||||
public function testValidatesVerify()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['verify' => '/does/not/exist'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testCanSetVerifyToFile()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['verify' => __FILE__],
|
||||
]);
|
||||
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_CAINFO]);
|
||||
$this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]);
|
||||
$this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]);
|
||||
}
|
||||
|
||||
public function testAddsVerifyAsTrue()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['verify' => true],
|
||||
]);
|
||||
$this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]);
|
||||
$this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]);
|
||||
$this->assertArrayNotHasKey(CURLOPT_CAINFO, $_SERVER['_curl']);
|
||||
}
|
||||
|
||||
public function testCanDisableVerify()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['verify' => false],
|
||||
]);
|
||||
$this->assertEquals(0, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]);
|
||||
$this->assertEquals(false, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]);
|
||||
}
|
||||
|
||||
public function testAddsProxy()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['proxy' => 'http://bar.com'],
|
||||
]);
|
||||
$this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]);
|
||||
}
|
||||
|
||||
public function testAddsViaScheme()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'scheme' => 'http',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => [
|
||||
'proxy' => ['http' => 'http://bar.com', 'https' => 'https://t'],
|
||||
],
|
||||
]);
|
||||
$this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage SSL private key not found: /does/not/exist
|
||||
*/
|
||||
public function testValidatesSslKey()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['ssl_key' => '/does/not/exist'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAddsSslKey()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['ssl_key' => __FILE__],
|
||||
]);
|
||||
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]);
|
||||
}
|
||||
|
||||
public function testAddsSslKeyWithPassword()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['ssl_key' => [__FILE__, 'test']],
|
||||
]);
|
||||
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]);
|
||||
$this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLKEYPASSWD]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage SSL certificate not found: /does/not/exist
|
||||
*/
|
||||
public function testValidatesCert()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['cert' => '/does/not/exist'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAddsCert()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['cert' => __FILE__],
|
||||
]);
|
||||
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]);
|
||||
}
|
||||
|
||||
public function testAddsCertWithPassword()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['cert' => [__FILE__, 'test']],
|
||||
]);
|
||||
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]);
|
||||
$this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLCERTPASSWD]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage progress client option must be callable
|
||||
*/
|
||||
public function testValidatesProgress()
|
||||
{
|
||||
$f = new CurlFactory();
|
||||
$f([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'client' => ['progress' => 'foo'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testEmitsDebugInfoToStream()
|
||||
{
|
||||
$res = fopen('php://memory', 'r+');
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'HEAD',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['debug' => $res],
|
||||
]);
|
||||
$response->wait();
|
||||
rewind($res);
|
||||
$output = str_replace("\r", '', stream_get_contents($res));
|
||||
$this->assertContains(
|
||||
"> HEAD / HTTP/1.1\nhost: 127.0.0.1:8125\n\n",
|
||||
$output
|
||||
);
|
||||
$this->assertContains("< HTTP/1.1 200", $output);
|
||||
fclose($res);
|
||||
}
|
||||
|
||||
public function testEmitsProgressToFunction()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$called = [];
|
||||
$response = $a([
|
||||
'http_method' => 'HEAD',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => [
|
||||
'progress' => function () use (&$called) {
|
||||
$called[] = func_get_args();
|
||||
},
|
||||
],
|
||||
]);
|
||||
$response->wait();
|
||||
$this->assertNotEmpty($called);
|
||||
foreach ($called as $call) {
|
||||
$this->assertCount(4, $call);
|
||||
}
|
||||
}
|
||||
|
||||
private function addDecodeResponse($withEncoding = true)
|
||||
{
|
||||
$content = gzencode('test');
|
||||
$response = [
|
||||
'status' => 200,
|
||||
'reason' => 'OK',
|
||||
'headers' => ['Content-Length' => [strlen($content)]],
|
||||
'body' => $content,
|
||||
];
|
||||
|
||||
if ($withEncoding) {
|
||||
$response['headers']['Content-Encoding'] = ['gzip'];
|
||||
}
|
||||
|
||||
Server::flush();
|
||||
Server::enqueue([$response]);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function testDecodesGzippedResponses()
|
||||
{
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['decode_content' => true],
|
||||
]);
|
||||
$response->wait();
|
||||
$this->assertEquals('test', Core::body($response));
|
||||
$this->assertEquals('', $_SERVER['_curl'][CURLOPT_ENCODING]);
|
||||
$sent = Server::received()[0];
|
||||
$this->assertNull(Core::header($sent, 'Accept-Encoding'));
|
||||
}
|
||||
|
||||
public function testDecodesGzippedResponsesWithHeader()
|
||||
{
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => [
|
||||
'host' => [Server::$host],
|
||||
'Accept-Encoding' => ['gzip'],
|
||||
],
|
||||
'client' => ['decode_content' => true],
|
||||
]);
|
||||
$response->wait();
|
||||
$this->assertEquals('gzip', $_SERVER['_curl'][CURLOPT_ENCODING]);
|
||||
$sent = Server::received()[0];
|
||||
$this->assertEquals('gzip', Core::header($sent, 'Accept-Encoding'));
|
||||
$this->assertEquals('test', Core::body($response));
|
||||
}
|
||||
|
||||
public function testDoesNotForceDecode()
|
||||
{
|
||||
$content = $this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['decode_content' => false],
|
||||
]);
|
||||
$response->wait();
|
||||
$sent = Server::received()[0];
|
||||
$this->assertNull(Core::header($sent, 'Accept-Encoding'));
|
||||
$this->assertEquals($content, Core::body($response));
|
||||
}
|
||||
|
||||
public function testProtocolVersion()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'version' => 1.0,
|
||||
]);
|
||||
$this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testValidatesSaveTo()
|
||||
{
|
||||
$handler = new CurlMultiHandler();
|
||||
$handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['save_to' => true],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSavesToStream()
|
||||
{
|
||||
$stream = fopen('php://memory', 'r+');
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => [
|
||||
'decode_content' => true,
|
||||
'save_to' => $stream,
|
||||
],
|
||||
]);
|
||||
$response->wait();
|
||||
rewind($stream);
|
||||
$this->assertEquals('test', stream_get_contents($stream));
|
||||
}
|
||||
|
||||
public function testSavesToGuzzleStream()
|
||||
{
|
||||
$stream = Stream::factory();
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => [
|
||||
'decode_content' => true,
|
||||
'save_to' => $stream,
|
||||
],
|
||||
]);
|
||||
$response->wait();
|
||||
$this->assertEquals('test', (string) $stream);
|
||||
}
|
||||
|
||||
public function testSavesToFileOnDisk()
|
||||
{
|
||||
$tmpfile = tempnam(sys_get_temp_dir(), 'testfile');
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => [
|
||||
'decode_content' => true,
|
||||
'save_to' => $tmpfile,
|
||||
],
|
||||
]);
|
||||
$response->wait();
|
||||
$this->assertEquals('test', file_get_contents($tmpfile));
|
||||
unlink($tmpfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testValidatesBody()
|
||||
{
|
||||
$handler = new CurlMultiHandler();
|
||||
$handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'body' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAddsLargePayloadFromStreamWithNoSizeUsingChunked()
|
||||
{
|
||||
$stream = Stream::factory('foo');
|
||||
$stream = FnStream::decorate($stream, [
|
||||
'getSize' => function () {
|
||||
return null;
|
||||
}
|
||||
]);
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'body' => $stream,
|
||||
]);
|
||||
$response->wait();
|
||||
$sent = Server::received()[0];
|
||||
$this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding'));
|
||||
$this->assertNull(Core::header($sent, 'Content-Length'));
|
||||
$this->assertEquals('foo', $sent['body']);
|
||||
}
|
||||
|
||||
public function testAddsPayloadFromIterator()
|
||||
{
|
||||
$iter = new \ArrayIterator(['f', 'o', 'o']);
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'body' => $iter,
|
||||
]);
|
||||
$response->wait();
|
||||
$sent = Server::received()[0];
|
||||
$this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding'));
|
||||
$this->assertNull(Core::header($sent, 'Content-Length'));
|
||||
$this->assertEquals('foo', $sent['body']);
|
||||
}
|
||||
|
||||
public function testAddsPayloadFromResource()
|
||||
{
|
||||
$res = fopen('php://memory', 'r+');
|
||||
$data = str_repeat('.', 1000000);
|
||||
fwrite($res, $data);
|
||||
rewind($res);
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => [
|
||||
'host' => [Server::$host],
|
||||
'content-length' => [1000000],
|
||||
],
|
||||
'body' => $res,
|
||||
]);
|
||||
$response->wait();
|
||||
$sent = Server::received()[0];
|
||||
$this->assertNull(Core::header($sent, 'Transfer-Encoding'));
|
||||
$this->assertEquals(1000000, Core::header($sent, 'Content-Length'));
|
||||
$this->assertEquals($data, $sent['body']);
|
||||
}
|
||||
|
||||
public function testAddsContentLengthFromStream()
|
||||
{
|
||||
$stream = Stream::factory('foo');
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'body' => $stream,
|
||||
]);
|
||||
$response->wait();
|
||||
$sent = Server::received()[0];
|
||||
$this->assertEquals(3, Core::header($sent, 'Content-Length'));
|
||||
$this->assertNull(Core::header($sent, 'Transfer-Encoding'));
|
||||
$this->assertEquals('foo', $sent['body']);
|
||||
}
|
||||
|
||||
public function testDoesNotAddMultipleContentLengthHeaders()
|
||||
{
|
||||
$this->addDecodeResponse();
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => [
|
||||
'host' => [Server::$host],
|
||||
'content-length' => [3],
|
||||
],
|
||||
'body' => 'foo',
|
||||
]);
|
||||
$response->wait();
|
||||
$sent = Server::received()[0];
|
||||
$this->assertEquals(3, Core::header($sent, 'Content-Length'));
|
||||
$this->assertNull(Core::header($sent, 'Transfer-Encoding'));
|
||||
$this->assertEquals('foo', $sent['body']);
|
||||
}
|
||||
|
||||
public function testSendsPostWithNoBodyOrDefaultContentType()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'POST',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
]);
|
||||
$response->wait();
|
||||
$received = Server::received()[0];
|
||||
$this->assertEquals('POST', $received['http_method']);
|
||||
$this->assertNull(Core::header($received, 'content-type'));
|
||||
$this->assertSame('0', Core::firstHeader($received, 'content-length'));
|
||||
}
|
||||
|
||||
public function testFailsWhenNoResponseAndNoBody()
|
||||
{
|
||||
$res = CurlFactory::createResponse(function () {}, [], [], [], null);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']);
|
||||
$this->assertContains(
|
||||
'No response was received for a request with no body',
|
||||
$res['error']->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testFailsWhenCannotRewindRetry()
|
||||
{
|
||||
$res = CurlFactory::createResponse(function () {}, [
|
||||
'body' => new NoSeekStream(Stream::factory('foo'))
|
||||
], [], [], null);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']);
|
||||
$this->assertContains(
|
||||
'rewind the request body failed',
|
||||
$res['error']->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testRetriesWhenBodyCanBeRewound()
|
||||
{
|
||||
$callHandler = $called = false;
|
||||
$res = CurlFactory::createResponse(function () use (&$callHandler) {
|
||||
$callHandler = true;
|
||||
return ['status' => 200];
|
||||
}, [
|
||||
'body' => FnStream::decorate(Stream::factory('test'), [
|
||||
'seek' => function () use (&$called) {
|
||||
$called = true;
|
||||
return true;
|
||||
}
|
||||
])
|
||||
], [], [], null);
|
||||
|
||||
$this->assertTrue($callHandler);
|
||||
$this->assertTrue($called);
|
||||
$this->assertEquals('200', $res['status']);
|
||||
}
|
||||
|
||||
public function testFailsWhenRetryMoreThanThreeTimes()
|
||||
{
|
||||
$call = 0;
|
||||
$mock = new MockHandler(function (array $request) use (&$mock, &$call) {
|
||||
$call++;
|
||||
return CurlFactory::createResponse($mock, $request, [], [], null);
|
||||
});
|
||||
$response = $mock([
|
||||
'http_method' => 'GET',
|
||||
'body' => 'test',
|
||||
]);
|
||||
$this->assertEquals(3, $call);
|
||||
$this->assertArrayHasKey('error', $response);
|
||||
$this->assertContains(
|
||||
'The cURL request was retried 3 times',
|
||||
$response['error']->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testHandles100Continue()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([
|
||||
[
|
||||
'status' => '200',
|
||||
'reason' => 'OK',
|
||||
'headers' => [
|
||||
'Test' => ['Hello'],
|
||||
'Content-Length' => ['4'],
|
||||
],
|
||||
'body' => 'test',
|
||||
],
|
||||
]);
|
||||
|
||||
$request = [
|
||||
'http_method' => 'PUT',
|
||||
'headers' => [
|
||||
'Host' => [Server::$host],
|
||||
'Expect' => ['100-Continue'],
|
||||
],
|
||||
'body' => 'test',
|
||||
];
|
||||
|
||||
$handler = new CurlMultiHandler();
|
||||
$response = $handler($request)->wait();
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$this->assertEquals('OK', $response['reason']);
|
||||
$this->assertEquals(['Hello'], $response['headers']['Test']);
|
||||
$this->assertEquals(['4'], $response['headers']['Content-Length']);
|
||||
$this->assertEquals('test', Core::body($response));
|
||||
}
|
||||
|
||||
public function testCreatesConnectException()
|
||||
{
|
||||
$m = new \ReflectionMethod('GuzzleHttp\Ring\Client\CurlFactory', 'createErrorResponse');
|
||||
$m->setAccessible(true);
|
||||
$response = $m->invoke(
|
||||
null,
|
||||
function () {},
|
||||
[],
|
||||
[
|
||||
'err_message' => 'foo',
|
||||
'curl' => [
|
||||
'errno' => CURLE_COULDNT_CONNECT,
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\ConnectException', $response['error']);
|
||||
}
|
||||
|
||||
public function testParsesLastResponseOnly()
|
||||
{
|
||||
$response1 = [
|
||||
'status' => 301,
|
||||
'headers' => [
|
||||
'Content-Length' => ['0'],
|
||||
'Location' => ['/foo']
|
||||
]
|
||||
];
|
||||
|
||||
$response2 = [
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Content-Length' => ['0'],
|
||||
'Foo' => ['bar']
|
||||
]
|
||||
];
|
||||
|
||||
Server::flush();
|
||||
Server::enqueue([$response1, $response2]);
|
||||
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => [Server::$host]],
|
||||
'client' => [
|
||||
'curl' => [
|
||||
CURLOPT_FOLLOWLOCATION => true
|
||||
]
|
||||
]
|
||||
])->wait();
|
||||
|
||||
$this->assertEquals(1, $response['transfer_stats']['redirect_count']);
|
||||
$this->assertEquals('http://127.0.0.1:8125/foo', $response['effective_url']);
|
||||
$this->assertEquals(['bar'], $response['headers']['Foo']);
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$this->assertFalse(Core::hasHeader($response, 'Location'));
|
||||
}
|
||||
|
||||
public function testMaintainsMultiHeaderOrder()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([
|
||||
[
|
||||
'status' => 200,
|
||||
'headers' => [
|
||||
'Content-Length' => ['0'],
|
||||
'Foo' => ['a', 'b'],
|
||||
'foo' => ['c', 'd'],
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['Host' => [Server::$host]]
|
||||
])->wait();
|
||||
|
||||
$this->assertEquals(
|
||||
['a', 'b', 'c', 'd'],
|
||||
Core::headerLines($response, 'Foo')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
96
core/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php
vendored
Normal file
96
core/vendor/guzzlehttp/ringphp/tests/Client/CurlHandlerTest.php
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlHandler;
|
||||
|
||||
class CurlHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
if (!function_exists('curl_reset')) {
|
||||
$this->markTestSkipped('curl_reset() is not available');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getHandler($factory = null, $options = [])
|
||||
{
|
||||
return new CurlHandler($options);
|
||||
}
|
||||
|
||||
public function testCanSetMaxHandles()
|
||||
{
|
||||
$a = new CurlHandler(['max_handles' => 10]);
|
||||
$this->assertEquals(10, $this->readAttribute($a, 'maxHandles'));
|
||||
}
|
||||
|
||||
public function testCreatesCurlErrors()
|
||||
{
|
||||
$handler = new CurlHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => ['localhost:123']],
|
||||
'client' => ['timeout' => 0.001, 'connect_timeout' => 0.001],
|
||||
]);
|
||||
$this->assertNull($response['status']);
|
||||
$this->assertNull($response['reason']);
|
||||
$this->assertEquals([], $response['headers']);
|
||||
$this->assertInstanceOf(
|
||||
'GuzzleHttp\Ring\Exception\RingException',
|
||||
$response['error']
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
1,
|
||||
preg_match('/^cURL error \d+: .*$/', $response['error']->getMessage())
|
||||
);
|
||||
}
|
||||
|
||||
public function testReleasesAdditionalEasyHandles()
|
||||
{
|
||||
Server::flush();
|
||||
$response = [
|
||||
'status' => 200,
|
||||
'headers' => ['Content-Length' => [4]],
|
||||
'body' => 'test',
|
||||
];
|
||||
|
||||
Server::enqueue([$response, $response, $response, $response]);
|
||||
$a = new CurlHandler(['max_handles' => 2]);
|
||||
|
||||
$fn = function () use (&$calls, $a, &$fn) {
|
||||
if (++$calls < 4) {
|
||||
$a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['progress' => $fn],
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => [
|
||||
'progress' => $fn,
|
||||
],
|
||||
];
|
||||
|
||||
$a($request);
|
||||
$this->assertCount(2, $this->readAttribute($a, 'handles'));
|
||||
}
|
||||
|
||||
public function testReusesHandles()
|
||||
{
|
||||
Server::flush();
|
||||
$response = ['status' => 200];
|
||||
Server::enqueue([$response, $response]);
|
||||
$a = new CurlHandler();
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
];
|
||||
$a($request);
|
||||
$a($request);
|
||||
}
|
||||
}
|
165
core/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php
vendored
Normal file
165
core/vendor/guzzlehttp/ringphp/tests/Client/CurlMultiHandlerTest.php
vendored
Normal file
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Client\CurlMultiHandler;
|
||||
|
||||
class CurlMultiHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testSendsRequest()
|
||||
{
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
]);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$this->assertArrayHasKey('transfer_stats', $response);
|
||||
$realUrl = trim($response['transfer_stats']['url'], '/');
|
||||
$this->assertEquals(trim(Server::$url, '/'), $realUrl);
|
||||
$this->assertArrayHasKey('effective_url', $response);
|
||||
$this->assertEquals(
|
||||
trim(Server::$url, '/'),
|
||||
trim($response['effective_url'], '/')
|
||||
);
|
||||
}
|
||||
|
||||
public function testCreatesErrorResponses()
|
||||
{
|
||||
$url = 'http://localhost:123/';
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['localhost:123']],
|
||||
]);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
|
||||
$this->assertNull($response['status']);
|
||||
$this->assertNull($response['reason']);
|
||||
$this->assertEquals([], $response['headers']);
|
||||
$this->assertArrayHasKey('error', $response);
|
||||
$this->assertContains('cURL error ', $response['error']->getMessage());
|
||||
$this->assertArrayHasKey('transfer_stats', $response);
|
||||
$this->assertEquals(
|
||||
trim($url, '/'),
|
||||
trim($response['transfer_stats']['url'], '/')
|
||||
);
|
||||
$this->assertArrayHasKey('effective_url', $response);
|
||||
$this->assertEquals(
|
||||
trim($url, '/'),
|
||||
trim($response['effective_url'], '/')
|
||||
);
|
||||
}
|
||||
|
||||
public function testSendsFuturesWhenDestructed()
|
||||
{
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
]);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
|
||||
$a->__destruct();
|
||||
$this->assertEquals(200, $response['status']);
|
||||
}
|
||||
|
||||
public function testCanSetMaxHandles()
|
||||
{
|
||||
$a = new CurlMultiHandler(['max_handles' => 2]);
|
||||
$this->assertEquals(2, $this->readAttribute($a, 'maxHandles'));
|
||||
}
|
||||
|
||||
public function testCanSetSelectTimeout()
|
||||
{
|
||||
$a = new CurlMultiHandler(['select_timeout' => 2]);
|
||||
$this->assertEquals(2, $this->readAttribute($a, 'selectTimeout'));
|
||||
}
|
||||
|
||||
public function testSendsFuturesWhenMaxHandlesIsReached()
|
||||
{
|
||||
$request = [
|
||||
'http_method' => 'PUT',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'future' => 'lazy', // passing this to control the test
|
||||
];
|
||||
$response = ['status' => 200];
|
||||
Server::flush();
|
||||
Server::enqueue([$response, $response, $response]);
|
||||
$a = new CurlMultiHandler(['max_handles' => 3]);
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$responses[] = $a($request);
|
||||
}
|
||||
$this->assertCount(3, Server::received());
|
||||
$responses[3]->cancel();
|
||||
$responses[4]->cancel();
|
||||
}
|
||||
|
||||
public function testCanCancel()
|
||||
{
|
||||
Server::flush();
|
||||
$response = ['status' => 200];
|
||||
Server::enqueue(array_fill_keys(range(0, 10), $response));
|
||||
$a = new CurlMultiHandler();
|
||||
$responses = [];
|
||||
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'future' => 'lazy',
|
||||
]);
|
||||
$response->cancel();
|
||||
$responses[] = $response;
|
||||
}
|
||||
|
||||
$this->assertCount(0, Server::received());
|
||||
|
||||
foreach ($responses as $response) {
|
||||
$this->assertTrue($this->readAttribute($response, 'isRealized'));
|
||||
}
|
||||
}
|
||||
|
||||
public function testCannotCancelFinished()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
]);
|
||||
$response->wait();
|
||||
$response->cancel();
|
||||
}
|
||||
|
||||
public function testDelaysInParallel()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 200]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$expected = microtime(true) + (100 / 1000);
|
||||
$response = $a([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['delay' => 100],
|
||||
]);
|
||||
$response->wait();
|
||||
$this->assertGreaterThanOrEqual($expected, microtime(true));
|
||||
}
|
||||
|
||||
public function testSendsNonLazyFutures()
|
||||
{
|
||||
$request = [
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'future' => true,
|
||||
];
|
||||
Server::flush();
|
||||
Server::enqueue([['status' => 202]]);
|
||||
$a = new CurlMultiHandler();
|
||||
$response = $a($request);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
|
||||
$this->assertEquals(202, $response['status']);
|
||||
}
|
||||
}
|
65
core/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php
vendored
Normal file
65
core/vendor/guzzlehttp/ringphp/tests/Client/MiddlewareTest.php
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Client\Middleware;
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
|
||||
class MiddlewareTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testFutureCallsDefaultHandler()
|
||||
{
|
||||
$future = new CompletedFutureArray(['status' => 200]);
|
||||
$calledA = false;
|
||||
$a = function (array $req) use (&$calledA, $future) {
|
||||
$calledA = true;
|
||||
return $future;
|
||||
};
|
||||
$calledB = false;
|
||||
$b = function (array $req) use (&$calledB) { $calledB = true; };
|
||||
$s = Middleware::wrapFuture($a, $b);
|
||||
$s([]);
|
||||
$this->assertTrue($calledA);
|
||||
$this->assertFalse($calledB);
|
||||
}
|
||||
|
||||
public function testFutureCallsStreamingHandler()
|
||||
{
|
||||
$future = new CompletedFutureArray(['status' => 200]);
|
||||
$calledA = false;
|
||||
$a = function (array $req) use (&$calledA) { $calledA = true; };
|
||||
$calledB = false;
|
||||
$b = function (array $req) use (&$calledB, $future) {
|
||||
$calledB = true;
|
||||
return $future;
|
||||
};
|
||||
$s = Middleware::wrapFuture($a, $b);
|
||||
$result = $s(['client' => ['future' => true]]);
|
||||
$this->assertFalse($calledA);
|
||||
$this->assertTrue($calledB);
|
||||
$this->assertSame($future, $result);
|
||||
}
|
||||
|
||||
public function testStreamingCallsDefaultHandler()
|
||||
{
|
||||
$calledA = false;
|
||||
$a = function (array $req) use (&$calledA) { $calledA = true; };
|
||||
$calledB = false;
|
||||
$b = function (array $req) use (&$calledB) { $calledB = true; };
|
||||
$s = Middleware::wrapStreaming($a, $b);
|
||||
$s([]);
|
||||
$this->assertTrue($calledA);
|
||||
$this->assertFalse($calledB);
|
||||
}
|
||||
|
||||
public function testStreamingCallsStreamingHandler()
|
||||
{
|
||||
$calledA = false;
|
||||
$a = function (array $req) use (&$calledA) { $calledA = true; };
|
||||
$calledB = false;
|
||||
$b = function (array $req) use (&$calledB) { $calledB = true; };
|
||||
$s = Middleware::wrapStreaming($a, $b);
|
||||
$s(['client' => ['stream' => true]]);
|
||||
$this->assertFalse($calledA);
|
||||
$this->assertTrue($calledB);
|
||||
}
|
||||
}
|
86
core/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php
vendored
Normal file
86
core/vendor/guzzlehttp/ringphp/tests/Client/MockHandlerTest.php
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Client\MockHandler;
|
||||
use GuzzleHttp\Ring\Future\FutureArray;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
class MockHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testReturnsArray()
|
||||
{
|
||||
$mock = new MockHandler(['status' => 200]);
|
||||
$response = $mock([]);
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$this->assertEquals([], $response['headers']);
|
||||
$this->assertNull($response['body']);
|
||||
$this->assertNull($response['reason']);
|
||||
$this->assertNull($response['effective_url']);
|
||||
}
|
||||
|
||||
public function testReturnsFutures()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$future = new FutureArray(
|
||||
$deferred->promise(),
|
||||
function () use ($deferred) {
|
||||
$deferred->resolve(['status' => 200]);
|
||||
}
|
||||
);
|
||||
$mock = new MockHandler($future);
|
||||
$response = $mock([]);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
|
||||
$this->assertEquals(200, $response['status']);
|
||||
}
|
||||
|
||||
public function testReturnsFuturesWithThenCall()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$future = new FutureArray(
|
||||
$deferred->promise(),
|
||||
function () use ($deferred) {
|
||||
$deferred->resolve(['status' => 200]);
|
||||
}
|
||||
);
|
||||
$mock = new MockHandler($future);
|
||||
$response = $mock([]);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$req = null;
|
||||
$promise = $response->then(function ($value) use (&$req) {
|
||||
$req = $value;
|
||||
$this->assertEquals(200, $req['status']);
|
||||
});
|
||||
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
|
||||
$this->assertEquals(200, $req['status']);
|
||||
}
|
||||
|
||||
public function testReturnsFuturesAndProxiesCancel()
|
||||
{
|
||||
$c = null;
|
||||
$deferred = new Deferred();
|
||||
$future = new FutureArray(
|
||||
$deferred->promise(),
|
||||
function () {},
|
||||
function () use (&$c) {
|
||||
$c = true;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
$mock = new MockHandler($future);
|
||||
$response = $mock([]);
|
||||
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
|
||||
$response->cancel();
|
||||
$this->assertTrue($c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Response must be an array or FutureArrayInterface. Found
|
||||
*/
|
||||
public function testEnsuresMockIsValid()
|
||||
{
|
||||
$mock = new MockHandler('foo');
|
||||
$mock([]);
|
||||
}
|
||||
}
|
183
core/vendor/guzzlehttp/ringphp/tests/Client/Server.php
vendored
Normal file
183
core/vendor/guzzlehttp/ringphp/tests/Client/Server.php
vendored
Normal file
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Client\StreamHandler;
|
||||
use GuzzleHttp\Ring\Core;
|
||||
|
||||
/**
|
||||
* Class uses to control the test webserver.
|
||||
*
|
||||
* Queued responses will be served to requests using a FIFO order. All requests
|
||||
* received by the server are stored on the node.js server and can be retrieved
|
||||
* by calling {@see Server::received()}.
|
||||
*
|
||||
* Mock responses that don't require data to be transmitted over HTTP a great
|
||||
* for testing. Mock response, however, cannot test the actual sending of an
|
||||
* HTTP request using cURL. This test server allows the simulation of any
|
||||
* number of HTTP request response transactions to test the actual sending of
|
||||
* requests over the wire without having to leave an internal network.
|
||||
*/
|
||||
class Server
|
||||
{
|
||||
public static $started;
|
||||
public static $url = 'http://127.0.0.1:8125/';
|
||||
public static $host = '127.0.0.1:8125';
|
||||
public static $port = 8125;
|
||||
|
||||
/**
|
||||
* Flush the received requests from the server
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function flush()
|
||||
{
|
||||
self::send('DELETE', '/guzzle-server/requests');
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue an array of responses or a single response on the server.
|
||||
*
|
||||
* Any currently queued responses will be overwritten. Subsequent requests
|
||||
* on the server will return queued responses in FIFO order.
|
||||
*
|
||||
* @param array $responses An array of responses. The shape of a response
|
||||
* is the shape described in the RingPHP spec.
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function enqueue(array $responses)
|
||||
{
|
||||
$data = [];
|
||||
|
||||
foreach ($responses as $response) {
|
||||
if (!is_array($response)) {
|
||||
throw new \Exception('Each response must be an array');
|
||||
}
|
||||
|
||||
if (isset($response['body'])) {
|
||||
$response['body'] = base64_encode($response['body']);
|
||||
}
|
||||
|
||||
$response += ['headers' => [], 'reason' => '', 'body' => ''];
|
||||
$data[] = $response;
|
||||
}
|
||||
|
||||
self::send('PUT', '/guzzle-server/responses', json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the received requests as a RingPHP request structure.
|
||||
*
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function received()
|
||||
{
|
||||
if (!self::$started) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$response = self::send('GET', '/guzzle-server/requests');
|
||||
$body = Core::body($response);
|
||||
$result = json_decode($body, true);
|
||||
if ($result === false) {
|
||||
throw new \RuntimeException('Error decoding response: '
|
||||
. json_last_error());
|
||||
}
|
||||
|
||||
foreach ($result as &$res) {
|
||||
if (isset($res['uri'])) {
|
||||
$res['resource'] = $res['uri'];
|
||||
}
|
||||
if (isset($res['query_string'])) {
|
||||
$res['resource'] .= '?' . $res['query_string'];
|
||||
}
|
||||
if (!isset($res['resource'])) {
|
||||
$res['resource'] = '';
|
||||
}
|
||||
// Ensure that headers are all arrays
|
||||
if (isset($res['headers'])) {
|
||||
foreach ($res['headers'] as &$h) {
|
||||
$h = (array) $h;
|
||||
}
|
||||
unset($h);
|
||||
}
|
||||
}
|
||||
|
||||
unset($res);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop running the node.js server
|
||||
*/
|
||||
public static function stop()
|
||||
{
|
||||
if (self::$started) {
|
||||
self::send('DELETE', '/guzzle-server');
|
||||
}
|
||||
|
||||
self::$started = false;
|
||||
}
|
||||
|
||||
public static function wait($maxTries = 20)
|
||||
{
|
||||
$tries = 0;
|
||||
while (!self::isListening() && ++$tries < $maxTries) {
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
if (!self::isListening()) {
|
||||
throw new \RuntimeException('Unable to contact node.js server');
|
||||
}
|
||||
}
|
||||
|
||||
public static function start()
|
||||
{
|
||||
if (self::$started) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
self::wait();
|
||||
} catch (\Exception $e) {
|
||||
exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js '
|
||||
. self::$port . ' >> /tmp/server.log 2>&1 &');
|
||||
self::wait();
|
||||
}
|
||||
|
||||
self::$started = true;
|
||||
}
|
||||
|
||||
private static function isListening()
|
||||
{
|
||||
$response = self::send('GET', '/guzzle-server/perf', null, [
|
||||
'connect_timeout' => 1,
|
||||
'timeout' => 1
|
||||
]);
|
||||
|
||||
return !isset($response['error']);
|
||||
}
|
||||
|
||||
private static function send(
|
||||
$method,
|
||||
$path,
|
||||
$body = null,
|
||||
array $client = []
|
||||
) {
|
||||
$handler = new StreamHandler();
|
||||
|
||||
$request = [
|
||||
'http_method' => $method,
|
||||
'uri' => $path,
|
||||
'request_port' => 8125,
|
||||
'headers' => ['host' => ['127.0.0.1:8125']],
|
||||
'body' => $body,
|
||||
'client' => $client,
|
||||
];
|
||||
|
||||
if ($body) {
|
||||
$request['headers']['content-length'] = [strlen($body)];
|
||||
}
|
||||
|
||||
return $handler($request);
|
||||
}
|
||||
}
|
479
core/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php
vendored
Normal file
479
core/vendor/guzzlehttp/ringphp/tests/Client/StreamHandlerTest.php
vendored
Normal file
|
@ -0,0 +1,479 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Client;
|
||||
|
||||
use GuzzleHttp\Ring\Client\ClientUtils;
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Client\StreamHandler;
|
||||
|
||||
class StreamHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testReturnsResponseForSuccessfulRequest()
|
||||
{
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => [
|
||||
'host' => [Server::$host],
|
||||
'Foo' => ['Bar'],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$this->assertEquals('OK', $response['reason']);
|
||||
$this->assertEquals(['Bar'], $response['headers']['Foo']);
|
||||
$this->assertEquals(['8'], $response['headers']['Content-Length']);
|
||||
$this->assertEquals('hi there', Core::body($response));
|
||||
|
||||
$sent = Server::received()[0];
|
||||
$this->assertEquals('GET', $sent['http_method']);
|
||||
$this->assertEquals('/', $sent['resource']);
|
||||
$this->assertEquals(['127.0.0.1:8125'], $sent['headers']['host']);
|
||||
$this->assertEquals('Bar', Core::header($sent, 'foo'));
|
||||
}
|
||||
|
||||
public function testAddsErrorToResponse()
|
||||
{
|
||||
$handler = new StreamHandler();
|
||||
$result = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => ['localhost:123']],
|
||||
'client' => ['timeout' => 0.01],
|
||||
]);
|
||||
$this->assertInstanceOf(
|
||||
'GuzzleHttp\Ring\Future\CompletedFutureArray',
|
||||
$result
|
||||
);
|
||||
$this->assertNull($result['status']);
|
||||
$this->assertNull($result['body']);
|
||||
$this->assertEquals([], $result['headers']);
|
||||
$this->assertInstanceOf(
|
||||
'GuzzleHttp\Ring\Exception\RingException',
|
||||
$result['error']
|
||||
);
|
||||
}
|
||||
|
||||
public function testEnsuresTheHttpProtocol()
|
||||
{
|
||||
$handler = new StreamHandler();
|
||||
$result = $handler([
|
||||
'http_method' => 'GET',
|
||||
'url' => 'ftp://localhost:123',
|
||||
]);
|
||||
$this->assertArrayHasKey('error', $result);
|
||||
$this->assertContains(
|
||||
'URL is invalid: ftp://localhost:123',
|
||||
$result['error']->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public function testStreamAttributeKeepsStreamOpen()
|
||||
{
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'PUT',
|
||||
'uri' => '/foo',
|
||||
'query_string' => 'baz=bar',
|
||||
'headers' => [
|
||||
'host' => [Server::$host],
|
||||
'Foo' => ['Bar'],
|
||||
],
|
||||
'body' => 'test',
|
||||
'client' => ['stream' => true],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$this->assertEquals('OK', $response['reason']);
|
||||
$this->assertEquals('8', Core::header($response, 'Content-Length'));
|
||||
$body = $response['body'];
|
||||
$this->assertTrue(is_resource($body));
|
||||
$this->assertEquals('http', stream_get_meta_data($body)['wrapper_type']);
|
||||
$this->assertEquals('hi there', stream_get_contents($body));
|
||||
fclose($body);
|
||||
$sent = Server::received()[0];
|
||||
$this->assertEquals('PUT', $sent['http_method']);
|
||||
$this->assertEquals('/foo', $sent['uri']);
|
||||
$this->assertEquals('baz=bar', $sent['query_string']);
|
||||
$this->assertEquals('/foo?baz=bar', $sent['resource']);
|
||||
$this->assertEquals('127.0.0.1:8125', Core::header($sent, 'host'));
|
||||
$this->assertEquals('Bar', Core::header($sent, 'foo'));
|
||||
}
|
||||
|
||||
public function testDrainsResponseIntoTempStream()
|
||||
{
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
]);
|
||||
$body = $response['body'];
|
||||
$this->assertEquals('php://temp', stream_get_meta_data($body)['uri']);
|
||||
$this->assertEquals('hi', fread($body, 2));
|
||||
fclose($body);
|
||||
}
|
||||
|
||||
public function testDrainsResponseIntoSaveToBody()
|
||||
{
|
||||
$r = fopen('php://temp', 'r+');
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['save_to' => $r],
|
||||
]);
|
||||
$body = $response['body'];
|
||||
$this->assertEquals('php://temp', stream_get_meta_data($body)['uri']);
|
||||
$this->assertEquals('hi', fread($body, 2));
|
||||
$this->assertEquals(' there', stream_get_contents($r));
|
||||
fclose($r);
|
||||
}
|
||||
|
||||
public function testDrainsResponseIntoSaveToBodyAtPath()
|
||||
{
|
||||
$tmpfname = tempnam('/tmp', 'save_to_path');
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => ['save_to' => $tmpfname],
|
||||
]);
|
||||
$body = $response['body'];
|
||||
$this->assertInstanceOf('GuzzleHttp\Stream\StreamInterface', $body);
|
||||
$this->assertEquals($tmpfname, $body->getMetadata('uri'));
|
||||
$this->assertEquals('hi', $body->read(2));
|
||||
$body->close();
|
||||
unlink($tmpfname);
|
||||
}
|
||||
|
||||
public function testAutomaticallyDecompressGzip()
|
||||
{
|
||||
Server::flush();
|
||||
$content = gzencode('test');
|
||||
Server::enqueue([
|
||||
[
|
||||
'status' => 200,
|
||||
'reason' => 'OK',
|
||||
'headers' => [
|
||||
'Content-Encoding' => ['gzip'],
|
||||
'Content-Length' => [strlen($content)],
|
||||
],
|
||||
'body' => $content,
|
||||
],
|
||||
]);
|
||||
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'uri' => '/',
|
||||
'client' => ['decode_content' => true],
|
||||
]);
|
||||
$this->assertEquals('test', Core::body($response));
|
||||
}
|
||||
|
||||
public function testDoesNotForceGzipDecode()
|
||||
{
|
||||
Server::flush();
|
||||
$content = gzencode('test');
|
||||
Server::enqueue([
|
||||
[
|
||||
'status' => 200,
|
||||
'reason' => 'OK',
|
||||
'headers' => [
|
||||
'Content-Encoding' => ['gzip'],
|
||||
'Content-Length' => [strlen($content)],
|
||||
],
|
||||
'body' => $content,
|
||||
],
|
||||
]);
|
||||
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler([
|
||||
'http_method' => 'GET',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'uri' => '/',
|
||||
'client' => ['stream' => true, 'decode_content' => false],
|
||||
]);
|
||||
$this->assertSame($content, Core::body($response));
|
||||
}
|
||||
|
||||
public function testProtocolVersion()
|
||||
{
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'version' => 1.0,
|
||||
]);
|
||||
|
||||
$this->assertEquals(1.0, Server::received()[0]['version']);
|
||||
}
|
||||
|
||||
protected function getSendResult(array $opts)
|
||||
{
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$opts['stream'] = true;
|
||||
return $handler([
|
||||
'http_method' => 'GET',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => [Server::$host]],
|
||||
'client' => $opts,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testAddsProxy()
|
||||
{
|
||||
$res = $this->getSendResult(['stream' => true, 'proxy' => '127.0.0.1:8125']);
|
||||
$opts = stream_context_get_options($res['body']);
|
||||
$this->assertEquals('127.0.0.1:8125', $opts['http']['proxy']);
|
||||
}
|
||||
|
||||
public function testAddsTimeout()
|
||||
{
|
||||
$res = $this->getSendResult(['stream' => true, 'timeout' => 200]);
|
||||
$opts = stream_context_get_options($res['body']);
|
||||
$this->assertEquals(200, $opts['http']['timeout']);
|
||||
}
|
||||
|
||||
public function testVerifiesVerifyIsValidIfPath()
|
||||
{
|
||||
$res = $this->getSendResult(['verify' => '/does/not/exist']);
|
||||
$this->assertContains(
|
||||
'SSL CA bundle not found: /does/not/exist',
|
||||
(string) $res['error']
|
||||
);
|
||||
}
|
||||
|
||||
public function testVerifyCanBeDisabled()
|
||||
{
|
||||
$res = $this->getSendResult(['verify' => false]);
|
||||
$this->assertArrayNotHasKey('error', $res);
|
||||
}
|
||||
|
||||
public function testVerifiesCertIfValidPath()
|
||||
{
|
||||
$res = $this->getSendResult(['cert' => '/does/not/exist']);
|
||||
$this->assertContains(
|
||||
'SSL certificate not found: /does/not/exist',
|
||||
(string) $res['error']
|
||||
);
|
||||
}
|
||||
|
||||
public function testVerifyCanBeSetToPath()
|
||||
{
|
||||
$path = $path = ClientUtils::getDefaultCaBundle();
|
||||
$res = $this->getSendResult(['verify' => $path]);
|
||||
$this->assertArrayNotHasKey('error', $res);
|
||||
$opts = stream_context_get_options($res['body']);
|
||||
$this->assertEquals(true, $opts['ssl']['verify_peer']);
|
||||
$this->assertEquals($path, $opts['ssl']['cafile']);
|
||||
$this->assertTrue(file_exists($opts['ssl']['cafile']));
|
||||
}
|
||||
|
||||
public function testUsesSystemDefaultBundle()
|
||||
{
|
||||
$path = $path = ClientUtils::getDefaultCaBundle();
|
||||
$res = $this->getSendResult(['verify' => true]);
|
||||
$this->assertArrayNotHasKey('error', $res);
|
||||
$opts = stream_context_get_options($res['body']);
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
$this->assertEquals($path, $opts['ssl']['cafile']);
|
||||
}
|
||||
}
|
||||
|
||||
public function testEnsuresVerifyOptionIsValid()
|
||||
{
|
||||
$res = $this->getSendResult(['verify' => 10]);
|
||||
$this->assertContains(
|
||||
'Invalid verify request option',
|
||||
(string) $res['error']
|
||||
);
|
||||
}
|
||||
|
||||
public function testCanSetPasswordWhenSettingCert()
|
||||
{
|
||||
$path = __FILE__;
|
||||
$res = $this->getSendResult(['cert' => [$path, 'foo']]);
|
||||
$opts = stream_context_get_options($res['body']);
|
||||
$this->assertEquals($path, $opts['ssl']['local_cert']);
|
||||
$this->assertEquals('foo', $opts['ssl']['passphrase']);
|
||||
}
|
||||
|
||||
public function testDebugAttributeWritesToStream()
|
||||
{
|
||||
$this->queueRes();
|
||||
$f = fopen('php://temp', 'w+');
|
||||
$this->getSendResult(['debug' => $f]);
|
||||
fseek($f, 0);
|
||||
$contents = stream_get_contents($f);
|
||||
$this->assertContains('<GET http://127.0.0.1:8125/> [CONNECT]', $contents);
|
||||
$this->assertContains('<GET http://127.0.0.1:8125/> [FILE_SIZE_IS]', $contents);
|
||||
$this->assertContains('<GET http://127.0.0.1:8125/> [PROGRESS]', $contents);
|
||||
}
|
||||
|
||||
public function testDebugAttributeWritesStreamInfoToBuffer()
|
||||
{
|
||||
$called = false;
|
||||
$this->queueRes();
|
||||
$buffer = fopen('php://temp', 'r+');
|
||||
$this->getSendResult([
|
||||
'progress' => function () use (&$called) { $called = true; },
|
||||
'debug' => $buffer,
|
||||
]);
|
||||
fseek($buffer, 0);
|
||||
$contents = stream_get_contents($buffer);
|
||||
$this->assertContains('<GET http://127.0.0.1:8125/> [CONNECT]', $contents);
|
||||
$this->assertContains('<GET http://127.0.0.1:8125/> [FILE_SIZE_IS] message: "Content-Length: 8"', $contents);
|
||||
$this->assertContains('<GET http://127.0.0.1:8125/> [PROGRESS] bytes_max: "8"', $contents);
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testEmitsProgressInformation()
|
||||
{
|
||||
$called = [];
|
||||
$this->queueRes();
|
||||
$this->getSendResult([
|
||||
'progress' => function () use (&$called) {
|
||||
$called[] = func_get_args();
|
||||
},
|
||||
]);
|
||||
$this->assertNotEmpty($called);
|
||||
$this->assertEquals(8, $called[0][0]);
|
||||
$this->assertEquals(0, $called[0][1]);
|
||||
}
|
||||
|
||||
public function testEmitsProgressInformationAndDebugInformation()
|
||||
{
|
||||
$called = [];
|
||||
$this->queueRes();
|
||||
$buffer = fopen('php://memory', 'w+');
|
||||
$this->getSendResult([
|
||||
'debug' => $buffer,
|
||||
'progress' => function () use (&$called) {
|
||||
$called[] = func_get_args();
|
||||
},
|
||||
]);
|
||||
$this->assertNotEmpty($called);
|
||||
$this->assertEquals(8, $called[0][0]);
|
||||
$this->assertEquals(0, $called[0][1]);
|
||||
rewind($buffer);
|
||||
$this->assertNotEmpty(stream_get_contents($buffer));
|
||||
fclose($buffer);
|
||||
}
|
||||
|
||||
public function testAddsProxyByProtocol()
|
||||
{
|
||||
$url = str_replace('http', 'tcp', Server::$url);
|
||||
$res = $this->getSendResult(['proxy' => ['http' => $url]]);
|
||||
$opts = stream_context_get_options($res['body']);
|
||||
$this->assertEquals($url, $opts['http']['proxy']);
|
||||
}
|
||||
|
||||
public function testPerformsShallowMergeOfCustomContextOptions()
|
||||
{
|
||||
$res = $this->getSendResult([
|
||||
'stream_context' => [
|
||||
'http' => [
|
||||
'request_fulluri' => true,
|
||||
'method' => 'HEAD',
|
||||
],
|
||||
'socket' => [
|
||||
'bindto' => '127.0.0.1:0',
|
||||
],
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$opts = stream_context_get_options($res['body']);
|
||||
$this->assertEquals('HEAD', $opts['http']['method']);
|
||||
$this->assertTrue($opts['http']['request_fulluri']);
|
||||
$this->assertFalse($opts['ssl']['verify_peer']);
|
||||
$this->assertEquals('127.0.0.1:0', $opts['socket']['bindto']);
|
||||
}
|
||||
|
||||
public function testEnsuresThatStreamContextIsAnArray()
|
||||
{
|
||||
$res = $this->getSendResult(['stream_context' => 'foo']);
|
||||
$this->assertContains(
|
||||
'stream_context must be an array',
|
||||
(string) $res['error']
|
||||
);
|
||||
}
|
||||
|
||||
public function testDoesNotAddContentTypeByDefault()
|
||||
{
|
||||
$this->queueRes();
|
||||
$handler = new StreamHandler();
|
||||
$handler([
|
||||
'http_method' => 'PUT',
|
||||
'uri' => '/',
|
||||
'headers' => ['host' => [Server::$host], 'content-length' => [3]],
|
||||
'body' => 'foo',
|
||||
]);
|
||||
$req = Server::received()[0];
|
||||
$this->assertEquals('', Core::header($req, 'Content-Type'));
|
||||
$this->assertEquals(3, Core::header($req, 'Content-Length'));
|
||||
}
|
||||
|
||||
private function queueRes()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([
|
||||
[
|
||||
'status' => 200,
|
||||
'reason' => 'OK',
|
||||
'headers' => [
|
||||
'Foo' => ['Bar'],
|
||||
'Content-Length' => [8],
|
||||
],
|
||||
'body' => 'hi there',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSupports100Continue()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([
|
||||
[
|
||||
'status' => '200',
|
||||
'reason' => 'OK',
|
||||
'headers' => [
|
||||
'Test' => ['Hello'],
|
||||
'Content-Length' => ['4'],
|
||||
],
|
||||
'body' => 'test',
|
||||
],
|
||||
]);
|
||||
|
||||
$request = [
|
||||
'http_method' => 'PUT',
|
||||
'headers' => [
|
||||
'Host' => [Server::$host],
|
||||
'Expect' => ['100-Continue'],
|
||||
],
|
||||
'body' => 'test',
|
||||
];
|
||||
|
||||
$handler = new StreamHandler();
|
||||
$response = $handler($request);
|
||||
$this->assertEquals(200, $response['status']);
|
||||
$this->assertEquals('OK', $response['reason']);
|
||||
$this->assertEquals(['Hello'], $response['headers']['Test']);
|
||||
$this->assertEquals(['4'], $response['headers']['Content-Length']);
|
||||
$this->assertEquals('test', Core::body($response));
|
||||
}
|
||||
}
|
241
core/vendor/guzzlehttp/ringphp/tests/Client/server.js
vendored
Normal file
241
core/vendor/guzzlehttp/ringphp/tests/Client/server.js
vendored
Normal file
|
@ -0,0 +1,241 @@
|
|||
/**
|
||||
* Guzzle node.js test server to return queued responses to HTTP requests and
|
||||
* expose a RESTful API for enqueueing responses and retrieving the requests
|
||||
* that have been received.
|
||||
*
|
||||
* - Delete all requests that have been received:
|
||||
* > DELETE /guzzle-server/requests
|
||||
* > Host: 127.0.0.1:8125
|
||||
*
|
||||
* - Enqueue responses
|
||||
* > PUT /guzzle-server/responses
|
||||
* > Host: 127.0.0.1:8125
|
||||
* >
|
||||
* > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }]
|
||||
*
|
||||
* - Get the received requests
|
||||
* > GET /guzzle-server/requests
|
||||
* > Host: 127.0.0.1:8125
|
||||
*
|
||||
* < HTTP/1.1 200 OK
|
||||
* <
|
||||
* < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}]
|
||||
*
|
||||
* - Attempt access to the secure area
|
||||
* > GET /secure/by-digest/qop-auth/guzzle-server/requests
|
||||
* > Host: 127.0.0.1:8125
|
||||
*
|
||||
* < HTTP/1.1 401 Unauthorized
|
||||
* < WWW-Authenticate: Digest realm="Digest Test", qop="auth", nonce="0796e98e1aeef43141fab2a66bf4521a", algorithm="MD5", stale="false"
|
||||
* <
|
||||
* < 401 Unauthorized
|
||||
*
|
||||
* - Shutdown the server
|
||||
* > DELETE /guzzle-server
|
||||
* > Host: 127.0.0.1:8125
|
||||
*
|
||||
* @package Guzzle PHP <http://www.guzzlephp.org>
|
||||
* @license See the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
|
||||
/**
|
||||
* Guzzle node.js server
|
||||
* @class
|
||||
*/
|
||||
var GuzzleServer = function(port, log) {
|
||||
|
||||
this.port = port;
|
||||
this.log = log;
|
||||
this.responses = [];
|
||||
this.requests = [];
|
||||
var that = this;
|
||||
|
||||
var md5 = function(input) {
|
||||
var crypto = require('crypto');
|
||||
var hasher = crypto.createHash('md5');
|
||||
hasher.update(input);
|
||||
return hasher.digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* Node.js HTTP server authentication module.
|
||||
*
|
||||
* It is only initialized on demand (by loadAuthentifier). This avoids
|
||||
* requiring the dependency to http-auth on standard operations, and the
|
||||
* performance hit at startup.
|
||||
*/
|
||||
var auth;
|
||||
|
||||
/**
|
||||
* Provides authentication handlers (Basic, Digest).
|
||||
*/
|
||||
var loadAuthentifier = function(type, options) {
|
||||
var typeId = type;
|
||||
if (type == 'digest') {
|
||||
typeId += '.'+(options && options.qop ? options.qop : 'none');
|
||||
}
|
||||
if (!loadAuthentifier[typeId]) {
|
||||
if (!auth) {
|
||||
try {
|
||||
auth = require('http-auth');
|
||||
} catch (e) {
|
||||
if (e.code == 'MODULE_NOT_FOUND') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case 'digest':
|
||||
var digestParams = {
|
||||
realm: 'Digest Test',
|
||||
login: 'me',
|
||||
password: 'test'
|
||||
};
|
||||
if (options && options.qop) {
|
||||
digestParams.qop = options.qop;
|
||||
}
|
||||
loadAuthentifier[typeId] = auth.digest(digestParams, function(username, callback) {
|
||||
callback(md5(digestParams.login + ':' + digestParams.realm + ':' + digestParams.password));
|
||||
});
|
||||
break
|
||||
}
|
||||
}
|
||||
return loadAuthentifier[typeId];
|
||||
};
|
||||
|
||||
var firewallRequest = function(request, req, res, requestHandlerCallback) {
|
||||
var securedAreaUriParts = request.uri.match(/^\/secure\/by-(digest)(\/qop-([^\/]*))?(\/.*)$/);
|
||||
if (securedAreaUriParts) {
|
||||
var authentifier = loadAuthentifier(securedAreaUriParts[1], { qop: securedAreaUriParts[2] });
|
||||
if (!authentifier) {
|
||||
res.writeHead(501, 'HTTP authentication not implemented', { 'Content-Length': 0 });
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
authentifier.check(req, res, function(req, res) {
|
||||
req.url = securedAreaUriParts[4];
|
||||
requestHandlerCallback(request, req, res);
|
||||
});
|
||||
} else {
|
||||
requestHandlerCallback(request, req, res);
|
||||
}
|
||||
};
|
||||
|
||||
var controlRequest = function(request, req, res) {
|
||||
if (req.url == '/guzzle-server/perf') {
|
||||
res.writeHead(200, 'OK', {'Content-Length': 16});
|
||||
res.end('Body of response');
|
||||
} else if (req.method == 'DELETE') {
|
||||
if (req.url == '/guzzle-server/requests') {
|
||||
// Clear the received requests
|
||||
that.requests = [];
|
||||
res.writeHead(200, 'OK', { 'Content-Length': 0 });
|
||||
res.end();
|
||||
if (that.log) {
|
||||
console.log('Flushing requests');
|
||||
}
|
||||
} else if (req.url == '/guzzle-server') {
|
||||
// Shutdown the server
|
||||
res.writeHead(200, 'OK', { 'Content-Length': 0, 'Connection': 'close' });
|
||||
res.end();
|
||||
if (that.log) {
|
||||
console.log('Shutting down');
|
||||
}
|
||||
that.server.close();
|
||||
}
|
||||
} else if (req.method == 'GET') {
|
||||
if (req.url === '/guzzle-server/requests') {
|
||||
if (that.log) {
|
||||
console.log('Sending received requests');
|
||||
}
|
||||
// Get received requests
|
||||
var body = JSON.stringify(that.requests);
|
||||
res.writeHead(200, 'OK', { 'Content-Length': body.length });
|
||||
res.end(body);
|
||||
}
|
||||
} else if (req.method == 'PUT' && req.url == '/guzzle-server/responses') {
|
||||
if (that.log) {
|
||||
console.log('Adding responses...');
|
||||
}
|
||||
if (!request.body) {
|
||||
if (that.log) {
|
||||
console.log('No response data was provided');
|
||||
}
|
||||
res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 });
|
||||
} else {
|
||||
that.responses = eval('(' + request.body + ')');
|
||||
for (var i = 0; i < that.responses.length; i++) {
|
||||
if (that.responses[i].body) {
|
||||
that.responses[i].body = new Buffer(that.responses[i].body, 'base64');
|
||||
}
|
||||
}
|
||||
if (that.log) {
|
||||
console.log(that.responses);
|
||||
}
|
||||
res.writeHead(200, 'OK', { 'Content-Length': 0 });
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
};
|
||||
|
||||
var receivedRequest = function(request, req, res) {
|
||||
if (req.url.indexOf('/guzzle-server') === 0) {
|
||||
controlRequest(request, req, res);
|
||||
} else if (req.url.indexOf('/guzzle-server') == -1 && !that.responses.length) {
|
||||
res.writeHead(500);
|
||||
res.end('No responses in queue');
|
||||
} else {
|
||||
if (that.log) {
|
||||
console.log('Returning response from queue and adding request');
|
||||
}
|
||||
that.requests.push(request);
|
||||
var response = that.responses.shift();
|
||||
res.writeHead(response.status, response.reason, response.headers);
|
||||
res.end(response.body);
|
||||
}
|
||||
};
|
||||
|
||||
this.start = function() {
|
||||
|
||||
that.server = http.createServer(function(req, res) {
|
||||
|
||||
var parts = url.parse(req.url, false);
|
||||
var request = {
|
||||
http_method: req.method,
|
||||
scheme: parts.scheme,
|
||||
uri: parts.pathname,
|
||||
query_string: parts.query,
|
||||
headers: req.headers,
|
||||
version: req.httpVersion,
|
||||
body: ''
|
||||
};
|
||||
|
||||
// Receive each chunk of the request body
|
||||
req.addListener('data', function(chunk) {
|
||||
request.body += chunk;
|
||||
});
|
||||
|
||||
// Called when the request completes
|
||||
req.addListener('end', function() {
|
||||
firewallRequest(request, req, res, receivedRequest);
|
||||
});
|
||||
});
|
||||
|
||||
that.server.listen(this.port, '127.0.0.1');
|
||||
|
||||
if (this.log) {
|
||||
console.log('Server running at http://127.0.0.1:8125/');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Get the port from the arguments
|
||||
port = process.argv.length >= 3 ? process.argv[2] : 8125;
|
||||
log = process.argv.length >= 4 ? process.argv[3] : false;
|
||||
|
||||
// Start the server
|
||||
server = new GuzzleServer(port, log);
|
||||
server.start();
|
336
core/vendor/guzzlehttp/ringphp/tests/CoreTest.php
vendored
Normal file
336
core/vendor/guzzlehttp/ringphp/tests/CoreTest.php
vendored
Normal file
|
@ -0,0 +1,336 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring;
|
||||
|
||||
use GuzzleHttp\Ring\Core;
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
use GuzzleHttp\Ring\Future\FutureArray;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
class CoreTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testReturnsNullNoHeadersAreSet()
|
||||
{
|
||||
$this->assertNull(Core::header([], 'Foo'));
|
||||
$this->assertNull(Core::firstHeader([], 'Foo'));
|
||||
}
|
||||
|
||||
public function testChecksIfHasHeader()
|
||||
{
|
||||
$message = [
|
||||
'headers' => [
|
||||
'Foo' => ['Bar', 'Baz'],
|
||||
'foo' => ['hello'],
|
||||
'bar' => ['1']
|
||||
]
|
||||
];
|
||||
$this->assertTrue(Core::hasHeader($message, 'Foo'));
|
||||
$this->assertTrue(Core::hasHeader($message, 'foo'));
|
||||
$this->assertTrue(Core::hasHeader($message, 'FoO'));
|
||||
$this->assertTrue(Core::hasHeader($message, 'bar'));
|
||||
$this->assertFalse(Core::hasHeader($message, 'barr'));
|
||||
}
|
||||
|
||||
public function testReturnsFirstHeaderWhenSimple()
|
||||
{
|
||||
$this->assertEquals('Bar', Core::firstHeader([
|
||||
'headers' => ['Foo' => ['Bar', 'Baz']],
|
||||
], 'Foo'));
|
||||
}
|
||||
|
||||
public function testReturnsFirstHeaderWhenMultiplePerLine()
|
||||
{
|
||||
$this->assertEquals('Bar', Core::firstHeader([
|
||||
'headers' => ['Foo' => ['Bar, Baz']],
|
||||
], 'Foo'));
|
||||
}
|
||||
|
||||
public function testExtractsCaseInsensitiveHeader()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'hello',
|
||||
Core::header(['headers' => ['foo' => ['hello']]], 'FoO')
|
||||
);
|
||||
}
|
||||
|
||||
public function testExtractsCaseInsensitiveHeaderLines()
|
||||
{
|
||||
$this->assertEquals(
|
||||
['a', 'b', 'c', 'd'],
|
||||
Core::headerLines([
|
||||
'headers' => [
|
||||
'foo' => ['a', 'b'],
|
||||
'Foo' => ['c', 'd']
|
||||
]
|
||||
], 'foo')
|
||||
);
|
||||
}
|
||||
|
||||
public function testExtractsHeaderLines()
|
||||
{
|
||||
$this->assertEquals(
|
||||
['bar', 'baz'],
|
||||
Core::headerLines([
|
||||
'headers' => [
|
||||
'Foo' => ['bar', 'baz'],
|
||||
],
|
||||
], 'Foo')
|
||||
);
|
||||
}
|
||||
|
||||
public function testExtractsHeaderAsString()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'bar, baz',
|
||||
Core::header([
|
||||
'headers' => [
|
||||
'Foo' => ['bar', 'baz'],
|
||||
],
|
||||
], 'Foo', true)
|
||||
);
|
||||
}
|
||||
|
||||
public function testReturnsNullWhenHeaderNotFound()
|
||||
{
|
||||
$this->assertNull(Core::header(['headers' => []], 'Foo'));
|
||||
}
|
||||
|
||||
public function testRemovesHeaders()
|
||||
{
|
||||
$message = [
|
||||
'headers' => [
|
||||
'foo' => ['bar'],
|
||||
'Foo' => ['bam'],
|
||||
'baz' => ['123'],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertSame($message, Core::removeHeader($message, 'bam'));
|
||||
$this->assertEquals([
|
||||
'headers' => ['baz' => ['123']],
|
||||
], Core::removeHeader($message, 'foo'));
|
||||
}
|
||||
|
||||
public function testCreatesUrl()
|
||||
{
|
||||
$req = [
|
||||
'scheme' => 'http',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'uri' => '/',
|
||||
];
|
||||
|
||||
$this->assertEquals('http://foo.com/', Core::url($req));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage No Host header was provided
|
||||
*/
|
||||
public function testEnsuresHostIsAvailableWhenCreatingUrls()
|
||||
{
|
||||
Core::url([]);
|
||||
}
|
||||
|
||||
public function testCreatesUrlWithQueryString()
|
||||
{
|
||||
$req = [
|
||||
'scheme' => 'http',
|
||||
'headers' => ['host' => ['foo.com']],
|
||||
'uri' => '/',
|
||||
'query_string' => 'foo=baz',
|
||||
];
|
||||
|
||||
$this->assertEquals('http://foo.com/?foo=baz', Core::url($req));
|
||||
}
|
||||
|
||||
public function testUsesUrlIfSet()
|
||||
{
|
||||
$req = ['url' => 'http://foo.com'];
|
||||
$this->assertEquals('http://foo.com', Core::url($req));
|
||||
}
|
||||
|
||||
public function testReturnsNullWhenNoBody()
|
||||
{
|
||||
$this->assertNull(Core::body([]));
|
||||
}
|
||||
|
||||
public function testReturnsStreamAsString()
|
||||
{
|
||||
$this->assertEquals(
|
||||
'foo',
|
||||
Core::body(['body' => Stream::factory('foo')])
|
||||
);
|
||||
}
|
||||
|
||||
public function testReturnsString()
|
||||
{
|
||||
$this->assertEquals('foo', Core::body(['body' => 'foo']));
|
||||
}
|
||||
|
||||
public function testReturnsResourceContent()
|
||||
{
|
||||
$r = fopen('php://memory', 'w+');
|
||||
fwrite($r, 'foo');
|
||||
rewind($r);
|
||||
$this->assertEquals('foo', Core::body(['body' => $r]));
|
||||
fclose($r);
|
||||
}
|
||||
|
||||
public function testReturnsIteratorContent()
|
||||
{
|
||||
$a = new \ArrayIterator(['a', 'b', 'cd', '']);
|
||||
$this->assertEquals('abcd', Core::body(['body' => $a]));
|
||||
}
|
||||
|
||||
public function testReturnsObjectToString()
|
||||
{
|
||||
$this->assertEquals('foo', Core::body(['body' => new StrClass]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testEnsuresBodyIsValid()
|
||||
{
|
||||
Core::body(['body' => false]);
|
||||
}
|
||||
|
||||
public function testParsesHeadersFromLines()
|
||||
{
|
||||
$lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b'];
|
||||
$this->assertEquals([
|
||||
'Foo' => ['bar', 'baz'],
|
||||
'Abc' => ['123'],
|
||||
'Def' => ['a, b'],
|
||||
], Core::headersFromLines($lines));
|
||||
}
|
||||
|
||||
public function testParsesHeadersFromLinesWithMultipleLines()
|
||||
{
|
||||
$lines = ['Foo: bar', 'Foo: baz', 'Foo: 123'];
|
||||
$this->assertEquals([
|
||||
'Foo' => ['bar', 'baz', '123'],
|
||||
], Core::headersFromLines($lines));
|
||||
}
|
||||
|
||||
public function testCreatesArrayCallFunctions()
|
||||
{
|
||||
$called = [];
|
||||
$a = function ($a, $b) use (&$called) {
|
||||
$called['a'] = func_get_args();
|
||||
};
|
||||
$b = function ($a, $b) use (&$called) {
|
||||
$called['b'] = func_get_args();
|
||||
};
|
||||
$c = Core::callArray([$a, $b]);
|
||||
$c(1, 2);
|
||||
$this->assertEquals([1, 2], $called['a']);
|
||||
$this->assertEquals([1, 2], $called['b']);
|
||||
}
|
||||
|
||||
public function testRewindsGuzzleStreams()
|
||||
{
|
||||
$str = Stream::factory('foo');
|
||||
$this->assertTrue(Core::rewindBody(['body' => $str]));
|
||||
}
|
||||
|
||||
public function testRewindsStreams()
|
||||
{
|
||||
$str = Stream::factory('foo')->detach();
|
||||
$this->assertTrue(Core::rewindBody(['body' => $str]));
|
||||
}
|
||||
|
||||
public function testRewindsIterators()
|
||||
{
|
||||
$iter = new \ArrayIterator(['foo']);
|
||||
$this->assertTrue(Core::rewindBody(['body' => $iter]));
|
||||
}
|
||||
|
||||
public function testRewindsStrings()
|
||||
{
|
||||
$this->assertTrue(Core::rewindBody(['body' => 'hi']));
|
||||
}
|
||||
|
||||
public function testRewindsToStrings()
|
||||
{
|
||||
$this->assertTrue(Core::rewindBody(['body' => new StrClass()]));
|
||||
}
|
||||
|
||||
public function typeProvider()
|
||||
{
|
||||
return [
|
||||
['foo', 'string(3) "foo"'],
|
||||
[true, 'bool(true)'],
|
||||
[false, 'bool(false)'],
|
||||
[10, 'int(10)'],
|
||||
[1.0, 'float(1)'],
|
||||
[new StrClass(), 'object(GuzzleHttp\Tests\Ring\StrClass)'],
|
||||
[['foo'], 'array(1)']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider typeProvider
|
||||
*/
|
||||
public function testDescribesType($input, $output)
|
||||
{
|
||||
$this->assertEquals($output, Core::describeType($input));
|
||||
}
|
||||
|
||||
public function testDoesSleep()
|
||||
{
|
||||
$t = microtime(true);
|
||||
$expected = $t + (100 / 1000);
|
||||
Core::doSleep(['client' => ['delay' => 100]]);
|
||||
$this->assertGreaterThanOrEqual($expected, microtime(true));
|
||||
}
|
||||
|
||||
public function testProxiesFuture()
|
||||
{
|
||||
$f = new CompletedFutureArray(['status' => 200]);
|
||||
$res = null;
|
||||
$proxied = Core::proxy($f, function ($value) use (&$res) {
|
||||
$value['foo'] = 'bar';
|
||||
$res = $value;
|
||||
return $value;
|
||||
});
|
||||
$this->assertNotSame($f, $proxied);
|
||||
$this->assertEquals(200, $f->wait()['status']);
|
||||
$this->assertArrayNotHasKey('foo', $f->wait());
|
||||
$this->assertEquals('bar', $proxied->wait()['foo']);
|
||||
$this->assertEquals(200, $proxied->wait()['status']);
|
||||
}
|
||||
|
||||
public function testProxiesDeferredFuture()
|
||||
{
|
||||
$d = new Deferred();
|
||||
$f = new FutureArray($d->promise());
|
||||
$f2 = Core::proxy($f);
|
||||
$d->resolve(['foo' => 'bar']);
|
||||
$this->assertEquals('bar', $f['foo']);
|
||||
$this->assertEquals('bar', $f2['foo']);
|
||||
}
|
||||
|
||||
public function testProxiesDeferredFutureFailure()
|
||||
{
|
||||
$d = new Deferred();
|
||||
$f = new FutureArray($d->promise());
|
||||
$f2 = Core::proxy($f);
|
||||
$d->reject(new \Exception('foo'));
|
||||
try {
|
||||
$f2['hello?'];
|
||||
$this->fail('did not throw');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertEquals('foo', $e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
final class StrClass
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return 'foo';
|
||||
}
|
||||
}
|
21
core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php
vendored
Normal file
21
core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureArrayTest.php
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Future;
|
||||
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||
|
||||
class CompletedFutureArrayTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testReturnsAsArray()
|
||||
{
|
||||
$f = new CompletedFutureArray(['foo' => 'bar']);
|
||||
$this->assertEquals('bar', $f['foo']);
|
||||
$this->assertFalse(isset($f['baz']));
|
||||
$f['abc'] = '123';
|
||||
$this->assertTrue(isset($f['abc']));
|
||||
$this->assertEquals(['foo' => 'bar', 'abc' => '123'], iterator_to_array($f));
|
||||
$this->assertEquals(2, count($f));
|
||||
unset($f['abc']);
|
||||
$this->assertEquals(1, count($f));
|
||||
$this->assertEquals(['foo' => 'bar'], iterator_to_array($f));
|
||||
}
|
||||
}
|
46
core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php
vendored
Normal file
46
core/vendor/guzzlehttp/ringphp/tests/Future/CompletedFutureValueTest.php
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Future;
|
||||
|
||||
use GuzzleHttp\Ring\Exception\CancelledFutureAccessException;
|
||||
use GuzzleHttp\Ring\Future\CompletedFutureValue;
|
||||
|
||||
class CompletedFutureValueTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testReturnsValue()
|
||||
{
|
||||
$f = new CompletedFutureValue('hi');
|
||||
$this->assertEquals('hi', $f->wait());
|
||||
$f->cancel();
|
||||
|
||||
$a = null;
|
||||
$f->then(function ($v) use (&$a) {
|
||||
$a = $v;
|
||||
});
|
||||
$this->assertSame('hi', $a);
|
||||
}
|
||||
|
||||
public function testThrows()
|
||||
{
|
||||
$ex = new \Exception('foo');
|
||||
$f = new CompletedFutureValue(null, $ex);
|
||||
$f->cancel();
|
||||
try {
|
||||
$f->wait();
|
||||
$this->fail('did not throw');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertSame($e, $ex);
|
||||
}
|
||||
}
|
||||
|
||||
public function testMarksAsCancelled()
|
||||
{
|
||||
$ex = new CancelledFutureAccessException();
|
||||
$f = new CompletedFutureValue(null, $ex);
|
||||
try {
|
||||
$f->wait();
|
||||
$this->fail('did not throw');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertSame($e, $ex);
|
||||
}
|
||||
}
|
||||
}
|
56
core/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php
vendored
Normal file
56
core/vendor/guzzlehttp/ringphp/tests/Future/FutureArrayTest.php
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Future;
|
||||
|
||||
use GuzzleHttp\Ring\Future\FutureArray;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
class FutureArrayTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testLazilyCallsDeref()
|
||||
{
|
||||
$c = false;
|
||||
$deferred = new Deferred();
|
||||
$f = new FutureArray(
|
||||
$deferred->promise(),
|
||||
function () use (&$c, $deferred) {
|
||||
$c = true;
|
||||
$deferred->resolve(['status' => 200]);
|
||||
}
|
||||
);
|
||||
$this->assertFalse($c);
|
||||
$this->assertFalse($this->readAttribute($f, 'isRealized'));
|
||||
$this->assertEquals(200, $f['status']);
|
||||
$this->assertTrue($c);
|
||||
}
|
||||
|
||||
public function testActsLikeArray()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$f = new FutureArray(
|
||||
$deferred->promise(),
|
||||
function () use (&$c, $deferred) {
|
||||
$deferred->resolve(['status' => 200]);
|
||||
}
|
||||
);
|
||||
|
||||
$this->assertTrue(isset($f['status']));
|
||||
$this->assertEquals(200, $f['status']);
|
||||
$this->assertEquals(['status' => 200], $f->wait());
|
||||
$this->assertEquals(1, count($f));
|
||||
$f['baz'] = 10;
|
||||
$this->assertEquals(10, $f['baz']);
|
||||
unset($f['baz']);
|
||||
$this->assertFalse(isset($f['baz']));
|
||||
$this->assertEquals(['status' => 200], iterator_to_array($f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testThrowsWhenAccessingInvalidProperty()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$f = new FutureArray($deferred->promise(), function () {});
|
||||
$f->foo;
|
||||
}
|
||||
}
|
109
core/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php
vendored
Normal file
109
core/vendor/guzzlehttp/ringphp/tests/Future/FutureValueTest.php
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Ring\Future;
|
||||
|
||||
use GuzzleHttp\Ring\Exception\CancelledFutureAccessException;
|
||||
use GuzzleHttp\Ring\Future\FutureValue;
|
||||
use React\Promise\Deferred;
|
||||
|
||||
class FutureValueTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testDerefReturnsValue()
|
||||
{
|
||||
$called = 0;
|
||||
$deferred = new Deferred();
|
||||
|
||||
$f = new FutureValue(
|
||||
$deferred->promise(),
|
||||
function () use ($deferred, &$called) {
|
||||
$called++;
|
||||
$deferred->resolve('foo');
|
||||
}
|
||||
);
|
||||
|
||||
$this->assertEquals('foo', $f->wait());
|
||||
$this->assertEquals(1, $called);
|
||||
$this->assertEquals('foo', $f->wait());
|
||||
$this->assertEquals(1, $called);
|
||||
$f->cancel();
|
||||
$this->assertTrue($this->readAttribute($f, 'isRealized'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException
|
||||
*/
|
||||
public function testThrowsWhenAccessingCancelled()
|
||||
{
|
||||
$f = new FutureValue(
|
||||
(new Deferred())->promise(),
|
||||
function () {},
|
||||
function () { return true; }
|
||||
);
|
||||
$f->cancel();
|
||||
$f->wait();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OutOfBoundsException
|
||||
*/
|
||||
public function testThrowsWhenDerefFailure()
|
||||
{
|
||||
$called = false;
|
||||
$deferred = new Deferred();
|
||||
$f = new FutureValue(
|
||||
$deferred->promise(),
|
||||
function () use(&$called) {
|
||||
$called = true;
|
||||
}
|
||||
);
|
||||
$deferred->reject(new \OutOfBoundsException());
|
||||
$f->wait();
|
||||
$this->assertFalse($called);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \GuzzleHttp\Ring\Exception\RingException
|
||||
* @expectedExceptionMessage Waiting did not resolve future
|
||||
*/
|
||||
public function testThrowsWhenDerefDoesNotResolve()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$f = new FutureValue(
|
||||
$deferred->promise(),
|
||||
function () use(&$called) {
|
||||
$called = true;
|
||||
}
|
||||
);
|
||||
$f->wait();
|
||||
}
|
||||
|
||||
public function testThrowingCancelledFutureAccessExceptionCancels()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$f = new FutureValue(
|
||||
$deferred->promise(),
|
||||
function () use ($deferred) {
|
||||
throw new CancelledFutureAccessException();
|
||||
}
|
||||
);
|
||||
try {
|
||||
$f->wait();
|
||||
$this->fail('did not throw');
|
||||
} catch (CancelledFutureAccessException $e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage foo
|
||||
*/
|
||||
public function testThrowingExceptionInDerefMarksAsFailed()
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$f = new FutureValue(
|
||||
$deferred->promise(),
|
||||
function () {
|
||||
throw new \Exception('foo');
|
||||
}
|
||||
);
|
||||
$f->wait();
|
||||
}
|
||||
}
|
11
core/vendor/guzzlehttp/ringphp/tests/bootstrap.php
vendored
Normal file
11
core/vendor/guzzlehttp/ringphp/tests/bootstrap.php
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
require __DIR__ . '/Client/Server.php';
|
||||
|
||||
use GuzzleHttp\Tests\Ring\Client\Server;
|
||||
|
||||
Server::start();
|
||||
|
||||
register_shutdown_function(function () {
|
||||
Server::stop();
|
||||
});
|
Reference in a new issue