Issue #2461985 by stefan.r: Update Guzzle to latest release
parent
5271f8f028
commit
ed89a08f25
|
@ -783,16 +783,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/guzzle",
|
"name": "guzzlehttp/guzzle",
|
||||||
"version": "5.0.3",
|
"version": "5.2.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/guzzle.git",
|
"url": "https://github.com/guzzle/guzzle.git",
|
||||||
"reference": "6c72627de1d66832e4270e36e56acdb0d1d8f282"
|
"reference": "475b29ccd411f2fa8a408e64576418728c032cfa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/6c72627de1d66832e4270e36e56acdb0d1d8f282",
|
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/475b29ccd411f2fa8a408e64576418728c032cfa",
|
||||||
"reference": "6c72627de1d66832e4270e36e56acdb0d1d8f282",
|
"reference": "475b29ccd411f2fa8a408e64576418728c032cfa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -837,20 +837,20 @@
|
||||||
"rest",
|
"rest",
|
||||||
"web service"
|
"web service"
|
||||||
],
|
],
|
||||||
"time": "2014-11-04 07:09:15"
|
"time": "2015-01-28 01:03:29"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/ringphp",
|
"name": "guzzlehttp/ringphp",
|
||||||
"version": "1.0.3",
|
"version": "1.0.7",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/RingPHP.git",
|
"url": "https://github.com/guzzle/RingPHP.git",
|
||||||
"reference": "e7c28f96c5ac12ab0e63412cfc15989756fcb964"
|
"reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/e7c28f96c5ac12ab0e63412cfc15989756fcb964",
|
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/52d868f13570a9a56e5fce6614e0ec75d0f13ac2",
|
||||||
"reference": "e7c28f96c5ac12ab0e63412cfc15989756fcb964",
|
"reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -887,7 +887,8 @@
|
||||||
"homepage": "https://github.com/mtdowling"
|
"homepage": "https://github.com/mtdowling"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2014-11-04 07:01:14"
|
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
|
||||||
|
"time": "2015-03-30 01:43:20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/streams",
|
"name": "guzzlehttp/streams",
|
||||||
|
|
|
@ -1218,66 +1218,6 @@
|
||||||
"templating"
|
"templating"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "guzzlehttp/guzzle",
|
|
||||||
"version": "5.0.3",
|
|
||||||
"version_normalized": "5.0.3.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/guzzle/guzzle.git",
|
|
||||||
"reference": "6c72627de1d66832e4270e36e56acdb0d1d8f282"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/6c72627de1d66832e4270e36e56acdb0d1d8f282",
|
|
||||||
"reference": "6c72627de1d66832e4270e36e56acdb0d1d8f282",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"guzzlehttp/ringphp": "~1.0",
|
|
||||||
"php": ">=5.4.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"ext-curl": "*",
|
|
||||||
"phpunit/phpunit": "~4.0",
|
|
||||||
"psr/log": "~1.0"
|
|
||||||
},
|
|
||||||
"time": "2014-11-04 07:09:15",
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "5.0-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"installation-source": "dist",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"GuzzleHttp\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Michael Dowling",
|
|
||||||
"email": "mtdowling@gmail.com",
|
|
||||||
"homepage": "https://github.com/mtdowling"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
|
|
||||||
"homepage": "http://guzzlephp.org/",
|
|
||||||
"keywords": [
|
|
||||||
"client",
|
|
||||||
"curl",
|
|
||||||
"framework",
|
|
||||||
"http",
|
|
||||||
"http client",
|
|
||||||
"rest",
|
|
||||||
"web service"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "egulias/email-validator",
|
"name": "egulias/email-validator",
|
||||||
"version": "1.2.5",
|
"version": "1.2.5",
|
||||||
|
@ -1382,58 +1322,6 @@
|
||||||
"hhvm"
|
"hhvm"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "guzzlehttp/ringphp",
|
|
||||||
"version": "1.0.3",
|
|
||||||
"version_normalized": "1.0.3.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/guzzle/RingPHP.git",
|
|
||||||
"reference": "e7c28f96c5ac12ab0e63412cfc15989756fcb964"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/e7c28f96c5ac12ab0e63412cfc15989756fcb964",
|
|
||||||
"reference": "e7c28f96c5ac12ab0e63412cfc15989756fcb964",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"guzzlehttp/streams": "~3.0",
|
|
||||||
"php": ">=5.4.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"
|
|
||||||
},
|
|
||||||
"time": "2014-11-04 07:01:14",
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.0-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"installation-source": "dist",
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"GuzzleHttp\\Ring\\": "src/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Michael Dowling",
|
|
||||||
"email": "mtdowling@gmail.com",
|
|
||||||
"homepage": "https://github.com/mtdowling"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "easyrdf/easyrdf",
|
"name": "easyrdf/easyrdf",
|
||||||
"version": "0.9.0",
|
"version": "0.9.0",
|
||||||
|
@ -3156,5 +3044,118 @@
|
||||||
],
|
],
|
||||||
"description": "Symfony BrowserKit Component",
|
"description": "Symfony BrowserKit Component",
|
||||||
"homepage": "http://symfony.com"
|
"homepage": "http://symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/guzzle",
|
||||||
|
"version": "5.2.0",
|
||||||
|
"version_normalized": "5.2.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/guzzle.git",
|
||||||
|
"reference": "475b29ccd411f2fa8a408e64576418728c032cfa"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/475b29ccd411f2fa8a408e64576418728c032cfa",
|
||||||
|
"reference": "475b29ccd411f2fa8a408e64576418728c032cfa",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"guzzlehttp/ringphp": "~1.0",
|
||||||
|
"php": ">=5.4.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"ext-curl": "*",
|
||||||
|
"phpunit/phpunit": "~4.0",
|
||||||
|
"psr/log": "~1.0"
|
||||||
|
},
|
||||||
|
"time": "2015-01-28 01:03:29",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "5.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
|
||||||
|
"homepage": "http://guzzlephp.org/",
|
||||||
|
"keywords": [
|
||||||
|
"client",
|
||||||
|
"curl",
|
||||||
|
"framework",
|
||||||
|
"http",
|
||||||
|
"http client",
|
||||||
|
"rest",
|
||||||
|
"web service"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "guzzlehttp/ringphp",
|
||||||
|
"version": "1.0.7",
|
||||||
|
"version_normalized": "1.0.7.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/guzzle/RingPHP.git",
|
||||||
|
"reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/52d868f13570a9a56e5fce6614e0ec75d0f13ac2",
|
||||||
|
"reference": "52d868f13570a9a56e5fce6614e0ec75d0f13ac2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"guzzlehttp/streams": "~3.0",
|
||||||
|
"php": ">=5.4.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"
|
||||||
|
},
|
||||||
|
"time": "2015-03-30 01:43:20",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "1.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"GuzzleHttp\\Ring\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Michael Dowling",
|
||||||
|
"email": "mtdowling@gmail.com",
|
||||||
|
"homepage": "https://github.com/mtdowling"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,35 @@
|
||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## 5.2.0 - 2015-01-27
|
||||||
|
|
||||||
|
* Added `AppliesHeadersInterface` to make applying headers to a request based
|
||||||
|
on the body more generic and not specific to `PostBodyInterface`.
|
||||||
|
* Reduced the number of stack frames needed to send requests.
|
||||||
|
* Nested futures are now resolved in the client rather than the RequestFsm
|
||||||
|
* Finishing state transitions is now handled in the RequestFsm rather than the
|
||||||
|
RingBridge.
|
||||||
|
* Added a guard in the Pool class to not use recursion for request retries.
|
||||||
|
|
||||||
|
## 5.1.0 - 2014-12-19
|
||||||
|
|
||||||
|
* Pool class no longer uses recursion when a request is intercepted.
|
||||||
|
* The size of a Pool can now be dynamically adjusted using a callback.
|
||||||
|
See https://github.com/guzzle/guzzle/pull/943.
|
||||||
|
* Setting a request option to `null` when creating a request with a client will
|
||||||
|
ensure that the option is not set. This allows you to overwrite default
|
||||||
|
request options on a per-request basis.
|
||||||
|
See https://github.com/guzzle/guzzle/pull/937.
|
||||||
|
* Added the ability to limit which protocols are allowed for redirects by
|
||||||
|
specifying a `protocols` array in the `allow_redirects` request option.
|
||||||
|
* Nested futures due to retries are now resolved when waiting for synchronous
|
||||||
|
responses. See https://github.com/guzzle/guzzle/pull/947.
|
||||||
|
* `"0"` is now an allowed URI path. See
|
||||||
|
https://github.com/guzzle/guzzle/pull/935.
|
||||||
|
* `Query` no longer typehints on the `$query` argument in the constructor,
|
||||||
|
allowing for strings and arrays.
|
||||||
|
* Exceptions thrown in the `end` event are now correctly wrapped with Guzzle
|
||||||
|
specific exceptions if necessary.
|
||||||
|
|
||||||
## 5.0.3 - 2014-11-03
|
## 5.0.3 - 2014-11-03
|
||||||
|
|
||||||
This change updates query strings so that they are treated as un-encoded values
|
This change updates query strings so that they are treated as un-encoded values
|
||||||
|
@ -71,10 +101,10 @@ The breaking changes in this release are relatively minor. The biggest thing to
|
||||||
look out for is that request and response objects no longer implement fluent
|
look out for is that request and response objects no longer implement fluent
|
||||||
interfaces.
|
interfaces.
|
||||||
|
|
||||||
* Removed the fluent interfaces (i.e., ``return $this``) from requests,
|
* Removed the fluent interfaces (i.e., `return $this`) from requests,
|
||||||
responses, ``GuzzleHttp\Collection``, ``GuzzleHttp\Url``,
|
responses, `GuzzleHttp\Collection`, `GuzzleHttp\Url`,
|
||||||
``GuzzleHttp\Query``, ``GuzzleHttp\Post\PostBody``, and
|
`GuzzleHttp\Query`, `GuzzleHttp\Post\PostBody`, and
|
||||||
``GuzzleHttp\Cookie\SetCookie``. This blog post provides a good outline of
|
`GuzzleHttp\Cookie\SetCookie`. This blog post provides a good outline of
|
||||||
why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/.
|
why I did this: http://ocramius.github.io/blog/fluent-interfaces-are-evil/.
|
||||||
This also makes the Guzzle message interfaces compatible with the current
|
This also makes the Guzzle message interfaces compatible with the current
|
||||||
PSR-7 message proposal.
|
PSR-7 message proposal.
|
||||||
|
@ -84,7 +114,7 @@ interfaces.
|
||||||
moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to
|
moved to `GuzzleHttp\Utils::jsonDecode`. `GuzzleHttp\get_path` moved to
|
||||||
`GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to
|
`GuzzleHttp\Utils::getPath`. `GuzzleHttp\set_path` moved to
|
||||||
`GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be
|
`GuzzleHttp\Utils::setPath`. `GuzzleHttp\batch` should now be
|
||||||
`GuzzleHttp\Pool::batch`, which returns a bjectStorage`. Using functions.php
|
`GuzzleHttp\Pool::batch`, which returns an `objectStorage`. Using functions.php
|
||||||
caused problems for many users: they aren't PSR-4 compliant, require an
|
caused problems for many users: they aren't PSR-4 compliant, require an
|
||||||
explicit include, and needed an if-guard to ensure that the functions are not
|
explicit include, and needed an if-guard to ensure that the functions are not
|
||||||
declared multiple times.
|
declared multiple times.
|
||||||
|
@ -105,10 +135,10 @@ interfaces.
|
||||||
written to.
|
written to.
|
||||||
* Removed the `asArray` parameter from
|
* Removed the `asArray` parameter from
|
||||||
`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
|
`GuzzleHttp\Message\MessageInterface::getHeader`. If you want to get a header
|
||||||
value as an array, then use the newly added ``getHeaderAsArray()`` method of
|
value as an array, then use the newly added `getHeaderAsArray()` method of
|
||||||
``MessageInterface``. This change makes the Guzzle interfaces compatible with
|
`MessageInterface`. This change makes the Guzzle interfaces compatible with
|
||||||
the PSR-7 interfaces.
|
the PSR-7 interfaces.
|
||||||
* ``GuzzleHttp\Message\MessageFactory`` no longer allows subclasses to add
|
* `GuzzleHttp\Message\MessageFactory` no longer allows subclasses to add
|
||||||
custom request options using double-dispatch (this was an implementation
|
custom request options using double-dispatch (this was an implementation
|
||||||
detail). Instead, you should now provide an associative array to the
|
detail). Instead, you should now provide an associative array to the
|
||||||
constructor which is a mapping of the request option name mapping to a
|
constructor which is a mapping of the request option name mapping to a
|
||||||
|
@ -121,9 +151,9 @@ interfaces.
|
||||||
* `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a
|
* `GuzzleHttp\Stream\StreamInterface::getContents()` no longer accepts a
|
||||||
`maxLen` parameter. This update makes the Guzzle streams project
|
`maxLen` parameter. This update makes the Guzzle streams project
|
||||||
compatible with the current PSR-7 proposal.
|
compatible with the current PSR-7 proposal.
|
||||||
* ``GuzzleHttp\Stream\Stream::__construct``,
|
* `GuzzleHttp\Stream\Stream::__construct`,
|
||||||
``GuzzleHttp\Stream\Stream::factory``, and
|
`GuzzleHttp\Stream\Stream::factory`, and
|
||||||
``GuzzleHttp\Stream\Utils::create`` no longer accept a size in the second
|
`GuzzleHttp\Stream\Utils::create` no longer accept a size in the second
|
||||||
argument. They now accept an associative array of options, including the
|
argument. They now accept an associative array of options, including the
|
||||||
"size" key and "metadata" key which can be used to provide custom metadata.
|
"size" key and "metadata" key which can be used to provide custom metadata.
|
||||||
|
|
||||||
|
@ -359,7 +389,7 @@ interfaces.
|
||||||
* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
|
* Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`.
|
||||||
* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
|
* Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody()
|
||||||
* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
|
* Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin
|
||||||
* Bug fix: Visiting XML attributes first before visting XML children when serializing requests
|
* Bug fix: Visiting XML attributes first before visiting XML children when serializing requests
|
||||||
* Bug fix: Properly parsing headers that contain commas contained in quotes
|
* Bug fix: Properly parsing headers that contain commas contained in quotes
|
||||||
* Bug fix: mimetype guessing based on a filename is now case-insensitive
|
* Bug fix: mimetype guessing based on a filename is now case-insensitive
|
||||||
|
|
||||||
|
@ -502,7 +532,7 @@ interfaces.
|
||||||
directly via interfaces
|
directly via interfaces
|
||||||
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
|
* Removed the injecting of a request object onto a response object. The methods to get and set a request still exist
|
||||||
but are a no-op until removed.
|
but are a no-op until removed.
|
||||||
* Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a
|
* Most classes that used to require a `Guzzle\Service\Command\CommandInterface` typehint now request a
|
||||||
`Guzzle\Service\Command\ArrayCommandInterface`.
|
`Guzzle\Service\Command\ArrayCommandInterface`.
|
||||||
* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
|
* Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response
|
||||||
on a request while the request is still being transferred
|
on a request while the request is still being transferred
|
||||||
|
@ -517,7 +547,7 @@ interfaces.
|
||||||
## 3.5.0 - 2013-05-13
|
## 3.5.0 - 2013-05-13
|
||||||
|
|
||||||
* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
|
* Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times
|
||||||
* Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove
|
* Bug: Better cleanup of one-time events across the board (when an event is meant to fire once, it will now remove
|
||||||
itself from the EventDispatcher)
|
itself from the EventDispatcher)
|
||||||
* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
|
* Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values
|
||||||
* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
|
* Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too
|
||||||
|
@ -833,15 +863,15 @@ interfaces.
|
||||||
* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
|
* Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body
|
||||||
* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
|
* Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new
|
||||||
* Added multiple inheritance to service description commands
|
* Added multiple inheritance to service description commands
|
||||||
* Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()``
|
* Added an ApiCommandInterface and added `getParamNames()` and `hasParam()`
|
||||||
* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
|
* Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything
|
||||||
* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles
|
* Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles
|
||||||
|
|
||||||
## 2.8.2 - 2012-07-24
|
## 2.8.2 - 2012-07-24
|
||||||
|
|
||||||
* Bug: Query string values set to 0 are no longer dropped from the query string
|
* Bug: Query string values set to 0 are no longer dropped from the query string
|
||||||
* Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()``
|
* Bug: A Collection object is no longer created each time a call is made to `Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`
|
||||||
* Bug: ``+`` is now treated as an encoded space when parsing query strings
|
* Bug: `+` is now treated as an encoded space when parsing query strings
|
||||||
* QueryString and Collection performance improvements
|
* QueryString and Collection performance improvements
|
||||||
* Allowing dot notation for class paths in filters attribute of a service descriptions
|
* Allowing dot notation for class paths in filters attribute of a service descriptions
|
||||||
|
|
||||||
|
@ -858,7 +888,7 @@ interfaces.
|
||||||
* Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
|
* Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool)
|
||||||
* Changed the aggregation functions of QueryString to be static methods
|
* Changed the aggregation functions of QueryString to be static methods
|
||||||
* Can now use fromString() with querystrings that have a leading ?
|
* Can now use fromString() with querystrings that have a leading ?
|
||||||
* cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters
|
* cURL configuration values can be specified in service descriptions using `curl.` prefixed parameters
|
||||||
* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
|
* Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body
|
||||||
* Cookies are no longer URL decoded by default
|
* Cookies are no longer URL decoded by default
|
||||||
* Bug: URI template variables set to null are no longer expanded
|
* Bug: URI template variables set to null are no longer expanded
|
||||||
|
|
|
@ -503,7 +503,7 @@ allow developers to more easily extend and decorate stream behavior.
|
||||||
## Metadata streams
|
## Metadata streams
|
||||||
|
|
||||||
`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
|
`GuzzleHttp\Stream\MetadataStreamInterface` has been added to denote streams
|
||||||
that contain additonal metadata accessible via `getMetadata()`.
|
that contain additional metadata accessible via `getMetadata()`.
|
||||||
`GuzzleHttp\Stream\StreamInterface::getMetadata` and
|
`GuzzleHttp\Stream\StreamInterface::getMetadata` and
|
||||||
`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
|
`GuzzleHttp\Stream\StreamInterface::setMetadata` have been removed.
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ foreach (['README.md', 'LICENSE'] as $file) {
|
||||||
|
|
||||||
// Copy each dependency to the staging directory. Copy *.php and *.pem files.
|
// Copy each dependency to the staging directory. Copy *.php and *.pem files.
|
||||||
$packager->recursiveCopy('src', 'GuzzleHttp', ['php']);
|
$packager->recursiveCopy('src', 'GuzzleHttp', ['php']);
|
||||||
$packager->recursiveCopy('vendor/react/promise/src', '');
|
$packager->recursiveCopy('vendor/react/promise/src', 'React/Promise');
|
||||||
$packager->recursiveCopy('vendor/guzzlehttp/ringphp/src', 'GuzzleHttp/Ring');
|
$packager->recursiveCopy('vendor/guzzlehttp/ringphp/src', 'GuzzleHttp/Ring');
|
||||||
$packager->recursiveCopy('vendor/guzzlehttp/streams/src', 'GuzzleHttp/Stream');
|
$packager->recursiveCopy('vendor/guzzlehttp/streams/src', 'GuzzleHttp/Stream');
|
||||||
$packager->createAutoloader(['React/Promise/functions.php']);
|
$packager->createAutoloader(['React/Promise/functions.php']);
|
||||||
|
|
|
@ -173,6 +173,12 @@ response has completed.
|
||||||
If an exception occurred while transferring the future response, then the
|
If an exception occurred while transferring the future response, then the
|
||||||
exception encountered will be thrown when dereferencing.
|
exception encountered will be thrown when dereferencing.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
It depends on the RingPHP handler used by a client, but you typically need
|
||||||
|
to use the same RingPHP handler in order to utilize asynchronous requests
|
||||||
|
across multiple clients.
|
||||||
|
|
||||||
Asynchronous Error Handling
|
Asynchronous Error Handling
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -524,7 +530,7 @@ json
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$request = $client->createRequest('/put', ['json' => ['foo' => 'bar']]);
|
$request = $client->createRequest('PUT', '/put', ['json' => ['foo' => 'bar']]);
|
||||||
echo $request->getHeader('Content-Type');
|
echo $request->getHeader('Content-Type');
|
||||||
// application/json
|
// application/json
|
||||||
echo $request->getBody();
|
echo $request->getBody();
|
||||||
|
@ -701,7 +707,15 @@ allow_redirects
|
||||||
:Types:
|
:Types:
|
||||||
- bool
|
- bool
|
||||||
- array
|
- array
|
||||||
:Default: ``['max' => 5, 'strict' => false, 'referer' => true]``
|
:Default:
|
||||||
|
::
|
||||||
|
|
||||||
|
[
|
||||||
|
'max' => 5,
|
||||||
|
'strict' => false,
|
||||||
|
'referer' => true,
|
||||||
|
'protocols' => ['http', 'https']
|
||||||
|
]
|
||||||
|
|
||||||
Set to ``false`` to disable redirects.
|
Set to ``false`` to disable redirects.
|
||||||
|
|
||||||
|
@ -721,19 +735,22 @@ number of 5 redirects.
|
||||||
// 200
|
// 200
|
||||||
|
|
||||||
Pass an associative array containing the 'max' key to specify the maximum
|
Pass an associative array containing the 'max' key to specify the maximum
|
||||||
number of redirects, optionally provide a 'strict' key value to specify
|
number of redirects, provide a 'strict' key value to specify whether or not to
|
||||||
whether or not to use strict RFC compliant redirects (meaning redirect POST
|
use strict RFC compliant redirects (meaning redirect POST requests with POST
|
||||||
requests with POST requests vs. doing what most browsers do which is redirect
|
requests vs. doing what most browsers do which is redirect POST requests with
|
||||||
POST requests with GET requests), and optionally provide a 'referer' key to
|
GET requests), provide a 'referer' key to specify whether or not the "Referer"
|
||||||
specify whether or not the "Referer" header should be added when redirecting.
|
header should be added when redirecting, and provide a 'protocols' array that
|
||||||
|
specifies which protocols are supported for redirects (defaults to
|
||||||
|
``['http', 'https']``).
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$res = $client->get('/redirect/3', [
|
$res = $client->get('/redirect/3', [
|
||||||
'allow_redirects' => [
|
'allow_redirects' => [
|
||||||
'max' => 10,
|
'max' => 10, // allow at most 10 redirects.
|
||||||
'strict' => true,
|
'strict' => true, // use "strict" RFC compliant redirects.
|
||||||
'referer' => true
|
'referer' => true, // add a Referer header
|
||||||
|
'protocols' => ['https'] // only allow https URLs
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
echo $res->getStatusCode();
|
echo $res->getStatusCode();
|
||||||
|
|
|
@ -11,12 +11,12 @@ Event Emitters
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Clients, requests, and any other class that implements the
|
Clients, requests, and any other class that implements the
|
||||||
``GuzzleHttp\Common\HasEmitterInterface`` interface have a
|
``GuzzleHttp\Event\HasEmitterInterface`` interface have a
|
||||||
``GuzzleHttp\Common\EventEmitter`` object. You can add event *listeners* and
|
``GuzzleHttp\Event\Emitter`` object. You can add event *listeners* and
|
||||||
event *subscribers* to an event *emitter*.
|
event *subscribers* to an event *emitter*.
|
||||||
|
|
||||||
emitter
|
emitter
|
||||||
An object that implements ``GuzzleHttp\Common\EventEmitterInterface``. This
|
An object that implements ``GuzzleHttp\Event\EmitterInterface``. This
|
||||||
object emits named events to event listeners. You may register event
|
object emits named events to event listeners. You may register event
|
||||||
listeners on subscribers on an emitter.
|
listeners on subscribers on an emitter.
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ propagation
|
||||||
Getting an EventEmitter
|
Getting an EventEmitter
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
You can get the event emitter of ``GuzzleHttp\Common\HasEmitterInterface``
|
You can get the event emitter of ``GuzzleHttp\Event\HasEmitterInterface``
|
||||||
object using the the ``getEmitter()`` method. Here's an example of getting a
|
object using the the ``getEmitter()`` method. Here's an example of getting a
|
||||||
client object's event emitter.
|
client object's event emitter.
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ event is triggered, and optionally provide a priority.
|
||||||
});
|
});
|
||||||
|
|
||||||
When a listener is triggered, it is passed an event that implements the
|
When a listener is triggered, it is passed an event that implements the
|
||||||
``GuzzleHttp\Common\EventInterface`` interface, the name of the event, and the
|
``GuzzleHttp\Event\EventInterface`` interface, the name of the event, and the
|
||||||
event emitter itself. The above example could more verbosely be written as
|
event emitter itself. The above example could more verbosely be written as
|
||||||
follows:
|
follows:
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ state. This technique is used in Guzzle extensively when intercepting error
|
||||||
events with responses.
|
events with responses.
|
||||||
|
|
||||||
You can stop the propagation of an event using the ``stopPropagation()`` method
|
You can stop the propagation of an event using the ``stopPropagation()`` method
|
||||||
of a ``GuzzleHttp\Common\EventInterface`` object:
|
of a ``GuzzleHttp\Event\EventInterface`` object:
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ Event Subscribers
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Event subscribers are classes that implement the
|
Event subscribers are classes that implement the
|
||||||
``GuzzleHttp\Common\EventSubscriberInterface`` object. They are used to register
|
``GuzzleHttp\Event\SubscriberInterface`` object. They are used to register
|
||||||
one or more event listeners to methods of the class. Event subscribers tell
|
one or more event listeners to methods of the class. Event subscribers tell
|
||||||
event emitters exactly which events to listen to and what method to invoke on
|
event emitters exactly which events to listen to and what method to invoke on
|
||||||
the class when the event is triggered by called the ``getEvents()`` method of
|
the class when the event is triggered by called the ``getEvents()`` method of
|
||||||
|
@ -213,6 +213,18 @@ priority of the listener (as shown in the ``before`` listener in the example).
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
To register the listeners the subscriber needs to be attached to the emitter:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$client = new GuzzleHttp\Client();
|
||||||
|
$emitter = $client->getEmitter();
|
||||||
|
$subscriber = new SimpleSubscriber();
|
||||||
|
$emitter->attach($subscriber);
|
||||||
|
|
||||||
|
//to remove the listeners
|
||||||
|
$emitter->detach($subscriber);
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
You can specify event priorities using integers or ``"first"`` and
|
You can specify event priorities using integers or ``"first"`` and
|
||||||
|
@ -315,7 +327,7 @@ a ``GuzzleHttp\Event\BeforeEvent``.
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\Common\EmitterInterface;
|
use GuzzleHttp\Event\EmitterInterface;
|
||||||
use GuzzleHttp\Event\BeforeEvent;
|
use GuzzleHttp\Event\BeforeEvent;
|
||||||
|
|
||||||
$client = new Client(['base_url' => 'http://httpbin.org']);
|
$client = new Client(['base_url' => 'http://httpbin.org']);
|
||||||
|
@ -480,7 +492,7 @@ end
|
||||||
---
|
---
|
||||||
|
|
||||||
The ``end`` event is a terminal event, emitted once per request, that provides
|
The ``end`` event is a terminal event, emitted once per request, that provides
|
||||||
access to the repsonse that was received or the exception that was encountered.
|
access to the response that was received or the exception that was encountered.
|
||||||
The event emitted is a ``GuzzleHttp\Event\EndEvent``.
|
The event emitted is a ``GuzzleHttp\Event\EndEvent``.
|
||||||
|
|
||||||
This event can be intercepted, but keep in mind that the ``complete`` event
|
This event can be intercepted, but keep in mind that the ``complete`` event
|
||||||
|
|
|
@ -64,7 +64,7 @@ an HTTP response into a more meaningful model object.
|
||||||
- `Guzzle Command <https://github.com/guzzle/command>`_: Provides the building
|
- `Guzzle Command <https://github.com/guzzle/command>`_: Provides the building
|
||||||
blocks for service description abstraction.
|
blocks for service description abstraction.
|
||||||
- `Guzzle Services <https://github.com/guzzle/guzzle-services>`_: Provides an
|
- `Guzzle Services <https://github.com/guzzle/guzzle-services>`_: Provides an
|
||||||
implementation of "Guzzle Command" that utlizes Guzzle's service description
|
implementation of "Guzzle Command" that utilizes Guzzle's service description
|
||||||
format.
|
format.
|
||||||
|
|
||||||
Does Guzzle require cURL?
|
Does Guzzle require cURL?
|
||||||
|
@ -91,7 +91,7 @@ Can Guzzle send asynchronous requests?
|
||||||
Yes. Pass the ``future`` true request option to a request to send it
|
Yes. Pass the ``future`` true request option to a request to send it
|
||||||
asynchronously. Guzzle will then return a ``GuzzleHttp\Message\FutureResponse``
|
asynchronously. Guzzle will then return a ``GuzzleHttp\Message\FutureResponse``
|
||||||
object that can be used synchronously by accessing the response object like a
|
object that can be used synchronously by accessing the response object like a
|
||||||
normal response, and it can be used asynchronoulsy using a promise that is
|
normal response, and it can be used asynchronously using a promise that is
|
||||||
notified when the response is resolved with a real response or rejected with an
|
notified when the response is resolved with a real response or rejected with an
|
||||||
exception.
|
exception.
|
||||||
|
|
||||||
|
|
|
@ -351,7 +351,7 @@ method of a request.
|
||||||
$request = $client->createRequest('GET', '/');
|
$request = $client->createRequest('GET', '/');
|
||||||
$config = $request->getConfig();
|
$config = $request->getConfig();
|
||||||
|
|
||||||
The config object is a ``GuzzleHttp\Common\Collection`` object that acts like
|
The config object is a ``GuzzleHttp\Collection`` object that acts like
|
||||||
an associative array. You can grab values from the collection using array like
|
an associative array. You can grab values from the collection using array like
|
||||||
access. You can also modify and remove values using array like access.
|
access. You can also modify and remove values using array like access.
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ allow customization through request configuration options.
|
||||||
Event Emitter
|
Event Emitter
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Request objects implement ``GuzzleHttp\Common\HasEmitterInterface``, so they
|
Request objects implement ``GuzzleHttp\Event\HasEmitterInterface``, so they
|
||||||
have a method called ``getEmitter()`` that can be used to get an event emitter
|
have a method called ``getEmitter()`` that can be used to get an event emitter
|
||||||
used by the request. Any listener or subscriber attached to a request will only
|
used by the request. Any listener or subscriber attached to a request will only
|
||||||
be triggered for the lifecycle events of a specific request. Conversely, adding
|
be triggered for the lifecycle events of a specific request. Conversely, adding
|
||||||
|
|
|
@ -177,26 +177,14 @@ class Client implements ClientInterface
|
||||||
|
|
||||||
public function createRequest($method, $url = null, array $options = [])
|
public function createRequest($method, $url = null, array $options = [])
|
||||||
{
|
{
|
||||||
$headers = $this->mergeDefaults($options);
|
$options = $this->mergeDefaults($options);
|
||||||
// Use a clone of the client's emitter
|
// Use a clone of the client's emitter
|
||||||
$options['config']['emitter'] = clone $this->getEmitter();
|
$options['config']['emitter'] = clone $this->getEmitter();
|
||||||
|
$url = $url || (is_string($url) && strlen($url))
|
||||||
|
? $this->buildUrl($url)
|
||||||
|
: (string) $this->baseUrl;
|
||||||
|
|
||||||
$request = $this->messageFactory->createRequest(
|
return $this->messageFactory->createRequest($method, $url, $options);
|
||||||
$method,
|
|
||||||
$url ? (string) $this->buildUrl($url) : (string) $this->baseUrl,
|
|
||||||
$options
|
|
||||||
);
|
|
||||||
|
|
||||||
// Merge in default headers
|
|
||||||
if ($headers) {
|
|
||||||
foreach ($headers as $key => $value) {
|
|
||||||
if (!$request->hasHeader($key)) {
|
|
||||||
$request->setHeader($key, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($url = null, $options = [])
|
public function get($url = null, $options = [])
|
||||||
|
@ -236,30 +224,31 @@ class Client implements ClientInterface
|
||||||
|
|
||||||
public function send(RequestInterface $request)
|
public function send(RequestInterface $request)
|
||||||
{
|
{
|
||||||
$trans = new Transaction($this, $request);
|
$isFuture = $request->getConfig()->get('future');
|
||||||
|
$trans = new Transaction($this, $request, $isFuture);
|
||||||
$fn = $this->fsm;
|
$fn = $this->fsm;
|
||||||
|
|
||||||
// Ensure a future response is returned if one was requested.
|
try {
|
||||||
if ($request->getConfig()->get('future')) {
|
$fn($trans);
|
||||||
try {
|
if ($isFuture) {
|
||||||
$fn($trans);
|
|
||||||
// Turn the normal response into a future if needed.
|
// Turn the normal response into a future if needed.
|
||||||
return $trans->response instanceof FutureInterface
|
return $trans->response instanceof FutureInterface
|
||||||
? $trans->response
|
? $trans->response
|
||||||
: new FutureResponse(new FulfilledPromise($trans->response));
|
: new FutureResponse(new FulfilledPromise($trans->response));
|
||||||
} catch (RequestException $e) {
|
}
|
||||||
// Wrap the exception in a promise if the user asked for a future.
|
// Resolve deep futures if this is not a future
|
||||||
|
// transaction. This accounts for things like retries
|
||||||
|
// that do not have an immediate side-effect.
|
||||||
|
while ($trans->response instanceof FutureInterface) {
|
||||||
|
$trans->response = $trans->response->wait();
|
||||||
|
}
|
||||||
|
return $trans->response;
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
if ($isFuture) {
|
||||||
|
// Wrap the exception in a promise
|
||||||
return new FutureResponse(new RejectedPromise($e));
|
return new FutureResponse(new RejectedPromise($e));
|
||||||
}
|
}
|
||||||
} else {
|
throw RequestException::wrapException($trans->request, $e);
|
||||||
try {
|
|
||||||
$fn($trans);
|
|
||||||
return $trans->response instanceof FutureInterface
|
|
||||||
? $trans->response->wait()
|
|
||||||
: $trans->response;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
throw RequestException::wrapException($trans->request, $e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,9 +281,10 @@ class Client implements ClientInterface
|
||||||
/**
|
/**
|
||||||
* Expand a URI template and inherit from the base URL if it's relative
|
* Expand a URI template and inherit from the base URL if it's relative
|
||||||
*
|
*
|
||||||
* @param string|array $url URL or URI template to expand
|
* @param string|array $url URL or an array of the URI template to expand
|
||||||
*
|
* followed by a hash of template varnames.
|
||||||
* @return string
|
* @return string
|
||||||
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
private function buildUrl($url)
|
private function buildUrl($url)
|
||||||
{
|
{
|
||||||
|
@ -305,6 +295,11 @@ class Client implements ClientInterface
|
||||||
: (string) $this->baseUrl->combine($url);
|
: (string) $this->baseUrl->combine($url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($url[1])) {
|
||||||
|
throw new \InvalidArgumentException('You must provide a hash of '
|
||||||
|
. 'varname options in the second element of a URL array.');
|
||||||
|
}
|
||||||
|
|
||||||
// Absolute URL
|
// Absolute URL
|
||||||
if (strpos($url[0], '://')) {
|
if (strpos($url[0], '://')) {
|
||||||
return Utils::uriTemplate($url[0], $url[1]);
|
return Utils::uriTemplate($url[0], $url[1]);
|
||||||
|
@ -320,7 +315,12 @@ class Client implements ClientInterface
|
||||||
{
|
{
|
||||||
if (!isset($config['base_url'])) {
|
if (!isset($config['base_url'])) {
|
||||||
$this->baseUrl = new Url('', '');
|
$this->baseUrl = new Url('', '');
|
||||||
} elseif (is_array($config['base_url'])) {
|
} elseif (!is_array($config['base_url'])) {
|
||||||
|
$this->baseUrl = Url::fromString($config['base_url']);
|
||||||
|
} elseif (count($config['base_url']) < 2) {
|
||||||
|
throw new \InvalidArgumentException('You must provide a hash of '
|
||||||
|
. 'varname options in the second element of a base_url array.');
|
||||||
|
} else {
|
||||||
$this->baseUrl = Url::fromString(
|
$this->baseUrl = Url::fromString(
|
||||||
Utils::uriTemplate(
|
Utils::uriTemplate(
|
||||||
$config['base_url'][0],
|
$config['base_url'][0],
|
||||||
|
@ -328,8 +328,6 @@ class Client implements ClientInterface
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$config['base_url'] = (string) $this->baseUrl;
|
$config['base_url'] = (string) $this->baseUrl;
|
||||||
} else {
|
|
||||||
$this->baseUrl = Url::fromString($config['base_url']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,27 +354,42 @@ class Client implements ClientInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges default options into the array passed by reference and returns
|
* Merges default options into the array passed by reference.
|
||||||
* an array of headers that need to be merged in after the request is
|
|
||||||
* created.
|
|
||||||
*
|
*
|
||||||
* @param array $options Options to modify by reference
|
* @param array $options Options to modify by reference
|
||||||
*
|
*
|
||||||
* @return array|null
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function mergeDefaults(&$options)
|
private function mergeDefaults($options)
|
||||||
{
|
{
|
||||||
// Merging optimization for when no headers are present
|
$defaults = $this->defaults;
|
||||||
if (!isset($options['headers']) || !isset($this->defaults['headers'])) {
|
|
||||||
$options = array_replace_recursive($this->defaults, $options);
|
// Case-insensitively merge in default headers if both defaults and
|
||||||
return null;
|
// options have headers specified.
|
||||||
|
if (!empty($defaults['headers']) && !empty($options['headers'])) {
|
||||||
|
// Create a set of lowercased keys that are present.
|
||||||
|
$lkeys = [];
|
||||||
|
foreach (array_keys($options['headers']) as $k) {
|
||||||
|
$lkeys[strtolower($k)] = true;
|
||||||
|
}
|
||||||
|
// Merge in lowercase default keys when not present in above set.
|
||||||
|
foreach ($defaults['headers'] as $key => $value) {
|
||||||
|
if (!isset($lkeys[strtolower($key)])) {
|
||||||
|
$options['headers'][$key] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No longer need to merge in headers.
|
||||||
|
unset($defaults['headers']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaults = $this->defaults;
|
$result = array_replace_recursive($defaults, $options);
|
||||||
unset($defaults['headers']);
|
foreach ($options as $k => $v) {
|
||||||
$options = array_replace_recursive($defaults, $options);
|
if ($v === null) {
|
||||||
|
unset($result[$k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $this->defaults['headers'];
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -385,6 +398,6 @@ class Client implements ClientInterface
|
||||||
*/
|
*/
|
||||||
public function sendAll($requests, array $options = [])
|
public function sendAll($requests, array $options = [])
|
||||||
{
|
{
|
||||||
(new Pool($this, $requests, $options))->wait();
|
Pool::send($this, $requests, $options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use GuzzleHttp\Message\ResponseInterface;
|
||||||
*/
|
*/
|
||||||
interface ClientInterface extends HasEmitterInterface
|
interface ClientInterface extends HasEmitterInterface
|
||||||
{
|
{
|
||||||
const VERSION = '5.0.3';
|
const VERSION = '5.2.0';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and return a new {@see RequestInterface} object.
|
* Create and return a new {@see RequestInterface} object.
|
||||||
|
|
|
@ -41,6 +41,16 @@ abstract class AbstractRequestEvent extends AbstractEvent
|
||||||
return $this->transaction->request;
|
return $this->transaction->request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of transaction retries.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getRetryCount()
|
||||||
|
{
|
||||||
|
return $this->transaction->retries;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Transaction
|
* @return Transaction
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,9 +27,9 @@ class AbstractRetryableEvent extends AbstractTransferEvent
|
||||||
*/
|
*/
|
||||||
public function retry($afterDelay = 0)
|
public function retry($afterDelay = 0)
|
||||||
{
|
{
|
||||||
$this->transaction->response = null;
|
// Setting the transition state to 'retry' will cause the next state
|
||||||
$this->transaction->exception = null;
|
// transition of the transaction to retry the request.
|
||||||
$this->transaction->state = 'before';
|
$this->transaction->state = 'retry';
|
||||||
|
|
||||||
if ($afterDelay) {
|
if ($afterDelay) {
|
||||||
$this->transaction->request->getConfig()->set('delay', $afterDelay);
|
$this->transaction->request->getConfig()->set('delay', $afterDelay);
|
||||||
|
|
|
@ -20,11 +20,13 @@ abstract class AbstractTransferEvent extends AbstractRequestEvent
|
||||||
*/
|
*/
|
||||||
public function getTransferInfo($name = null)
|
public function getTransferInfo($name = null)
|
||||||
{
|
{
|
||||||
return !$name
|
if (!$name) {
|
||||||
? $this->transaction->transferInfo
|
return $this->transaction->transferInfo;
|
||||||
: (isset($this->transaction->transferInfo[$name])
|
}
|
||||||
? $this->transaction->transferInfo[$name]
|
|
||||||
: null);
|
return isset($this->transaction->transferInfo[$name])
|
||||||
|
? $this->transaction->transferInfo[$name]
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
namespace GuzzleHttp\Message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies headers to a request.
|
||||||
|
*
|
||||||
|
* This interface can be used with Guzzle streams to apply body specific
|
||||||
|
* headers to a request during the PREPARE_REQUEST priority of the before event
|
||||||
|
*
|
||||||
|
* NOTE: a body that implements this interface will prevent a default
|
||||||
|
* content-type from being added to a request during the before event. If you
|
||||||
|
* want a default content-type to be added, then it will need to be done
|
||||||
|
* manually (e.g., using {@see GuzzleHttp\Mimetypes}).
|
||||||
|
*/
|
||||||
|
interface AppliesHeadersInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Apply headers to a request appropriate for the current state of the
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @param RequestInterface $request Request
|
||||||
|
*/
|
||||||
|
public function applyRequestHeaders(RequestInterface $request);
|
||||||
|
}
|
|
@ -40,9 +40,10 @@ class MessageFactory implements MessageFactoryInterface
|
||||||
|
|
||||||
/** @var array Default allow_redirects request option settings */
|
/** @var array Default allow_redirects request option settings */
|
||||||
private static $defaultRedirect = [
|
private static $defaultRedirect = [
|
||||||
'max' => 5,
|
'max' => 5,
|
||||||
'strict' => false,
|
'strict' => false,
|
||||||
'referer' => false
|
'referer' => false,
|
||||||
|
'protocols' => ['http', 'https']
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -198,9 +199,8 @@ class MessageFactory implements MessageFactoryInterface
|
||||||
|
|
||||||
if ($value === true) {
|
if ($value === true) {
|
||||||
$value = self::$defaultRedirect;
|
$value = self::$defaultRedirect;
|
||||||
} elseif (!isset($value['max'])) {
|
} elseif (!is_array($value)) {
|
||||||
throw new Iae('allow_redirects must be true, false, or an '
|
throw new Iae('allow_redirects must be true, false, or array');
|
||||||
. 'array that contains the \'max\' key');
|
|
||||||
} else {
|
} else {
|
||||||
// Merge the default settings with the provided settings
|
// Merge the default settings with the provided settings
|
||||||
$value += self::$defaultRedirect;
|
$value += self::$defaultRedirect;
|
||||||
|
@ -227,12 +227,8 @@ class MessageFactory implements MessageFactoryInterface
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
throw new Iae('header value must be an array');
|
throw new Iae('header value must be an array');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not overwrite existing headers
|
|
||||||
foreach ($value as $k => $v) {
|
foreach ($value as $k => $v) {
|
||||||
if (!$request->hasHeader($k)) {
|
$request->setHeader($k, $v);
|
||||||
$request->setHeader($k, $v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
namespace GuzzleHttp;
|
namespace GuzzleHttp;
|
||||||
|
|
||||||
|
use GuzzleHttp\Event\BeforeEvent;
|
||||||
use GuzzleHttp\Event\RequestEvents;
|
use GuzzleHttp\Event\RequestEvents;
|
||||||
use GuzzleHttp\Message\RequestInterface;
|
use GuzzleHttp\Message\RequestInterface;
|
||||||
use GuzzleHttp\Message\ResponseInterface;
|
use GuzzleHttp\Message\ResponseInterface;
|
||||||
|
@ -9,7 +10,9 @@ use GuzzleHttp\Ring\Future\FutureInterface;
|
||||||
use GuzzleHttp\Event\ListenerAttacherTrait;
|
use GuzzleHttp\Event\ListenerAttacherTrait;
|
||||||
use GuzzleHttp\Event\EndEvent;
|
use GuzzleHttp\Event\EndEvent;
|
||||||
use React\Promise\Deferred;
|
use React\Promise\Deferred;
|
||||||
|
use React\Promise\FulfilledPromise;
|
||||||
use React\Promise\PromiseInterface;
|
use React\Promise\PromiseInterface;
|
||||||
|
use React\Promise\RejectedPromise;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends and iterator of requests concurrently using a capped pool size.
|
* Sends and iterator of requests concurrently using a capped pool size.
|
||||||
|
@ -50,9 +53,9 @@ class Pool implements FutureInterface
|
||||||
private $isRealized = false;
|
private $isRealized = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The option values for 'before', 'after', and 'error' can be a callable,
|
* The option values for 'before', 'complete', 'error' and 'end' can be a
|
||||||
* an associative array containing event data, or an array of event data
|
* callable, an associative array containing event data, or an array of
|
||||||
* arrays. Event data arrays contain the following keys:
|
* event data arrays. Event data arrays contain the following keys:
|
||||||
*
|
*
|
||||||
* - fn: callable to invoke that receives the event
|
* - fn: callable to invoke that receives the event
|
||||||
* - priority: Optional event priority (defaults to 0)
|
* - priority: Optional event priority (defaults to 0)
|
||||||
|
@ -61,10 +64,14 @@ class Pool implements FutureInterface
|
||||||
* @param ClientInterface $client Client used to send the requests.
|
* @param ClientInterface $client Client used to send the requests.
|
||||||
* @param array|\Iterator $requests Requests to send in parallel
|
* @param array|\Iterator $requests Requests to send in parallel
|
||||||
* @param array $options Associative array of options
|
* @param array $options Associative array of options
|
||||||
* - pool_size: (int) Maximum number of requests to send concurrently
|
* - pool_size: (callable|int) Maximum number of requests to send
|
||||||
|
* concurrently, or a callback that receives
|
||||||
|
* the current queue size and returns the
|
||||||
|
* number of new requests to send
|
||||||
* - before: (callable|array) Receives a BeforeEvent
|
* - before: (callable|array) Receives a BeforeEvent
|
||||||
* - after: (callable|array) Receives a CompleteEvent
|
* - complete: (callable|array) Receives a CompleteEvent
|
||||||
* - error: (callable|array) Receives a ErrorEvent
|
* - error: (callable|array) Receives a ErrorEvent
|
||||||
|
* - end: (callable|array) Receives an EndEvent
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ClientInterface $client,
|
ClientInterface $client,
|
||||||
|
@ -140,7 +147,28 @@ class Pool implements FutureInterface
|
||||||
$requests,
|
$requests,
|
||||||
array $options = []
|
array $options = []
|
||||||
) {
|
) {
|
||||||
(new self($client, $requests, $options))->wait();
|
$pool = new self($client, $requests, $options);
|
||||||
|
$pool->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPoolSize()
|
||||||
|
{
|
||||||
|
return is_callable($this->poolSize)
|
||||||
|
? call_user_func($this->poolSize, count($this->waitQueue))
|
||||||
|
: $this->poolSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add as many requests as possible up to the current pool limit.
|
||||||
|
*/
|
||||||
|
private function addNextRequests()
|
||||||
|
{
|
||||||
|
$limit = max($this->getPoolSize() - count($this->waitQueue), 0);
|
||||||
|
while ($limit--) {
|
||||||
|
if (!$this->addNextRequest()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function wait()
|
public function wait()
|
||||||
|
@ -150,11 +178,7 @@ class Pool implements FutureInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seed the pool with N number of requests.
|
// Seed the pool with N number of requests.
|
||||||
for ($i = 0; $i < $this->poolSize; $i++) {
|
$this->addNextRequests();
|
||||||
if (!$this->addNextRequest()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop if the pool was cancelled while transferring requests.
|
// Stop if the pool was cancelled while transferring requests.
|
||||||
if ($this->isRealized) {
|
if ($this->isRealized) {
|
||||||
|
@ -168,6 +192,7 @@ class Pool implements FutureInterface
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
// Eat exceptions because they should be handled asynchronously
|
// Eat exceptions because they should be handled asynchronously
|
||||||
}
|
}
|
||||||
|
$this->addNextRequests();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up no longer needed state.
|
// Clean up no longer needed state.
|
||||||
|
@ -241,6 +266,8 @@ class Pool implements FutureInterface
|
||||||
*/
|
*/
|
||||||
private function addNextRequest()
|
private function addNextRequest()
|
||||||
{
|
{
|
||||||
|
add_next:
|
||||||
|
|
||||||
if ($this->isRealized || !$this->iter || !$this->iter->valid()) {
|
if ($this->isRealized || !$this->iter || !$this->iter->valid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -258,23 +285,49 @@ class Pool implements FutureInterface
|
||||||
|
|
||||||
// Be sure to use "lazy" futures, meaning they do not send right away.
|
// Be sure to use "lazy" futures, meaning they do not send right away.
|
||||||
$request->getConfig()->set('future', 'lazy');
|
$request->getConfig()->set('future', 'lazy');
|
||||||
$this->attachListeners($request, $this->eventListeners);
|
|
||||||
$response = $this->client->send($request);
|
|
||||||
$hash = spl_object_hash($request);
|
$hash = spl_object_hash($request);
|
||||||
|
$this->attachListeners($request, $this->eventListeners);
|
||||||
|
$request->getEmitter()->on('before', [$this, '_trackRetries'], RequestEvents::EARLY);
|
||||||
|
$response = $this->client->send($request);
|
||||||
$this->waitQueue[$hash] = $response;
|
$this->waitQueue[$hash] = $response;
|
||||||
|
$promise = $response->promise();
|
||||||
|
|
||||||
|
// Don't recursively call itself for completed or rejected responses.
|
||||||
|
if ($promise instanceof FulfilledPromise
|
||||||
|
|| $promise instanceof RejectedPromise
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
$this->finishResponse($request, $response->wait(), $hash);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->finishResponse($request, $e, $hash);
|
||||||
|
}
|
||||||
|
goto add_next;
|
||||||
|
}
|
||||||
|
|
||||||
// Use this function for both resolution and rejection.
|
// Use this function for both resolution and rejection.
|
||||||
$fn = function ($value) use ($request, $hash) {
|
$thenFn = function ($value) use ($request, $hash) {
|
||||||
unset($this->waitQueue[$hash]);
|
$this->finishResponse($request, $value, $hash);
|
||||||
$result = $value instanceof ResponseInterface
|
if (!$request->getConfig()->get('_pool_retries')) {
|
||||||
? ['request' => $request, 'response' => $value, 'error' => null]
|
$this->addNextRequests();
|
||||||
: ['request' => $request, 'response' => null, 'error' => $value];
|
}
|
||||||
$this->deferred->progress($result);
|
|
||||||
$this->addNextRequest();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$response->then($fn, $fn);
|
$promise->then($thenFn, $thenFn);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function _trackRetries(BeforeEvent $e)
|
||||||
|
{
|
||||||
|
$e->getRequest()->getConfig()->set('_pool_retries', $e->getRetryCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function finishResponse($request, $value, $hash)
|
||||||
|
{
|
||||||
|
unset($this->waitQueue[$hash]);
|
||||||
|
$result = $value instanceof ResponseInterface
|
||||||
|
? ['request' => $request, 'response' => $value, 'error' => null]
|
||||||
|
: ['request' => $request, 'response' => null, 'error' => $value];
|
||||||
|
$this->deferred->progress($result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
namespace GuzzleHttp\Post;
|
namespace GuzzleHttp\Post;
|
||||||
|
|
||||||
use GuzzleHttp\Message\RequestInterface;
|
use GuzzleHttp\Message\AppliesHeadersInterface;
|
||||||
use GuzzleHttp\Stream\StreamInterface;
|
use GuzzleHttp\Stream\StreamInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a POST body that is sent as either a multipart/form-data stream
|
* Represents a POST body that is sent as either a multipart/form-data stream
|
||||||
* or application/x-www-urlencoded stream.
|
* or application/x-www-urlencoded stream.
|
||||||
*/
|
*/
|
||||||
interface PostBodyInterface extends StreamInterface, \Countable
|
interface PostBodyInterface extends StreamInterface, \Countable, AppliesHeadersInterface
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Apply headers to the request appropriate for the current state of the object
|
|
||||||
*
|
|
||||||
* @param RequestInterface $request Request
|
|
||||||
*/
|
|
||||||
public function applyRequestHeaders(RequestInterface $request);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a specific field
|
* Set a specific field
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,30 +20,6 @@ class RequestFsm
|
||||||
private $mf;
|
private $mf;
|
||||||
private $maxTransitions;
|
private $maxTransitions;
|
||||||
|
|
||||||
private $states = [
|
|
||||||
// When a mock intercepts the emitted "before" event, then we
|
|
||||||
// transition to the "complete" intercept state.
|
|
||||||
'before' => [
|
|
||||||
'success' => 'send',
|
|
||||||
'intercept' => 'complete',
|
|
||||||
'error' => 'error'
|
|
||||||
],
|
|
||||||
// The complete and error events are handled using the "then" of
|
|
||||||
// the RingPHP request, so we exit the FSM.
|
|
||||||
'send' => ['error' => 'error'],
|
|
||||||
'complete' => [
|
|
||||||
'success' => 'end',
|
|
||||||
'intercept' => 'before',
|
|
||||||
'error' => 'error'
|
|
||||||
],
|
|
||||||
'error' => [
|
|
||||||
'success' => 'complete',
|
|
||||||
'intercept' => 'before',
|
|
||||||
'error' => 'end'
|
|
||||||
],
|
|
||||||
'end' => []
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
callable $handler,
|
callable $handler,
|
||||||
MessageFactoryInterface $messageFactory,
|
MessageFactoryInterface $messageFactory,
|
||||||
|
@ -59,156 +35,119 @@ class RequestFsm
|
||||||
* optionally supplied $finalState is entered.
|
* optionally supplied $finalState is entered.
|
||||||
*
|
*
|
||||||
* @param Transaction $trans Transaction being transitioned.
|
* @param Transaction $trans Transaction being transitioned.
|
||||||
* @param string $finalState The state to stop on. If unspecified,
|
|
||||||
* runs until a terminal state is found.
|
|
||||||
*
|
*
|
||||||
* @throws \Exception if a terminal state throws an exception.
|
* @throws \Exception if a terminal state throws an exception.
|
||||||
*/
|
*/
|
||||||
public function __invoke(Transaction $trans, $finalState = null)
|
public function __invoke(Transaction $trans)
|
||||||
{
|
{
|
||||||
$trans->_transitionCount = 1;
|
$trans->_transitionCount = 0;
|
||||||
|
|
||||||
if (!$trans->state) {
|
if (!$trans->state) {
|
||||||
$trans->state = 'before';
|
$trans->state = 'before';
|
||||||
}
|
}
|
||||||
|
|
||||||
while ($trans->state !== $finalState) {
|
transition:
|
||||||
|
|
||||||
if (!isset($this->states[$trans->state])) {
|
if (++$trans->_transitionCount > $this->maxTransitions) {
|
||||||
throw new StateException("Invalid state: {$trans->state}");
|
throw new StateException("Too many state transitions were "
|
||||||
} elseif (++$trans->_transitionCount > $this->maxTransitions) {
|
. "encountered ({$trans->_transitionCount}). This likely "
|
||||||
throw new StateException('Too many state transitions were '
|
. "means that a combination of event listeners are in an "
|
||||||
. 'encountered ({$trans->_transitionCount}). This likely '
|
. "infinite loop.");
|
||||||
. 'means that a combination of event listeners are in an '
|
}
|
||||||
. 'infinite loop.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$state = $this->states[$trans->state];
|
switch ($trans->state) {
|
||||||
|
case 'before': goto before;
|
||||||
|
case 'complete': goto complete;
|
||||||
|
case 'error': goto error;
|
||||||
|
case 'retry': goto retry;
|
||||||
|
case 'send': goto send;
|
||||||
|
case 'end': goto end;
|
||||||
|
default: throw new StateException("Invalid state: {$trans->state}");
|
||||||
|
}
|
||||||
|
|
||||||
|
before: {
|
||||||
try {
|
try {
|
||||||
/** @var callable $fn */
|
$trans->request->getEmitter()->emit('before', new BeforeEvent($trans));
|
||||||
$fn = [$this, $trans->state];
|
$trans->state = 'send';
|
||||||
if ($fn($trans)) {
|
if ((bool) $trans->response) {
|
||||||
// Handles transitioning to the "intercept" state.
|
$trans->state = 'complete';
|
||||||
if (isset($state['intercept'])) {
|
|
||||||
$trans->state = $state['intercept'];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw new StateException('Invalid intercept state '
|
|
||||||
. 'transition from ' . $trans->state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($state['success'])) {
|
|
||||||
// Transition to the success state
|
|
||||||
$trans->state = $state['success'];
|
|
||||||
} else {
|
|
||||||
// Break: this is a terminal state with no transition.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (StateException $e) {
|
|
||||||
// State exceptions are thrown no matter what.
|
|
||||||
throw $e;
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
$trans->state = 'error';
|
||||||
$trans->exception = $e;
|
$trans->exception = $e;
|
||||||
// Terminal error states throw the exception.
|
}
|
||||||
if (!isset($state['error'])) {
|
goto transition;
|
||||||
throw $e;
|
}
|
||||||
|
|
||||||
|
complete: {
|
||||||
|
try {
|
||||||
|
if ($trans->response instanceof FutureInterface) {
|
||||||
|
// Futures will have their own end events emitted when
|
||||||
|
// dereferenced.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Transition to the error state.
|
$trans->state = 'end';
|
||||||
$trans->state = $state['error'];
|
$trans->response->setEffectiveUrl($trans->request->getUrl());
|
||||||
|
$trans->request->getEmitter()->emit('complete', new CompleteEvent($trans));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$trans->state = 'error';
|
||||||
|
$trans->exception = $e;
|
||||||
}
|
}
|
||||||
|
goto transition;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private function before(Transaction $trans)
|
error: {
|
||||||
{
|
try {
|
||||||
$trans->request->getEmitter()->emit('before', new BeforeEvent($trans));
|
// Convert non-request exception to a wrapped exception
|
||||||
|
$trans->exception = RequestException::wrapException(
|
||||||
// When a response is set during the before event (i.e., a mock), then
|
$trans->request, $trans->exception
|
||||||
// we don't need to send anything. Skip ahead to the complete event
|
);
|
||||||
// by returning to to go to the intercept state.
|
$trans->state = 'end';
|
||||||
return (bool) $trans->response;
|
$trans->request->getEmitter()->emit('error', new ErrorEvent($trans));
|
||||||
}
|
// An intercepted request (not retried) transitions to complete
|
||||||
|
if (!$trans->exception && $trans->state !== 'retry') {
|
||||||
private function send(Transaction $trans)
|
$trans->state = 'complete';
|
||||||
{
|
}
|
||||||
$fn = $this->handler;
|
} catch (\Exception $e) {
|
||||||
$trans->response = FutureResponse::proxy(
|
$trans->state = 'end';
|
||||||
$fn(RingBridge::prepareRingRequest($trans)),
|
$trans->exception = $e;
|
||||||
function ($value) use ($trans) {
|
|
||||||
RingBridge::completeRingResponse($trans, $value, $this->mf, $this);
|
|
||||||
return $trans->response;
|
|
||||||
}
|
}
|
||||||
);
|
goto transition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
retry: {
|
||||||
* Emits the error event and ensures that the exception is set and is an
|
$trans->retries++;
|
||||||
* instance of RequestException. If the error event is not intercepted,
|
$trans->response = null;
|
||||||
* then the exception is thrown and we transition to the "end" event. This
|
$trans->exception = null;
|
||||||
* event also allows requests to be retried, and when retried, transitions
|
$trans->state = 'before';
|
||||||
* to the "before" event. Otherwise, when no retries, and the exception is
|
goto transition;
|
||||||
* intercepted, transition to the "complete" event.
|
}
|
||||||
*/
|
|
||||||
private function error(Transaction $trans)
|
send: {
|
||||||
{
|
$fn = $this->handler;
|
||||||
// Convert non-request exception to a wrapped exception
|
$trans->response = FutureResponse::proxy(
|
||||||
if (!($trans->exception instanceof RequestException)) {
|
$fn(RingBridge::prepareRingRequest($trans)),
|
||||||
$trans->exception = RequestException::wrapException(
|
function ($value) use ($trans) {
|
||||||
$trans->request, $trans->exception
|
RingBridge::completeRingResponse($trans, $value, $this->mf, $this);
|
||||||
|
$this($trans);
|
||||||
|
return $trans->response;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// Dispatch an event and allow interception
|
|
||||||
$event = new ErrorEvent($trans);
|
|
||||||
$trans->request->getEmitter()->emit('error', $event);
|
|
||||||
|
|
||||||
if ($trans->exception) {
|
|
||||||
throw $trans->exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
$trans->exception = null;
|
|
||||||
|
|
||||||
// Return true to transition to the 'before' state. False otherwise.
|
|
||||||
return $trans->state === 'before';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits a complete event, and if a request is marked for a retry during
|
|
||||||
* the complete event, then the "before" state is transitioned to.
|
|
||||||
*/
|
|
||||||
private function complete(Transaction $trans)
|
|
||||||
{
|
|
||||||
// Futures will have their own end events emitted when dereferenced.
|
|
||||||
if ($trans->response instanceof FutureInterface) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$trans->response->setEffectiveUrl($trans->request->getUrl());
|
|
||||||
$trans->request->getEmitter()->emit('complete', new CompleteEvent($trans));
|
|
||||||
|
|
||||||
// Return true to transition to the 'before' state. False otherwise.
|
|
||||||
return $trans->state === 'before';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emits the "end" event and throws an exception if one is present.
|
|
||||||
*/
|
|
||||||
private function end(Transaction $trans)
|
|
||||||
{
|
|
||||||
// Futures will have their own end events emitted when dereferenced,
|
|
||||||
// but still emit, even for futures, when an exception is present.
|
|
||||||
if (!$trans->exception && $trans->response instanceof FutureInterface) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$trans->request->getEmitter()->emit('end', new EndEvent($trans));
|
end: {
|
||||||
|
$trans->request->getEmitter()->emit('end', new EndEvent($trans));
|
||||||
// Throw exceptions in the terminal event if the exception was not
|
// Throw exceptions in the terminal event if the exception
|
||||||
// handled by an "end" event listener.
|
// was not handled by an "end" event listener.
|
||||||
if ($trans->exception) {
|
if ($trans->exception) {
|
||||||
throw $trans->exception;
|
if (!($trans->exception instanceof RequestException)) {
|
||||||
|
$trans->exception = RequestException::wrapException(
|
||||||
|
$trans->request, $trans->exception
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw $trans->exception;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,19 +72,17 @@ class RingBridge
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the process of processing a response received from a ring
|
* Handles the process of processing a response received from a ring
|
||||||
* handler. The created response is added to the transaction, and any
|
* handler. The created response is added to the transaction, and the
|
||||||
* necessary events are emitted based on the ring response.
|
* transaction stat is set appropriately.
|
||||||
*
|
*
|
||||||
* @param Transaction $trans Owns request and response.
|
* @param Transaction $trans Owns request and response.
|
||||||
* @param array $response Ring response array
|
* @param array $response Ring response array
|
||||||
* @param MessageFactoryInterface $messageFactory Creates response objects.
|
* @param MessageFactoryInterface $messageFactory Creates response objects.
|
||||||
* @param callable $fsm Request FSM function.
|
|
||||||
*/
|
*/
|
||||||
public static function completeRingResponse(
|
public static function completeRingResponse(
|
||||||
Transaction $trans,
|
Transaction $trans,
|
||||||
array $response,
|
array $response,
|
||||||
MessageFactoryInterface $messageFactory,
|
MessageFactoryInterface $messageFactory
|
||||||
callable $fsm
|
|
||||||
) {
|
) {
|
||||||
$trans->state = 'complete';
|
$trans->state = 'complete';
|
||||||
$trans->transferInfo = isset($response['transfer_stats'])
|
$trans->transferInfo = isset($response['transfer_stats'])
|
||||||
|
@ -116,9 +114,6 @@ class RingBridge
|
||||||
$trans->state = 'error';
|
$trans->state = 'error';
|
||||||
$trans->exception = $response['error'];
|
$trans->exception = $response['error'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete the lifecycle of the request.
|
|
||||||
$fsm($trans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,7 +158,7 @@ class RingBridge
|
||||||
Sending the request did not return a response, exception, or populate the
|
Sending the request did not return a response, exception, or populate the
|
||||||
transaction with a response. This is most likely due to an incorrectly
|
transaction with a response. This is most likely due to an incorrectly
|
||||||
implemented RingPHP handler. If you are simply trying to mock responses,
|
implemented RingPHP handler. If you are simply trying to mock responses,
|
||||||
then it is recommneded to use the GuzzleHttp\Ring\Client\MockHandler.
|
then it is recommended to use the GuzzleHttp\Ring\Client\MockHandler.
|
||||||
EOT;
|
EOT;
|
||||||
return new RequestException($message, $request);
|
return new RequestException($message, $request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,9 @@ namespace GuzzleHttp\Subscriber;
|
||||||
use GuzzleHttp\Event\BeforeEvent;
|
use GuzzleHttp\Event\BeforeEvent;
|
||||||
use GuzzleHttp\Event\RequestEvents;
|
use GuzzleHttp\Event\RequestEvents;
|
||||||
use GuzzleHttp\Event\SubscriberInterface;
|
use GuzzleHttp\Event\SubscriberInterface;
|
||||||
|
use GuzzleHttp\Message\AppliesHeadersInterface;
|
||||||
use GuzzleHttp\Message\RequestInterface;
|
use GuzzleHttp\Message\RequestInterface;
|
||||||
use GuzzleHttp\Mimetypes;
|
use GuzzleHttp\Mimetypes;
|
||||||
use GuzzleHttp\Post\PostBodyInterface;
|
|
||||||
use GuzzleHttp\Stream\StreamInterface;
|
use GuzzleHttp\Stream\StreamInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,8 +40,8 @@ class Prepare implements SubscriberInterface
|
||||||
|
|
||||||
$this->addContentLength($request, $body);
|
$this->addContentLength($request, $body);
|
||||||
|
|
||||||
if ($body instanceof PostBodyInterface) {
|
if ($body instanceof AppliesHeadersInterface) {
|
||||||
// Synchronize the POST body with the request's headers
|
// Synchronize the body with the request headers
|
||||||
$body->applyRequestHeaders($request);
|
$body->applyRequestHeaders($request);
|
||||||
} elseif (!$request->hasHeader('Content-Type')) {
|
} elseif (!$request->hasHeader('Content-Type')) {
|
||||||
$this->addContentType($request, $body);
|
$this->addContentType($request, $body);
|
||||||
|
|
|
@ -4,6 +4,7 @@ namespace GuzzleHttp\Subscriber;
|
||||||
use GuzzleHttp\Event\CompleteEvent;
|
use GuzzleHttp\Event\CompleteEvent;
|
||||||
use GuzzleHttp\Event\RequestEvents;
|
use GuzzleHttp\Event\RequestEvents;
|
||||||
use GuzzleHttp\Event\SubscriberInterface;
|
use GuzzleHttp\Event\SubscriberInterface;
|
||||||
|
use GuzzleHttp\Exception\BadResponseException;
|
||||||
use GuzzleHttp\Exception\CouldNotRewindStreamException;
|
use GuzzleHttp\Exception\CouldNotRewindStreamException;
|
||||||
use GuzzleHttp\Exception\TooManyRedirectsException;
|
use GuzzleHttp\Exception\TooManyRedirectsException;
|
||||||
use GuzzleHttp\Message\RequestInterface;
|
use GuzzleHttp\Message\RequestInterface;
|
||||||
|
@ -25,6 +26,9 @@ use GuzzleHttp\Url;
|
||||||
* POST request with a GET request).
|
* POST request with a GET request).
|
||||||
* - referer: Set to true to automatically add the "Referer" header when a
|
* - referer: Set to true to automatically add the "Referer" header when a
|
||||||
* redirect request is sent.
|
* redirect request is sent.
|
||||||
|
* - protocols: Array of allowed protocols. Defaults to 'http' and 'https'.
|
||||||
|
* When a redirect attempts to utilize a protocol that is not white listed,
|
||||||
|
* an exception is thrown.
|
||||||
*/
|
*/
|
||||||
class Redirect implements SubscriberInterface
|
class Redirect implements SubscriberInterface
|
||||||
{
|
{
|
||||||
|
@ -99,6 +103,7 @@ class Redirect implements SubscriberInterface
|
||||||
ResponseInterface $response
|
ResponseInterface $response
|
||||||
) {
|
) {
|
||||||
$config = $request->getConfig();
|
$config = $request->getConfig();
|
||||||
|
$protocols = $config->getPath('redirect/protocols') ?: ['http', 'https'];
|
||||||
|
|
||||||
// Use a GET request if this is an entity enclosing request and we are
|
// Use a GET request if this is an entity enclosing request and we are
|
||||||
// not forcing RFC compliance, but rather emulating what all browsers
|
// not forcing RFC compliance, but rather emulating what all browsers
|
||||||
|
@ -112,7 +117,7 @@ class Redirect implements SubscriberInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
$previousUrl = $request->getUrl();
|
$previousUrl = $request->getUrl();
|
||||||
$this->setRedirectUrl($request, $response);
|
$this->setRedirectUrl($request, $response, $protocols);
|
||||||
$this->rewindEntityBody($request);
|
$this->rewindEntityBody($request);
|
||||||
|
|
||||||
// Add the Referer header if it is told to do so and only
|
// Add the Referer header if it is told to do so and only
|
||||||
|
@ -134,10 +139,12 @@ class Redirect implements SubscriberInterface
|
||||||
*
|
*
|
||||||
* @param RequestInterface $request
|
* @param RequestInterface $request
|
||||||
* @param ResponseInterface $response
|
* @param ResponseInterface $response
|
||||||
|
* @param array $protocols
|
||||||
*/
|
*/
|
||||||
private function setRedirectUrl(
|
private function setRedirectUrl(
|
||||||
RequestInterface $request,
|
RequestInterface $request,
|
||||||
ResponseInterface $response
|
ResponseInterface $response,
|
||||||
|
array $protocols
|
||||||
) {
|
) {
|
||||||
$location = $response->getHeader('Location');
|
$location = $response->getHeader('Location');
|
||||||
$location = Url::fromString($location);
|
$location = Url::fromString($location);
|
||||||
|
@ -151,6 +158,19 @@ class Redirect implements SubscriberInterface
|
||||||
$location = $originalUrl->combine($location);
|
$location = $originalUrl->combine($location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure that the redirect URL is allowed based on the protocols.
|
||||||
|
if (!in_array($location->getScheme(), $protocols)) {
|
||||||
|
throw new BadResponseException(
|
||||||
|
sprintf(
|
||||||
|
'Redirect URL, %s, does not use one of the allowed redirect protocols: %s',
|
||||||
|
$location,
|
||||||
|
implode(', ', $protocols)
|
||||||
|
),
|
||||||
|
$request,
|
||||||
|
$response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$request->setUrl($location);
|
$request->setUrl($location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,30 +56,48 @@ class Transaction
|
||||||
public $transferInfo = [];
|
public $transferInfo = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The transaction's state.
|
* The number of transaction retries.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $retries = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The transaction's current state.
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
public $state;
|
public $state;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of state transitions that this transactions has been through.
|
* Whether or not this is a future transaction. This value should not be
|
||||||
|
* changed after the future is constructed.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $future;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of state transitions that this transaction has been through.
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
* @internal This is for internal use only. If you modify this, then you
|
* @internal This is for internal use only. If you modify this, then you
|
||||||
* are asking for trouble.
|
* are asking for trouble.
|
||||||
*/
|
*/
|
||||||
public $_transitionCount;
|
public $_transitionCount = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ClientInterface $client Client that is used to send the requests
|
* @param ClientInterface $client Client that is used to send the requests
|
||||||
* @param RequestInterface $request Request to send
|
* @param RequestInterface $request Request to send
|
||||||
|
* @param bool $future Whether or not this is a future request.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ClientInterface $client,
|
ClientInterface $client,
|
||||||
RequestInterface $request
|
RequestInterface $request,
|
||||||
|
$future = false
|
||||||
) {
|
) {
|
||||||
$this->client = $client;
|
$this->client = $client;
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
|
$this->_future = $future;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,7 @@ class Url
|
||||||
$password = null,
|
$password = null,
|
||||||
$port = null,
|
$port = null,
|
||||||
$path = null,
|
$path = null,
|
||||||
Query $query = null,
|
$query = null,
|
||||||
$fragment = null
|
$fragment = null
|
||||||
) {
|
) {
|
||||||
$this->scheme = $scheme;
|
$this->scheme = $scheme;
|
||||||
|
|
|
@ -7,10 +7,12 @@ use GuzzleHttp\Event\ErrorEvent;
|
||||||
use GuzzleHttp\Message\MessageFactory;
|
use GuzzleHttp\Message\MessageFactory;
|
||||||
use GuzzleHttp\Message\Response;
|
use GuzzleHttp\Message\Response;
|
||||||
use GuzzleHttp\Exception\RequestException;
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use GuzzleHttp\Query;
|
||||||
use GuzzleHttp\Ring\Client\MockHandler;
|
use GuzzleHttp\Ring\Client\MockHandler;
|
||||||
use GuzzleHttp\Ring\Future\FutureArray;
|
use GuzzleHttp\Ring\Future\FutureArray;
|
||||||
use GuzzleHttp\Subscriber\History;
|
use GuzzleHttp\Subscriber\History;
|
||||||
use GuzzleHttp\Subscriber\Mock;
|
use GuzzleHttp\Subscriber\Mock;
|
||||||
|
use GuzzleHttp\Url;
|
||||||
use React\Promise\Deferred;
|
use React\Promise\Deferred;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,6 +72,14 @@ class ClientTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals('http://foo.com/baz/', $client->getBaseUrl());
|
$this->assertEquals('http://foo.com/baz/', $client->getBaseUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function testValidatesUriTemplateValue()
|
||||||
|
{
|
||||||
|
new Client(['base_url' => ['http://foo.com/']]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Exception
|
* @expectedException \Exception
|
||||||
* @expectedExceptionMessage Foo
|
* @expectedExceptionMessage Foo
|
||||||
|
@ -243,6 +253,15 @@ class ClientTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals('custom', $request->getHeader('Foo'));
|
$this->assertEquals('custom', $request->getHeader('Foo'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCanOverrideDefaultOptionWithNull()
|
||||||
|
{
|
||||||
|
$client = new Client(['defaults' => ['proxy' => 'invalid!']]);
|
||||||
|
$request = $client->createRequest('GET', 'http://foo.com?a=b', [
|
||||||
|
'proxy' => null
|
||||||
|
]);
|
||||||
|
$this->assertFalse($request->getConfig()->hasKey('proxy'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testDoesNotOverwriteExistingUA()
|
public function testDoesNotOverwriteExistingUA()
|
||||||
{
|
{
|
||||||
$client = new Client(['defaults' => [
|
$client = new Client(['defaults' => [
|
||||||
|
@ -272,6 +291,15 @@ class ClientTest extends \PHPUnit_Framework_TestCase
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFalsyPathsAreCombinedWithBaseUrl()
|
||||||
|
{
|
||||||
|
$client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']);
|
||||||
|
$this->assertEquals(
|
||||||
|
'http://www.foo.com/0',
|
||||||
|
$client->createRequest('GET', '0')->getUrl()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testUsesBaseUrlCombinedWithProvidedUrlViaUriTemplate()
|
public function testUsesBaseUrlCombinedWithProvidedUrlViaUriTemplate()
|
||||||
{
|
{
|
||||||
$client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']);
|
$client = new Client(['base_url' => 'http://www.foo.com/baz?bam=bar']);
|
||||||
|
@ -582,4 +610,15 @@ class ClientTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res);
|
$this->assertInstanceOf('GuzzleHttp\Message\FutureResponse', $res);
|
||||||
$this->assertEquals(200, $res->getStatusCode());
|
$this->assertEquals(200, $res->getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCanUseUrlWithCustomQuery()
|
||||||
|
{
|
||||||
|
$client = new Client();
|
||||||
|
$url = Url::fromString('http://foo.com/bar');
|
||||||
|
$query = new Query(['baz' => '123%20']);
|
||||||
|
$query->setEncodingType(false);
|
||||||
|
$url->setQuery($query);
|
||||||
|
$r = $client->createRequest('GET', $url);
|
||||||
|
$this->assertEquals('http://foo.com/bar?baz=123%20', $r->getUrl());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class AbstractRetryableEventTest extends \PHPUnit_Framework_TestCase
|
||||||
->getMockForAbstractClass();
|
->getMockForAbstractClass();
|
||||||
$e->retry();
|
$e->retry();
|
||||||
$this->assertTrue($e->isPropagationStopped());
|
$this->assertTrue($e->isPropagationStopped());
|
||||||
$this->assertEquals('before', $t->state);
|
$this->assertEquals('retry', $t->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCanRetryAfterDelay()
|
public function testCanRetryAfterDelay()
|
||||||
|
@ -31,7 +31,7 @@ class AbstractRetryableEventTest extends \PHPUnit_Framework_TestCase
|
||||||
->getMockForAbstractClass();
|
->getMockForAbstractClass();
|
||||||
$e->retry(10);
|
$e->retry(10);
|
||||||
$this->assertTrue($e->isPropagationStopped());
|
$this->assertTrue($e->isPropagationStopped());
|
||||||
$this->assertEquals('before', $t->state);
|
$this->assertEquals('retry', $t->state);
|
||||||
$this->assertEquals(10, $t->request->getConfig()->get('delay'));
|
$this->assertEquals(10, $t->request->getConfig()->get('delay'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,4 +46,14 @@ class AbstractTransferEventTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertSame($t->response, $e->getResponse());
|
$this->assertSame($t->response, $e->getResponse());
|
||||||
$this->assertTrue($e->isPropagationStopped());
|
$this->assertTrue($e->isPropagationStopped());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testReturnsNumberOfRetries()
|
||||||
|
{
|
||||||
|
$t = new Transaction(new Client(), new Request('GET', '/'));
|
||||||
|
$t->retries = 2;
|
||||||
|
$e = $this->getMockBuilder('GuzzleHttp\Event\AbstractTransferEvent')
|
||||||
|
->setConstructorArgs([$t])
|
||||||
|
->getMockForAbstractClass();
|
||||||
|
$this->assertEquals(2, $e->getRetryCount());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@ namespace GuzzleHttp\Tests;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use GuzzleHttp\Event\AbstractTransferEvent;
|
use GuzzleHttp\Event\AbstractTransferEvent;
|
||||||
|
use GuzzleHttp\Event\CompleteEvent;
|
||||||
use GuzzleHttp\Event\EndEvent;
|
use GuzzleHttp\Event\EndEvent;
|
||||||
|
use GuzzleHttp\Event\ErrorEvent;
|
||||||
|
use GuzzleHttp\Exception\RequestException;
|
||||||
use GuzzleHttp\Message\Response;
|
use GuzzleHttp\Message\Response;
|
||||||
use GuzzleHttp\Pool;
|
use GuzzleHttp\Pool;
|
||||||
|
|
||||||
|
@ -70,4 +73,51 @@ class IntegrationTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertNotEmpty($transfer);
|
$this->assertNotEmpty($transfer);
|
||||||
$this->assertArrayHasKey('url', $transfer);
|
$this->assertArrayHasKey('url', $transfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testNestedFutureResponsesAreResolvedWhenSending()
|
||||||
|
{
|
||||||
|
$c = new Client();
|
||||||
|
$total = 3;
|
||||||
|
Server::enqueue([
|
||||||
|
new Response(200),
|
||||||
|
new Response(201),
|
||||||
|
new Response(202)
|
||||||
|
]);
|
||||||
|
$c->getEmitter()->on(
|
||||||
|
'complete',
|
||||||
|
function (CompleteEvent $e) use (&$total) {
|
||||||
|
if (--$total) {
|
||||||
|
$e->retry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$response = $c->get(Server::$url);
|
||||||
|
$this->assertEquals(202, $response->getStatusCode());
|
||||||
|
$this->assertEquals('GuzzleHttp\Message\Response', get_class($response));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNestedFutureErrorsAreResolvedWhenSending()
|
||||||
|
{
|
||||||
|
$c = new Client();
|
||||||
|
$total = 3;
|
||||||
|
Server::enqueue([
|
||||||
|
new Response(500),
|
||||||
|
new Response(501),
|
||||||
|
new Response(502)
|
||||||
|
]);
|
||||||
|
$c->getEmitter()->on(
|
||||||
|
'error',
|
||||||
|
function (ErrorEvent $e) use (&$total) {
|
||||||
|
if (--$total) {
|
||||||
|
$e->retry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
$c->get(Server::$url);
|
||||||
|
$this->fail('Did not throw!');
|
||||||
|
} catch (RequestException $e) {
|
||||||
|
$this->assertEquals(502, $e->getResponse()->getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ class MessageFactoryTest extends \PHPUnit_Framework_TestCase
|
||||||
*/
|
*/
|
||||||
public function testValidatesRedirects()
|
public function testValidatesRedirects()
|
||||||
{
|
{
|
||||||
(new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => []]);
|
(new MessageFactory())->createRequest('GET', '/', ['allow_redirects' => 'foo']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCanEnableStrictRedirectsAndSpecifyMax()
|
public function testCanEnableStrictRedirectsAndSpecifyMax()
|
||||||
|
|
|
@ -154,6 +154,64 @@ class PoolTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertSame($responses[3], $result[2]->getResponse());
|
$this->assertSame($responses[3], $result[2]->getResponse());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBatchesRequestsWithDynamicPoolSize()
|
||||||
|
{
|
||||||
|
$client = new Client(['handler' => function () {
|
||||||
|
throw new \RuntimeException('No network access');
|
||||||
|
}]);
|
||||||
|
|
||||||
|
$responses = [
|
||||||
|
new Response(301, ['Location' => 'http://foo.com/bar']),
|
||||||
|
new Response(200),
|
||||||
|
new Response(200),
|
||||||
|
new Response(404)
|
||||||
|
];
|
||||||
|
|
||||||
|
$client->getEmitter()->attach(new Mock($responses));
|
||||||
|
$requests = [
|
||||||
|
$client->createRequest('GET', 'http://foo.com/baz'),
|
||||||
|
$client->createRequest('HEAD', 'http://httpbin.org/get'),
|
||||||
|
$client->createRequest('PUT', 'http://httpbin.org/put'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$a = $b = $c = $d = 0;
|
||||||
|
$result = Pool::batch($client, $requests, [
|
||||||
|
'before' => function (BeforeEvent $e) use (&$a) { $a++; },
|
||||||
|
'complete' => function (CompleteEvent $e) use (&$b) { $b++; },
|
||||||
|
'error' => function (ErrorEvent $e) use (&$c) { $c++; },
|
||||||
|
'end' => function (EndEvent $e) use (&$d) { $d++; },
|
||||||
|
'pool_size' => function ($queueSize) {
|
||||||
|
static $options = [1, 2, 1];
|
||||||
|
static $queued = 0;
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
$queued,
|
||||||
|
$queueSize,
|
||||||
|
'The number of queued requests should be equal to the sum of pool sizes so far.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$next = array_shift($options);
|
||||||
|
$queued += $next;
|
||||||
|
|
||||||
|
return $next;
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertEquals(4, $a);
|
||||||
|
$this->assertEquals(2, $b);
|
||||||
|
$this->assertEquals(1, $c);
|
||||||
|
$this->assertEquals(3, $d);
|
||||||
|
$this->assertCount(3, $result);
|
||||||
|
$this->assertInstanceOf('GuzzleHttp\BatchResults', $result);
|
||||||
|
|
||||||
|
// The first result is actually the second (redirect) response.
|
||||||
|
$this->assertSame($responses[1], $result[0]);
|
||||||
|
// The second result is a 1:1 request:response map
|
||||||
|
$this->assertSame($responses[2], $result[1]);
|
||||||
|
// The third entry is the 404 RequestException
|
||||||
|
$this->assertSame($responses[3], $result[2]->getResponse());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \InvalidArgumentException
|
* @expectedException \InvalidArgumentException
|
||||||
* @expectedExceptionMessage Each event listener must be a callable or
|
* @expectedExceptionMessage Each event listener must be a callable or
|
||||||
|
@ -228,4 +286,34 @@ class PoolTest extends \PHPUnit_Framework_TestCase
|
||||||
Pool::send($client, $requests);
|
Pool::send($client, $requests);
|
||||||
$this->assertCount(1, $history);
|
$this->assertCount(1, $history);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testDoesNotInfinitelyRecurse()
|
||||||
|
{
|
||||||
|
$client = new Client(['handler' => function () {
|
||||||
|
throw new \RuntimeException('No network access');
|
||||||
|
}]);
|
||||||
|
|
||||||
|
$last = null;
|
||||||
|
$client->getEmitter()->on(
|
||||||
|
'before',
|
||||||
|
function (BeforeEvent $e) use (&$last) {
|
||||||
|
$e->intercept(new Response(200));
|
||||||
|
if (function_exists('xdebug_get_stack_depth')) {
|
||||||
|
if ($last) {
|
||||||
|
$this->assertEquals($last, xdebug_get_stack_depth());
|
||||||
|
} else {
|
||||||
|
$last = xdebug_get_stack_depth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
$requests = [];
|
||||||
|
for ($i = 0; $i < 100; $i++) {
|
||||||
|
$requests[] = $client->createRequest('GET', 'http://foo.com');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pool = new Pool($client, $requests);
|
||||||
|
$pool->wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,6 @@ class PostBodyTest extends \PHPUnit_Framework_TestCase
|
||||||
$b->seek(0);
|
$b->seek(0);
|
||||||
$this->assertEquals('foo=bar&baz=123', $b->read(1000));
|
$this->assertEquals('foo=bar&baz=123', $b->read(1000));
|
||||||
$this->assertEquals(15, $b->tell());
|
$this->assertEquals(15, $b->tell());
|
||||||
$this->assertTrue($b->eof());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCanSpecifyQueryAggregator()
|
public function testCanSpecifyQueryAggregator()
|
||||||
|
|
|
@ -5,6 +5,7 @@ use GuzzleHttp\Exception\RequestException;
|
||||||
use GuzzleHttp\Message\MessageFactory;
|
use GuzzleHttp\Message\MessageFactory;
|
||||||
use GuzzleHttp\Message\Response;
|
use GuzzleHttp\Message\Response;
|
||||||
use GuzzleHttp\RequestFsm;
|
use GuzzleHttp\RequestFsm;
|
||||||
|
use GuzzleHttp\Ring\Future\CompletedFutureArray;
|
||||||
use GuzzleHttp\Subscriber\Mock;
|
use GuzzleHttp\Subscriber\Mock;
|
||||||
use GuzzleHttp\Transaction;
|
use GuzzleHttp\Transaction;
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
@ -29,19 +30,23 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
public function testEmitsBeforeEventInTransition()
|
public function testEmitsBeforeEventInTransition()
|
||||||
{
|
{
|
||||||
$fsm = new RequestFsm(function () {}, $this->mf);
|
$fsm = new RequestFsm(function () {
|
||||||
|
return new CompletedFutureArray(['status' => 200]);
|
||||||
|
}, $this->mf);
|
||||||
$t = new Transaction(new Client(), new Request('GET', 'http://foo.com'));
|
$t = new Transaction(new Client(), new Request('GET', 'http://foo.com'));
|
||||||
$c = false;
|
$c = false;
|
||||||
$t->request->getEmitter()->on('before', function (BeforeEvent $e) use (&$c) {
|
$t->request->getEmitter()->on('before', function (BeforeEvent $e) use (&$c) {
|
||||||
$c = true;
|
$c = true;
|
||||||
});
|
});
|
||||||
$fsm($t, 'send');
|
$fsm($t);
|
||||||
$this->assertTrue($c);
|
$this->assertTrue($c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEmitsCompleteEventInTransition()
|
public function testEmitsCompleteEventInTransition()
|
||||||
{
|
{
|
||||||
$fsm = new RequestFsm(function () {}, $this->mf);
|
$fsm = new RequestFsm(function () {
|
||||||
|
return new CompletedFutureArray(['status' => 200]);
|
||||||
|
}, $this->mf);
|
||||||
$t = new Transaction(new Client(), new Request('GET', 'http://foo.com'));
|
$t = new Transaction(new Client(), new Request('GET', 'http://foo.com'));
|
||||||
$t->response = new Response(200);
|
$t->response = new Response(200);
|
||||||
$t->state = 'complete';
|
$t->state = 'complete';
|
||||||
|
@ -49,13 +54,15 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
$t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) {
|
$t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) {
|
||||||
$c = true;
|
$c = true;
|
||||||
});
|
});
|
||||||
$fsm($t, 'end');
|
$fsm($t);
|
||||||
$this->assertTrue($c);
|
$this->assertTrue($c);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDoesNotEmitCompleteForFuture()
|
public function testDoesNotEmitCompleteForFuture()
|
||||||
{
|
{
|
||||||
$fsm = new RequestFsm(function () {}, $this->mf);
|
$fsm = new RequestFsm(function () {
|
||||||
|
return new CompletedFutureArray(['status' => 200]);
|
||||||
|
}, $this->mf);
|
||||||
$t = new Transaction(new Client(), new Request('GET', 'http://foo.com'));
|
$t = new Transaction(new Client(), new Request('GET', 'http://foo.com'));
|
||||||
$deferred = new Deferred();
|
$deferred = new Deferred();
|
||||||
$t->response = new FutureResponse($deferred->promise());
|
$t->response = new FutureResponse($deferred->promise());
|
||||||
|
@ -64,21 +71,6 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
$t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) {
|
$t->request->getEmitter()->on('complete', function (CompleteEvent $e) use (&$c) {
|
||||||
$c = true;
|
$c = true;
|
||||||
});
|
});
|
||||||
$fsm($t, 'end');
|
|
||||||
$this->assertFalse($c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDoesNotEmitEndForFuture()
|
|
||||||
{
|
|
||||||
$fsm = new RequestFsm(function () {}, $this->mf);
|
|
||||||
$t = new Transaction(new Client(), new Request('GET', 'http://foo.com'));
|
|
||||||
$deferred = new Deferred();
|
|
||||||
$t->response = new FutureResponse($deferred->promise());
|
|
||||||
$t->state = 'end';
|
|
||||||
$c = false;
|
|
||||||
$t->request->getEmitter()->on('end', function (EndEvent $e) use (&$c) {
|
|
||||||
$c = true;
|
|
||||||
});
|
|
||||||
$fsm($t);
|
$fsm($t);
|
||||||
$this->assertFalse($c);
|
$this->assertFalse($c);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +87,9 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
public function testTransitionsThroughErrorsInBefore()
|
public function testTransitionsThroughErrorsInBefore()
|
||||||
{
|
{
|
||||||
$fsm = new RequestFsm(function () {}, $this->mf);
|
$fsm = new RequestFsm(function () {
|
||||||
|
return new CompletedFutureArray(['status' => 200]);
|
||||||
|
}, $this->mf);
|
||||||
$client = new Client();
|
$client = new Client();
|
||||||
$request = $client->createRequest('GET', 'http://ewfewwef.com');
|
$request = $client->createRequest('GET', 'http://ewfewwef.com');
|
||||||
$t = new Transaction($client, $request);
|
$t = new Transaction($client, $request);
|
||||||
|
@ -105,7 +99,7 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
throw new \Exception('foo');
|
throw new \Exception('foo');
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
$fsm($t, 'send');
|
$fsm($t);
|
||||||
$this->fail('did not throw');
|
$this->fail('did not throw');
|
||||||
} catch (RequestException $e) {
|
} catch (RequestException $e) {
|
||||||
$this->assertContains('foo', $t->exception->getMessage());
|
$this->assertContains('foo', $t->exception->getMessage());
|
||||||
|
@ -133,7 +127,9 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
public function testTransitionsThroughErrorInterception()
|
public function testTransitionsThroughErrorInterception()
|
||||||
{
|
{
|
||||||
$fsm = new RequestFsm(function () {}, $this->mf);
|
$fsm = new RequestFsm(function () {
|
||||||
|
return new CompletedFutureArray(['status' => 404]);
|
||||||
|
}, $this->mf);
|
||||||
$client = new Client();
|
$client = new Client();
|
||||||
$request = $client->createRequest('GET', 'http://ewfewwef.com');
|
$request = $client->createRequest('GET', 'http://ewfewwef.com');
|
||||||
$t = new Transaction($client, $request);
|
$t = new Transaction($client, $request);
|
||||||
|
@ -142,9 +138,6 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
$t->request->getEmitter()->on('error', function (ErrorEvent $e) {
|
$t->request->getEmitter()->on('error', function (ErrorEvent $e) {
|
||||||
$e->intercept(new Response(200));
|
$e->intercept(new Response(200));
|
||||||
});
|
});
|
||||||
$fsm($t, 'send');
|
|
||||||
$t->response = new Response(404);
|
|
||||||
$t->state = 'complete';
|
|
||||||
$fsm($t);
|
$fsm($t);
|
||||||
$this->assertEquals(200, $t->response->getStatusCode());
|
$this->assertEquals(200, $t->response->getStatusCode());
|
||||||
$this->assertNull($t->exception);
|
$this->assertNull($t->exception);
|
||||||
|
@ -175,10 +168,12 @@ class RequestFsmTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
$client = new Client([
|
$client = new Client([
|
||||||
'fsm' => $fsm = new RequestFsm(
|
'fsm' => $fsm = new RequestFsm(
|
||||||
function () {},
|
function () {
|
||||||
new MessageFactory(),
|
return new CompletedFutureArray(['status' => 200]);
|
||||||
3
|
},
|
||||||
)
|
new MessageFactory(),
|
||||||
|
3
|
||||||
|
)
|
||||||
]);
|
]);
|
||||||
$request = $client->createRequest('GET', 'http://foo.com:123');
|
$request = $client->createRequest('GET', 'http://foo.com:123');
|
||||||
$request->getEmitter()->on('before', function () {
|
$request->getEmitter()->on('before', function () {
|
||||||
|
|
|
@ -107,24 +107,6 @@ class RingBridgeTest extends \PHPUnit_Framework_TestCase
|
||||||
$this->assertEquals('foo', (string) $response->getBody());
|
$this->assertEquals('foo', (string) $response->getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEmitsCompleteEventOnSuccess()
|
|
||||||
{
|
|
||||||
$c = false;
|
|
||||||
$trans = new Transaction(new Client(), new Request('GET', 'http://f.co'));
|
|
||||||
$trans->request->getEmitter()->on('complete', function () use (&$c) {
|
|
||||||
$c = true;
|
|
||||||
});
|
|
||||||
$f = new MessageFactory();
|
|
||||||
$res = ['status' => 200, 'headers' => []];
|
|
||||||
$fsm = new RequestFsm(function () {}, new MessageFactory());
|
|
||||||
RingBridge::completeRingResponse($trans, $res, $f, $fsm);
|
|
||||||
$this->assertInstanceOf(
|
|
||||||
'GuzzleHttp\Message\ResponseInterface',
|
|
||||||
$trans->response
|
|
||||||
);
|
|
||||||
$this->assertTrue($c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testEmitsErrorEventOnError()
|
public function testEmitsErrorEventOnError()
|
||||||
{
|
{
|
||||||
$client = new Client(['base_url' => 'http://127.0.0.1:123']);
|
$client = new Client(['base_url' => 'http://127.0.0.1:123']);
|
||||||
|
|
|
@ -266,4 +266,23 @@ class RedirectTest extends \PHPUnit_Framework_TestCase
|
||||||
$response->getEffectiveUrl()
|
$response->getEffectiveUrl()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \GuzzleHttp\Exception\BadResponseException
|
||||||
|
* @expectedExceptionMessage Redirect URL, https://foo.com/redirect2, does not use one of the allowed redirect protocols: http
|
||||||
|
*/
|
||||||
|
public function testThrowsWhenRedirectingToInvalidUrlProtocol()
|
||||||
|
{
|
||||||
|
$mock = new Mock([
|
||||||
|
"HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n",
|
||||||
|
"HTTP/1.1 301 Moved Permanently\r\nLocation: https://foo.com/redirect2\r\nContent-Length: 0\r\n\r\n"
|
||||||
|
]);
|
||||||
|
$client = new Client();
|
||||||
|
$client->getEmitter()->attach($mock);
|
||||||
|
$client->get('http://www.example.com/foo', [
|
||||||
|
'allow_redirects' => [
|
||||||
|
'protocols' => ['http']
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ php:
|
||||||
- 5.4
|
- 5.4
|
||||||
- 5.5
|
- 5.5
|
||||||
- 5.6
|
- 5.6
|
||||||
|
- 7.0
|
||||||
- hhvm
|
- hhvm
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
|
|
@ -1,5 +1,27 @@
|
||||||
# CHANGELOG
|
# 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
|
## 1.0.3 - 2014-11-03
|
||||||
|
|
||||||
* Setting the `header` stream option as a string to be compatible with GAE.
|
* Setting the `header` stream option as a string to be compatible with GAE.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/ringphp",
|
"name": "guzzlehttp/ringphp",
|
||||||
|
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,11 +15,11 @@ without tying your application to a specific implementation.
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
spec
|
spec
|
||||||
futures
|
futures
|
||||||
client_middleware
|
client_middleware
|
||||||
client_handlers
|
client_handlers
|
||||||
testing
|
testing
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
|
|
@ -406,12 +406,20 @@ class CurlFactory
|
||||||
|
|
||||||
case 'timeout':
|
case 'timeout':
|
||||||
|
|
||||||
$options[CURLOPT_TIMEOUT_MS] = $value * 1000;
|
if (defined('CURLOPT_TIMEOUT_MS')) {
|
||||||
|
$options[CURLOPT_TIMEOUT_MS] = $value * 1000;
|
||||||
|
} else {
|
||||||
|
$options[CURLOPT_TIMEOUT] = $value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'connect_timeout':
|
case 'connect_timeout':
|
||||||
|
|
||||||
$options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000;
|
if (defined('CURLOPT_CONNECTTIMEOUT_MS')) {
|
||||||
|
$options[CURLOPT_CONNECTTIMEOUT_MS] = $value * 1000;
|
||||||
|
} else {
|
||||||
|
$options[CURLOPT_CONNECTTIMEOUT] = $value;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'proxy':
|
case 'proxy':
|
||||||
|
|
|
@ -71,6 +71,7 @@ class CurlHandler
|
||||||
$response = ['transfer_stats' => curl_getinfo($h)];
|
$response = ['transfer_stats' => curl_getinfo($h)];
|
||||||
$response['curl']['error'] = curl_error($h);
|
$response['curl']['error'] = curl_error($h);
|
||||||
$response['curl']['errno'] = curl_errno($h);
|
$response['curl']['errno'] = curl_errno($h);
|
||||||
|
$response['transfer_stats'] = array_merge($response['transfer_stats'], $response['curl']);
|
||||||
$this->releaseEasyHandle($h);
|
$this->releaseEasyHandle($h);
|
||||||
|
|
||||||
return new CompletedFutureArray(
|
return new CompletedFutureArray(
|
||||||
|
|
|
@ -13,13 +13,14 @@ use React\Promise\Deferred;
|
||||||
* When using the CurlMultiHandler, custom curl options can be specified as an
|
* When using the CurlMultiHandler, custom curl options can be specified as an
|
||||||
* associative array of curl option constants mapping to values in the
|
* associative array of curl option constants mapping to values in the
|
||||||
* **curl** key of the "client" key of the request.
|
* **curl** key of the "client" key of the request.
|
||||||
|
*
|
||||||
|
* @property resource $_mh Internal use only. Lazy loaded multi-handle.
|
||||||
*/
|
*/
|
||||||
class CurlMultiHandler
|
class CurlMultiHandler
|
||||||
{
|
{
|
||||||
/** @var callable */
|
/** @var callable */
|
||||||
private $factory;
|
private $factory;
|
||||||
private $selectTimeout;
|
private $selectTimeout;
|
||||||
private $mh;
|
|
||||||
private $active;
|
private $active;
|
||||||
private $handles = [];
|
private $handles = [];
|
||||||
private $delays = [];
|
private $delays = [];
|
||||||
|
@ -42,8 +43,9 @@ class CurlMultiHandler
|
||||||
*/
|
*/
|
||||||
public function __construct(array $options = [])
|
public function __construct(array $options = [])
|
||||||
{
|
{
|
||||||
$this->mh = isset($options['mh'])
|
if (isset($options['mh'])) {
|
||||||
? $options['mh'] : curl_multi_init();
|
$this->_mh = $options['mh'];
|
||||||
|
}
|
||||||
$this->factory = isset($options['handle_factory'])
|
$this->factory = isset($options['handle_factory'])
|
||||||
? $options['handle_factory'] : new CurlFactory();
|
? $options['handle_factory'] : new CurlFactory();
|
||||||
$this->selectTimeout = isset($options['select_timeout'])
|
$this->selectTimeout = isset($options['select_timeout'])
|
||||||
|
@ -52,6 +54,15 @@ class CurlMultiHandler
|
||||||
? $options['max_handles'] : 100;
|
? $options['max_handles'] : 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __get($name)
|
||||||
|
{
|
||||||
|
if ($name === '_mh') {
|
||||||
|
return $this->_mh = curl_multi_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \BadMethodCallException();
|
||||||
|
}
|
||||||
|
|
||||||
public function __destruct()
|
public function __destruct()
|
||||||
{
|
{
|
||||||
// Finish any open connections before terminating the script.
|
// Finish any open connections before terminating the script.
|
||||||
|
@ -59,9 +70,9 @@ class CurlMultiHandler
|
||||||
$this->execute();
|
$this->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->mh) {
|
if (isset($this->_mh)) {
|
||||||
curl_multi_close($this->mh);
|
curl_multi_close($this->_mh);
|
||||||
$this->mh = null;
|
unset($this->_mh);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +117,7 @@ class CurlMultiHandler
|
||||||
do {
|
do {
|
||||||
|
|
||||||
if ($this->active &&
|
if ($this->active &&
|
||||||
curl_multi_select($this->mh, $this->selectTimeout) === -1
|
curl_multi_select($this->_mh, $this->selectTimeout) === -1
|
||||||
) {
|
) {
|
||||||
// Perform a usleep if a select returns -1.
|
// Perform a usleep if a select returns -1.
|
||||||
// See: https://bugs.php.net/bug.php?id=61141
|
// See: https://bugs.php.net/bug.php?id=61141
|
||||||
|
@ -119,7 +130,7 @@ class CurlMultiHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$mrc = curl_multi_exec($this->mh, $this->active);
|
$mrc = curl_multi_exec($this->_mh, $this->active);
|
||||||
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
||||||
|
|
||||||
$this->processMessages();
|
$this->processMessages();
|
||||||
|
@ -142,13 +153,13 @@ class CurlMultiHandler
|
||||||
if (isset($entry['request']['client']['delay'])) {
|
if (isset($entry['request']['client']['delay'])) {
|
||||||
$this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000);
|
$this->delays[$id] = microtime(true) + ($entry['request']['client']['delay'] / 1000);
|
||||||
} elseif (empty($entry['request']['future'])) {
|
} elseif (empty($entry['request']['future'])) {
|
||||||
curl_multi_add_handle($this->mh, $entry['handle']);
|
curl_multi_add_handle($this->_mh, $entry['handle']);
|
||||||
} else {
|
} else {
|
||||||
curl_multi_add_handle($this->mh, $entry['handle']);
|
curl_multi_add_handle($this->_mh, $entry['handle']);
|
||||||
// "lazy" futures are only sent once the pool has many requests.
|
// "lazy" futures are only sent once the pool has many requests.
|
||||||
if ($entry['request']['future'] !== 'lazy') {
|
if ($entry['request']['future'] !== 'lazy') {
|
||||||
do {
|
do {
|
||||||
$mrc = curl_multi_exec($this->mh, $this->active);
|
$mrc = curl_multi_exec($this->_mh, $this->active);
|
||||||
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
||||||
$this->processMessages();
|
$this->processMessages();
|
||||||
}
|
}
|
||||||
|
@ -159,7 +170,7 @@ class CurlMultiHandler
|
||||||
{
|
{
|
||||||
if (isset($this->handles[$id])) {
|
if (isset($this->handles[$id])) {
|
||||||
curl_multi_remove_handle(
|
curl_multi_remove_handle(
|
||||||
$this->mh,
|
$this->_mh,
|
||||||
$this->handles[$id]['handle']
|
$this->handles[$id]['handle']
|
||||||
);
|
);
|
||||||
curl_close($this->handles[$id]['handle']);
|
curl_close($this->handles[$id]['handle']);
|
||||||
|
@ -183,7 +194,7 @@ class CurlMultiHandler
|
||||||
|
|
||||||
$handle = $this->handles[$id]['handle'];
|
$handle = $this->handles[$id]['handle'];
|
||||||
unset($this->delays[$id], $this->handles[$id]);
|
unset($this->delays[$id], $this->handles[$id]);
|
||||||
curl_multi_remove_handle($this->mh, $handle);
|
curl_multi_remove_handle($this->_mh, $handle);
|
||||||
curl_close($handle);
|
curl_close($handle);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -197,7 +208,7 @@ class CurlMultiHandler
|
||||||
if ($currentTime >= $delay) {
|
if ($currentTime >= $delay) {
|
||||||
unset($this->delays[$id]);
|
unset($this->delays[$id]);
|
||||||
curl_multi_add_handle(
|
curl_multi_add_handle(
|
||||||
$this->mh,
|
$this->_mh,
|
||||||
$this->handles[$id]['handle']
|
$this->handles[$id]['handle']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -206,7 +217,7 @@ class CurlMultiHandler
|
||||||
|
|
||||||
private function processMessages()
|
private function processMessages()
|
||||||
{
|
{
|
||||||
while ($done = curl_multi_info_read($this->mh)) {
|
while ($done = curl_multi_info_read($this->_mh)) {
|
||||||
$id = (int) $done['handle'];
|
$id = (int) $done['handle'];
|
||||||
|
|
||||||
if (!isset($this->handles[$id])) {
|
if (!isset($this->handles[$id])) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ use GuzzleHttp\Stream\Utils;
|
||||||
class StreamHandler
|
class StreamHandler
|
||||||
{
|
{
|
||||||
private $options;
|
private $options;
|
||||||
|
private $lastHeaders;
|
||||||
|
|
||||||
public function __construct(array $options = [])
|
public function __construct(array $options = [])
|
||||||
{
|
{
|
||||||
|
@ -30,15 +31,17 @@ class StreamHandler
|
||||||
try {
|
try {
|
||||||
// Does not support the expect header.
|
// Does not support the expect header.
|
||||||
$request = Core::removeHeader($request, 'Expect');
|
$request = Core::removeHeader($request, 'Expect');
|
||||||
$stream = $this->createStream($url, $request, $headers);
|
$stream = $this->createStream($url, $request);
|
||||||
return $this->createResponse($request, $url, $headers, $stream);
|
return $this->createResponse($request, $url, $stream);
|
||||||
} catch (RingException $e) {
|
} catch (RingException $e) {
|
||||||
return $this->createErrorResponse($url, $e);
|
return $this->createErrorResponse($url, $e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createResponse(array $request, $url, array $hdrs, $stream)
|
private function createResponse(array $request, $url, $stream)
|
||||||
{
|
{
|
||||||
|
$hdrs = $this->lastHeaders;
|
||||||
|
$this->lastHeaders = null;
|
||||||
$parts = explode(' ', array_shift($hdrs), 3);
|
$parts = explode(' ', array_shift($hdrs), 3);
|
||||||
$response = [
|
$response = [
|
||||||
'status' => $parts[1],
|
'status' => $parts[1],
|
||||||
|
@ -131,13 +134,13 @@ class StreamHandler
|
||||||
$e = new ConnectException($e->getMessage(), 0, $e);
|
$e = new ConnectException($e->getMessage(), 0, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return new CompletedFutureArray([
|
||||||
'status' => null,
|
'status' => null,
|
||||||
'body' => null,
|
'body' => null,
|
||||||
'headers' => [],
|
'headers' => [],
|
||||||
'effective_url' => $url,
|
'effective_url' => $url,
|
||||||
'error' => $e
|
'error' => $e
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,17 +153,25 @@ class StreamHandler
|
||||||
*/
|
*/
|
||||||
private function createResource(callable $callback)
|
private function createResource(callable $callback)
|
||||||
{
|
{
|
||||||
// Turn off error reporting while we try to initiate the request
|
$errors = null;
|
||||||
$level = error_reporting(0);
|
set_error_handler(function ($_, $msg, $file, $line) use (&$errors) {
|
||||||
$resource = call_user_func($callback);
|
$errors[] = [
|
||||||
error_reporting($level);
|
'message' => $msg,
|
||||||
|
'file' => $file,
|
||||||
|
'line' => $line
|
||||||
|
];
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
// If the resource could not be created, then grab the last error and
|
$resource = $callback();
|
||||||
// throw an exception.
|
restore_error_handler();
|
||||||
if (!is_resource($resource)) {
|
|
||||||
|
if (!$resource) {
|
||||||
$message = 'Error creating resource: ';
|
$message = 'Error creating resource: ';
|
||||||
foreach ((array) error_get_last() as $key => $value) {
|
foreach ($errors as $err) {
|
||||||
$message .= "[{$key}] {$value} ";
|
foreach ($err as $key => $value) {
|
||||||
|
$message .= "[$key] $value" . PHP_EOL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
throw new RingException(trim($message));
|
throw new RingException(trim($message));
|
||||||
}
|
}
|
||||||
|
@ -168,11 +179,8 @@ class StreamHandler
|
||||||
return $resource;
|
return $resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createStream(
|
private function createStream($url, array $request)
|
||||||
$url,
|
{
|
||||||
array $request,
|
|
||||||
&$http_response_header
|
|
||||||
) {
|
|
||||||
static $methods;
|
static $methods;
|
||||||
if (!$methods) {
|
if (!$methods) {
|
||||||
$methods = array_flip(get_class_methods(__CLASS__));
|
$methods = array_flip(get_class_methods(__CLASS__));
|
||||||
|
@ -207,8 +215,7 @@ class StreamHandler
|
||||||
$url,
|
$url,
|
||||||
$request,
|
$request,
|
||||||
$options,
|
$options,
|
||||||
$this->createContext($request, $options, $params),
|
$this->createContext($request, $options, $params)
|
||||||
$http_response_header
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +309,7 @@ class StreamHandler
|
||||||
|
|
||||||
private function add_progress(array $request, &$options, $value, &$params)
|
private function add_progress(array $request, &$options, $value, &$params)
|
||||||
{
|
{
|
||||||
$fn = function ($code, $_, $_, $_, $transferred, $total) use ($value) {
|
$fn = function ($code, $_1, $_2, $_3, $transferred, $total) use ($value) {
|
||||||
if ($code == STREAM_NOTIFY_PROGRESS) {
|
if ($code == STREAM_NOTIFY_PROGRESS) {
|
||||||
$value($total, $transferred, null, null);
|
$value($total, $transferred, null, null);
|
||||||
}
|
}
|
||||||
|
@ -316,6 +323,10 @@ class StreamHandler
|
||||||
|
|
||||||
private function add_debug(array $request, &$options, $value, &$params)
|
private function add_debug(array $request, &$options, $value, &$params)
|
||||||
{
|
{
|
||||||
|
if ($value === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static $map = [
|
static $map = [
|
||||||
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
STREAM_NOTIFY_CONNECT => 'CONNECT',
|
||||||
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED',
|
||||||
|
@ -382,16 +393,17 @@ class StreamHandler
|
||||||
$url,
|
$url,
|
||||||
array $request,
|
array $request,
|
||||||
array $options,
|
array $options,
|
||||||
$context,
|
$context
|
||||||
&$http_response_header
|
|
||||||
) {
|
) {
|
||||||
return $this->createResource(
|
return $this->createResource(
|
||||||
function () use ($url, &$http_response_header, $context) {
|
function () use ($url, $context) {
|
||||||
if (false === strpos($url, 'http')) {
|
if (false === strpos($url, 'http')) {
|
||||||
trigger_error("URL is invalid: {$url}", E_USER_WARNING);
|
trigger_error("URL is invalid: {$url}", E_USER_WARNING);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return fopen($url, 'r', null, $context);
|
$resource = fopen($url, 'r', null, $context);
|
||||||
|
$this->lastHeaders = $http_response_header;
|
||||||
|
return $resource;
|
||||||
},
|
},
|
||||||
$request,
|
$request,
|
||||||
$options
|
$options
|
||||||
|
|
|
@ -16,7 +16,7 @@ use React\Promise\PromisorInterface;
|
||||||
* computation has not yet completed when wait() is called, the call to wait()
|
* computation has not yet completed when wait() is called, the call to wait()
|
||||||
* will block until the future has completed.
|
* will block until the future has completed.
|
||||||
*/
|
*/
|
||||||
interface FutureInterface extends PromisorInterface
|
interface FutureInterface extends PromiseInterface, PromisorInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Returns the result of the future either from cache or by blocking until
|
* Returns the result of the future either from cache or by blocking until
|
||||||
|
@ -37,20 +37,4 @@ interface FutureInterface extends PromisorInterface
|
||||||
* Cancels the future, if possible.
|
* Cancels the future, if possible.
|
||||||
*/
|
*/
|
||||||
public function cancel();
|
public function cancel();
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and return a promise that invokes the given methods when the
|
|
||||||
* future has a value, exception, or progress events.
|
|
||||||
*
|
|
||||||
* @param callable $onFulfilled Called when the promise is resolved.
|
|
||||||
* @param callable $onRejected Called when the promise is rejected.
|
|
||||||
* @param callable $onProgress Called on progress events.
|
|
||||||
*
|
|
||||||
* @return PromiseInterface
|
|
||||||
*/
|
|
||||||
public function then(
|
|
||||||
callable $onFulfilled = null,
|
|
||||||
callable $onRejected = null,
|
|
||||||
callable $onProgress = null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,10 @@ class StreamHandlerTest extends \PHPUnit_Framework_TestCase
|
||||||
'headers' => ['host' => ['localhost:123']],
|
'headers' => ['host' => ['localhost:123']],
|
||||||
'client' => ['timeout' => 0.01],
|
'client' => ['timeout' => 0.01],
|
||||||
]);
|
]);
|
||||||
|
$this->assertInstanceOf(
|
||||||
|
'GuzzleHttp\Ring\Future\CompletedFutureArray',
|
||||||
|
$result
|
||||||
|
);
|
||||||
$this->assertNull($result['status']);
|
$this->assertNull($result['status']);
|
||||||
$this->assertNull($result['body']);
|
$this->assertNull($result['body']);
|
||||||
$this->assertEquals([], $result['headers']);
|
$this->assertEquals([], $result['headers']);
|
||||||
|
|
|
@ -21,6 +21,15 @@
|
||||||
* <
|
* <
|
||||||
* < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}]
|
* < [{'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
|
* - Shutdown the server
|
||||||
* > DELETE /guzzle-server
|
* > DELETE /guzzle-server
|
||||||
* > Host: 127.0.0.1:8125
|
* > Host: 127.0.0.1:8125
|
||||||
|
@ -44,6 +53,77 @@ var GuzzleServer = function(port, log) {
|
||||||
this.requests = [];
|
this.requests = [];
|
||||||
var that = this;
|
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) {
|
var controlRequest = function(request, req, res) {
|
||||||
if (req.url == '/guzzle-server/perf') {
|
if (req.url == '/guzzle-server/perf') {
|
||||||
res.writeHead(200, 'OK', {'Content-Length': 16});
|
res.writeHead(200, 'OK', {'Content-Length': 16});
|
||||||
|
@ -140,7 +220,7 @@ var GuzzleServer = function(port, log) {
|
||||||
|
|
||||||
// Called when the request completes
|
// Called when the request completes
|
||||||
req.addListener('end', function() {
|
req.addListener('end', function() {
|
||||||
receivedRequest(request, req, res);
|
firewallRequest(request, req, res, receivedRequest);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue