Issue #2317845 by sun | Arla: Upgrade Guzzle to version 4.1.7.
parent
453a4451b7
commit
cdf77e49f8
|
@ -459,21 +459,21 @@
|
|||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "4.1.3",
|
||||
"version": "4.1.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "012b2aecbda4e38f119c19580898685851015fa7"
|
||||
"reference": "448f2c2076cf0fb756230611491c4f7ecb735a29"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/012b2aecbda4e38f119c19580898685851015fa7",
|
||||
"reference": "012b2aecbda4e38f119c19580898685851015fa7",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/448f2c2076cf0fb756230611491c4f7ecb735a29",
|
||||
"reference": "448f2c2076cf0fb756230611491c4f7ecb735a29",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/streams": "~1.3",
|
||||
"guzzlehttp/streams": "~1.4",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
@ -487,7 +487,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1.x-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -520,20 +520,20 @@
|
|||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"time": "2014-07-16 03:01:02"
|
||||
"time": "2014-08-08 01:30:43"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/streams",
|
||||
"version": "1.3.0",
|
||||
"version": "1.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/streams.git",
|
||||
"reference": "d6aaa91cfdbae86355dd2a168a3ca536755898a2"
|
||||
"reference": "fb0d1ee29987c2bdc59867bffaade6fc88c2675f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/streams/zipball/d6aaa91cfdbae86355dd2a168a3ca536755898a2",
|
||||
"reference": "d6aaa91cfdbae86355dd2a168a3ca536755898a2",
|
||||
"url": "https://api.github.com/repos/guzzle/streams/zipball/fb0d1ee29987c2bdc59867bffaade6fc88c2675f",
|
||||
"reference": "fb0d1ee29987c2bdc59867bffaade6fc88c2675f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -545,7 +545,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
"dev-master": "1.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -573,7 +573,7 @@
|
|||
"Guzzle",
|
||||
"stream"
|
||||
],
|
||||
"time": "2014-07-15 22:02:02"
|
||||
"time": "2014-08-10 23:57:01"
|
||||
},
|
||||
{
|
||||
"name": "kriswallsmith/assetic",
|
||||
|
@ -2448,6 +2448,7 @@
|
|||
"symfony-cmf/routing": 15,
|
||||
"phpunit/phpunit-mock-objects": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"platform": {
|
||||
"php": ">=5.4.2"
|
||||
},
|
||||
|
|
|
@ -143,6 +143,8 @@ class ClassLoader
|
|||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
|
@ -202,10 +204,13 @@ class ClassLoader
|
|||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths) {
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
|
|
|
@ -23,9 +23,6 @@ class ComposerAutoloaderInitDrupal8
|
|||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitDrupal8', 'loadClassLoader'));
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname(dirname($vendorDir));
|
||||
|
||||
$includePaths = require __DIR__ . '/include_paths.php';
|
||||
array_push($includePaths, get_include_path());
|
||||
set_include_path(join(PATH_SEPARATOR, $includePaths));
|
||||
|
|
|
@ -2300,128 +2300,6 @@
|
|||
"xunit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/streams",
|
||||
"version": "1.3.0",
|
||||
"version_normalized": "1.3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/streams.git",
|
||||
"reference": "d6aaa91cfdbae86355dd2a168a3ca536755898a2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/streams/zipball/d6aaa91cfdbae86355dd2a168a3ca536755898a2",
|
||||
"reference": "d6aaa91cfdbae86355dd2a168a3ca536755898a2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"time": "2014-07-15 22:02:02",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Stream\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"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 abstraction over streams of data (Guzzle 4+)",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": [
|
||||
"Guzzle",
|
||||
"stream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "4.1.3",
|
||||
"version_normalized": "4.1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "012b2aecbda4e38f119c19580898685851015fa7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/012b2aecbda4e38f119c19580898685851015fa7",
|
||||
"reference": "012b2aecbda4e38f119c19580898685851015fa7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/streams": "~1.3",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "~4.0",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Guzzle will use specific adapters if cURL is present"
|
||||
},
|
||||
"time": "2014-07-16 03:01:02",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"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": "symfony/serializer",
|
||||
"version": "v2.5.2",
|
||||
|
@ -2512,5 +2390,127 @@
|
|||
"BSD"
|
||||
],
|
||||
"homepage": "http://vfs.bovigo.org/"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/streams",
|
||||
"version": "1.5.1",
|
||||
"version_normalized": "1.5.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/streams.git",
|
||||
"reference": "fb0d1ee29987c2bdc59867bffaade6fc88c2675f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/streams/zipball/fb0d1ee29987c2bdc59867bffaade6fc88c2675f",
|
||||
"reference": "fb0d1ee29987c2bdc59867bffaade6fc88c2675f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"time": "2014-08-10 23:57:01",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.5-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Stream\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"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 abstraction over streams of data (Guzzle 4+)",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": [
|
||||
"Guzzle",
|
||||
"stream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "4.1.7",
|
||||
"version_normalized": "4.1.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "448f2c2076cf0fb756230611491c4f7ecb735a29"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/448f2c2076cf0fb756230611491c4f7ecb735a29",
|
||||
"reference": "448f2c2076cf0fb756230611491c4f7ecb735a29",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/streams": "~1.4",
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "~4.0",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "Guzzle will use specific adapters if cURL is present"
|
||||
},
|
||||
"time": "2014-08-08 01:30:43",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
]
|
||||
},
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -10,7 +10,8 @@ before_script:
|
|||
- curl --version
|
||||
- pear config-set php_ini ~/.phpenv/versions/`php -r 'echo phpversion();'`/etc/php.ini || echo 'Error modifying PEAR'
|
||||
- pecl install uri_template || echo 'Error installing uri_template'
|
||||
- composer install
|
||||
- composer self-update
|
||||
- composer install --no-interaction --prefer-source --dev
|
||||
- ~/.nvm/nvm.sh install v0.6.14
|
||||
- ~/.nvm/nvm.sh run v0.6.14
|
||||
|
||||
|
|
|
@ -1,6 +1,49 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.1.7 (2014-08-07)
|
||||
------------------
|
||||
|
||||
* Fixed an error in the HistoryPlugin that caused the same request and response
|
||||
to be logged multiple times when an HTTP protocol error occurs.
|
||||
* Ensuring that cURL does not add a default Content-Type when no Content-Type
|
||||
has been supplied by the user. This prevents the adapter layer from modifying
|
||||
the request that is sent over the wire after any listeners may have already
|
||||
put the request in a desired state (e.g., signed the request).
|
||||
* Throwing an exception when you attempt to send requests that have the
|
||||
"stream" set to true in parallel using the MultiAdapter.
|
||||
* Only calling curl_multi_select when there are active cURL handles. This was
|
||||
previously changed and caused performance problems on some systems due to PHP
|
||||
always selecting until the maximum select timeout.
|
||||
* Fixed a bug where multipart/form-data POST fields were not correctly
|
||||
aggregated (e.g., values with "&").
|
||||
|
||||
4.1.6 (2014-08-03)
|
||||
------------------
|
||||
|
||||
* Added helper methods to make it easier to represent messages as strings,
|
||||
including getting the start line and getting headers as a string.
|
||||
|
||||
4.1.5 (2014-08-02)
|
||||
------------------
|
||||
|
||||
* Automatically retrying cURL "Connection died, retrying a fresh connect"
|
||||
errors when possible.
|
||||
* cURL implementation cleanup
|
||||
* Allowing multiple event subscriber listeners to be registered per event by
|
||||
passing an array of arrays of listener configuration.
|
||||
|
||||
4.1.4 (2014-07-22)
|
||||
------------------
|
||||
|
||||
* Fixed a bug that caused multi-part POST requests with more than one field to
|
||||
serialize incorrectly.
|
||||
* Paths can now be set to "0"
|
||||
* `ResponseInterface::xml` now accepts a `libxml_options` option and added a
|
||||
missing default argument that was required when parsing XML response bodies.
|
||||
* A `save_to` stream is now created lazily, which means that files are not
|
||||
created on disk unless a request succeeds.
|
||||
|
||||
4.1.3 (2014-07-15)
|
||||
------------------
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/streams": "~1.3"
|
||||
"guzzlehttp/streams": "~1.4"
|
||||
},
|
||||
|
||||
"suggest": {
|
||||
|
@ -43,7 +43,7 @@
|
|||
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1.x-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,7 +297,7 @@ immeditaley and prevent subsequent requests from being sent.
|
|||
use GuzzleHttp\Event\ErrorEvent;
|
||||
|
||||
$client->sendAll($requests, [
|
||||
'error' => function (ErrorEvent $event) use (&$errors) {
|
||||
'error' => function (ErrorEvent $event) {
|
||||
throw $event->getException();
|
||||
}
|
||||
]);
|
||||
|
|
|
@ -184,7 +184,7 @@ priority of the listener (as shown in the ``before`` listener in the example).
|
|||
|
||||
.. code-block:: php
|
||||
|
||||
use GuzzleHttp\Event\EventEmitterInterface;
|
||||
use GuzzleHttp\Event\EmitterInterface;
|
||||
use GuzzleHttp\Event\SubscriberInterface;
|
||||
use GuzzleHttp\Event\BeforeEvent;
|
||||
use GuzzleHttp\Event\CompleteEvent;
|
||||
|
@ -194,8 +194,11 @@ priority of the listener (as shown in the ``before`` listener in the example).
|
|||
public function getEvents()
|
||||
{
|
||||
return [
|
||||
'before' => ['onBefore', 100], // Provide name and optional priority
|
||||
'complete' => ['onComplete']
|
||||
// Provide name and optional priority
|
||||
'before' => ['onBefore', 100],
|
||||
'complete' => ['onComplete'],
|
||||
// You can pass a list of listeners with different priorities
|
||||
'error' => [['beforeError', 'first'], ['afterError', 'last]]
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ then you'll need to use a ``GuzzleHttp\ClientInterface`` object.
|
|||
use GuzzleHttp\Client;
|
||||
|
||||
$client = new Client();
|
||||
$response = $client->get('https://github.com/timeline.json');
|
||||
$response = $client->get('http://httpbin.org/get');
|
||||
|
||||
// You can use the same methods you saw in the procedural API
|
||||
$response = $client->delete('http://httpbin.org/delete');
|
||||
|
@ -120,9 +120,9 @@ response.
|
|||
|
||||
.. code-block:: php
|
||||
|
||||
$response = $client->get('https://github.com/timeline.json');
|
||||
$response = $client->get('http://httpbin.org/get');
|
||||
$json = $response->json();
|
||||
var_dump($json[0]['repository']);
|
||||
var_dump($json[0]['origin']);
|
||||
|
||||
Guzzle internally uses PHP's ``json_decode()`` function to parse responses. If
|
||||
Guzzle is unable to parse the JSON response body, then a
|
||||
|
|
|
@ -59,6 +59,16 @@ class BatchContext
|
|||
throw new AdapterException('No curl handle was found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any active requests.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
return count($this->handles) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are any remaining pending transactions
|
||||
*
|
||||
|
@ -143,16 +153,15 @@ class BatchContext
|
|||
}
|
||||
|
||||
$handle = $this->handles[$transaction];
|
||||
|
||||
$this->handles->detach($transaction);
|
||||
$info = curl_getinfo($handle);
|
||||
$code = curl_multi_remove_handle($this->multi, $handle);
|
||||
if ($code != CURLM_OK) {
|
||||
curl_close($handle);
|
||||
|
||||
if ($code !== CURLM_OK) {
|
||||
MultiAdapter::throwMultiError($code);
|
||||
}
|
||||
|
||||
$info = curl_getinfo($handle);
|
||||
curl_close($handle);
|
||||
unset($this->handles[$transaction]);
|
||||
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,11 @@ class CurlFactory
|
|||
$this->removeHeader('Accept-Encoding', $options);
|
||||
}
|
||||
|
||||
// cURL sometimes adds a content-type by default. Prevent this.
|
||||
if (!$request->hasHeader('Content-Type')) {
|
||||
$options[CURLOPT_HTTPHEADER][] = 'Content-Type:';
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
@ -273,6 +278,18 @@ class CurlFactory
|
|||
$options[CURLOPT_SSLKEY] = $value;
|
||||
}
|
||||
|
||||
private function add_stream()
|
||||
{
|
||||
throw new AdapterException('cURL adapters do not support the "stream"'
|
||||
. ' request option. This error is typically encountered when trying'
|
||||
. ' to send requests with the "stream" option set to true in '
|
||||
. ' parallel. You will either need to send these one at a time or'
|
||||
. ' implement a custom ParallelAdapterInterface that supports'
|
||||
. ' sending these types of requests in parallel. This error can'
|
||||
. ' also occur if the StreamAdapter is not available on your'
|
||||
. ' system (e.g., allow_url_fopen is disabled in your php.ini).');
|
||||
}
|
||||
|
||||
private function add_save_to(
|
||||
RequestInterface $request,
|
||||
RequestMediator $mediator,
|
||||
|
@ -280,7 +297,7 @@ class CurlFactory
|
|||
$value
|
||||
) {
|
||||
$mediator->setResponseBody(is_string($value)
|
||||
? Stream\create(fopen($value, 'w'))
|
||||
? new Stream\LazyOpenStream($value, 'w')
|
||||
: Stream\create($value));
|
||||
}
|
||||
|
||||
|
|
|
@ -128,20 +128,25 @@ class MultiAdapter implements AdapterInterface, ParallelAdapterInterface
|
|||
$multi = $context->getMultiHandle();
|
||||
|
||||
do {
|
||||
while (($mrc = curl_multi_exec($multi, $active)) == CURLM_CALL_MULTI_PERFORM);
|
||||
if ($mrc != CURLM_OK && $mrc != CURLM_CALL_MULTI_PERFORM) {
|
||||
do {
|
||||
$mrc = curl_multi_exec($multi, $active);
|
||||
} while ($mrc === CURLM_CALL_MULTI_PERFORM);
|
||||
|
||||
if ($mrc != CURLM_OK) {
|
||||
self::throwMultiError($mrc);
|
||||
}
|
||||
// Need to check if there are pending transactions before processing
|
||||
// them so that we don't bail from the loop too early.
|
||||
$pending = $context->hasPending();
|
||||
|
||||
$this->processMessages($context);
|
||||
if ($active && curl_multi_select($multi, $this->selectTimeout) === -1) {
|
||||
|
||||
if ($active &&
|
||||
curl_multi_select($multi, $this->selectTimeout) === -1
|
||||
) {
|
||||
// Perform a usleep if a select returns -1.
|
||||
// See: https://bugs.php.net/bug.php?id=61141
|
||||
usleep(250);
|
||||
}
|
||||
} while ($active || $pending);
|
||||
|
||||
} while ($context->isActive() || $active);
|
||||
|
||||
$this->releaseMultiHandle($multi);
|
||||
}
|
||||
|
@ -168,7 +173,9 @@ class MultiAdapter implements AdapterInterface, ParallelAdapterInterface
|
|||
$info = $context->removeTransaction($transaction);
|
||||
|
||||
try {
|
||||
if (!$this->isCurlException($transaction, $curl, $context, $info)) {
|
||||
if (!$this->isCurlException($transaction, $curl, $context, $info) &&
|
||||
$this->validateResponseWasSet($transaction, $context)
|
||||
) {
|
||||
RequestEvents::emitComplete($transaction, $info);
|
||||
}
|
||||
} catch (RequestException $e) {
|
||||
|
@ -281,4 +288,75 @@ class MultiAdapter implements AdapterInterface, ParallelAdapterInterface
|
|||
unset($this->multiHandles[$id], $this->multiOwned[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function ensures that a response was set on a transaction. If one
|
||||
* was not set, then the request is retried if possible. This error
|
||||
* typically means you are sending a payload, curl encountered a
|
||||
* "Connection died, retrying a fresh connect" error, tried to rewind the
|
||||
* stream, and then encountered a "necessary data rewind wasn't possible"
|
||||
* error, causing the request to be sent through curl_multi_info_read()
|
||||
* without an error status.
|
||||
*
|
||||
* @param TransactionInterface $transaction
|
||||
* @param BatchContext $context
|
||||
*
|
||||
* @return bool Returns true if it's OK, and false if it failed.
|
||||
* @throws \GuzzleHttp\Exception\RequestException If it failed and cannot
|
||||
* recover.
|
||||
*/
|
||||
private function validateResponseWasSet(
|
||||
TransactionInterface $transaction,
|
||||
BatchContext $context
|
||||
) {
|
||||
if ($transaction->getResponse()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$body = $transaction->getRequest()->getBody();
|
||||
|
||||
if (!$body) {
|
||||
// This is weird and should probably never happen.
|
||||
RequestEvents::emitError(
|
||||
$transaction,
|
||||
new RequestException(
|
||||
'No response was received for a request with no body. This'
|
||||
. ' could mean that you are saturating your network.',
|
||||
$transaction->getRequest()
|
||||
)
|
||||
);
|
||||
} elseif (!$body->isSeekable() || !$body->seek(0)) {
|
||||
// Nothing we can do with this. Sorry!
|
||||
RequestEvents::emitError(
|
||||
$transaction,
|
||||
new RequestException(
|
||||
'The connection was unexpectedly closed. The request would'
|
||||
. ' have been retried, but attempting to rewind the'
|
||||
. ' request body failed. Consider wrapping your request'
|
||||
. ' body in a CachingStream decorator to work around this'
|
||||
. ' issue if necessary.',
|
||||
$transaction->getRequest()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->retryFailedConnection($transaction, $context);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function retryFailedConnection(
|
||||
TransactionInterface $transaction,
|
||||
BatchContext $context
|
||||
) {
|
||||
// Add the request back to the batch to retry automatically.
|
||||
$context->addTransaction(
|
||||
$transaction,
|
||||
call_user_func(
|
||||
$this->curlFactory,
|
||||
$transaction,
|
||||
$this->messageFactory
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ class StreamAdapter implements AdapterInterface
|
|||
if ($saveTo = $request->getConfig()['save_to']) {
|
||||
// Stream the response into the destination stream
|
||||
$saveTo = is_string($saveTo)
|
||||
? Stream\create(fopen($saveTo, 'r+'))
|
||||
? new Stream\LazyOpenStream($saveTo, 'r+')
|
||||
: Stream\create($saveTo);
|
||||
} else {
|
||||
// Stream into the default temp stream
|
||||
|
@ -151,7 +151,7 @@ class StreamAdapter implements AdapterInterface
|
|||
if (isset($options['http']['proxy'])) {
|
||||
$message .= "[proxy] {$options['http']['proxy']} ";
|
||||
}
|
||||
foreach (error_get_last() as $key => $value) {
|
||||
foreach ((array) error_get_last() as $key => $value) {
|
||||
$message .= "[{$key}] {$value} ";
|
||||
}
|
||||
throw new RequestException(trim($message), $request);
|
||||
|
@ -316,13 +316,13 @@ class StreamAdapter implements AdapterInterface
|
|||
array $options,
|
||||
array $params
|
||||
) {
|
||||
return $this->createResource(function () use (
|
||||
return $this->createResource(
|
||||
function () use ($request, $options, $params) {
|
||||
return stream_context_create($options, $params);
|
||||
},
|
||||
$request,
|
||||
$options,
|
||||
$params
|
||||
) {
|
||||
return stream_context_create($options, $params);
|
||||
}, $request, $options);
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
private function createStreamResource(
|
||||
|
@ -337,16 +337,16 @@ class StreamAdapter implements AdapterInterface
|
|||
$url = 'compress.zlib://' . $url;
|
||||
}
|
||||
|
||||
return $this->createResource(function () use (
|
||||
$url,
|
||||
&$http_response_header,
|
||||
$context
|
||||
) {
|
||||
if (false === strpos($url, 'http')) {
|
||||
trigger_error("URL is invalid: {$url}", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
return fopen($url, 'r', null, $context);
|
||||
}, $request, $options);
|
||||
return $this->createResource(
|
||||
function () use ($url, &$http_response_header, $context) {
|
||||
if (false === strpos($url, 'http')) {
|
||||
trigger_error("URL is invalid: {$url}", E_USER_WARNING);
|
||||
return null;
|
||||
}
|
||||
return fopen($url, 'r', null, $context);
|
||||
},
|
||||
$request,
|
||||
$options
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use GuzzleHttp\Exception\AdapterException;
|
|||
*/
|
||||
interface ClientInterface extends HasEmitterInterface
|
||||
{
|
||||
const VERSION = '4.1.3';
|
||||
const VERSION = '4.1.7';
|
||||
|
||||
/**
|
||||
* Create and return a new {@see RequestInterface} object.
|
||||
|
|
|
@ -114,12 +114,22 @@ class Emitter implements EmitterInterface
|
|||
|
||||
public function attach(SubscriberInterface $subscriber)
|
||||
{
|
||||
foreach ($subscriber->getEvents() as $eventName => $listener) {
|
||||
$this->on(
|
||||
$eventName,
|
||||
array($subscriber, $listener[0]),
|
||||
isset($listener[1]) ? $listener[1] : 0
|
||||
);
|
||||
foreach ($subscriber->getEvents() as $eventName => $listeners) {
|
||||
if (is_array($listeners[0])) {
|
||||
foreach ($listeners as $listener) {
|
||||
$this->on(
|
||||
$eventName,
|
||||
[$subscriber, $listener[0]],
|
||||
isset($listener[1]) ? $listener[1] : 0
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->on(
|
||||
$eventName,
|
||||
[$subscriber, $listeners[0]],
|
||||
isset($listeners[1]) ? $listeners[1] : 0
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,13 +17,17 @@ interface SubscriberInterface
|
|||
*
|
||||
* The returned array keys MUST map to an event name. Each array value
|
||||
* MUST be an array in which the first element is the name of a function
|
||||
* on the EventSubscriber. The second element in the array is optional, and
|
||||
* if specified, designates the event priority.
|
||||
* on the EventSubscriber OR an array of arrays in the aforementioned
|
||||
* format. The second element in the array is optional, and if specified,
|
||||
* designates the event priority.
|
||||
*
|
||||
* For example:
|
||||
* For example, the following are all valid:
|
||||
*
|
||||
* - ['eventName' => ['methodName']]
|
||||
* - ['eventName' => ['methodName', $priority]]
|
||||
* - ['eventName' => [['methodName'], ['otherMethod']]
|
||||
* - ['eventName' => [['methodName'], ['otherMethod', $priority]]
|
||||
* - ['eventName' => [['methodName', $priority], ['otherMethod', $priority]]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Message;
|
||||
|
||||
use GuzzleHttp\Stream\StreamInterface;
|
||||
|
@ -20,12 +19,8 @@ abstract class AbstractMessage implements MessageInterface
|
|||
|
||||
public function __toString()
|
||||
{
|
||||
$result = $this->getStartLine();
|
||||
foreach ($this->getHeaders() as $name => $values) {
|
||||
$result .= "\r\n{$name}: " . implode(', ', $values);
|
||||
}
|
||||
|
||||
return $result . "\r\n\r\n" . $this->body;
|
||||
return static::getStartLineAndHeaders($this)
|
||||
. "\r\n\r\n" . $this->getBody();
|
||||
}
|
||||
|
||||
public function getProtocolVersion()
|
||||
|
@ -214,11 +209,57 @@ abstract class AbstractMessage implements MessageInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the start line of a message.
|
||||
* Gets the start-line and headers of a message as a string
|
||||
*
|
||||
* @param MessageInterface $message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getStartLine();
|
||||
public static function getStartLineAndHeaders(MessageInterface $message)
|
||||
{
|
||||
return static::getStartLine($message)
|
||||
. self::getHeadersAsString($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the headers of a message as a string
|
||||
*
|
||||
* @param MessageInterface $message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getHeadersAsString(MessageInterface $message)
|
||||
{
|
||||
$result = '';
|
||||
foreach ($message->getHeaders() as $name => $values) {
|
||||
$result .= "\r\n{$name}: " . implode(', ', $values);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start line of a message
|
||||
*
|
||||
* @param MessageInterface $message
|
||||
*
|
||||
* @return string
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function getStartLine(MessageInterface $message)
|
||||
{
|
||||
if ($message instanceof RequestInterface) {
|
||||
return trim($message->getMethod() . ' '
|
||||
. $message->getResource())
|
||||
. ' HTTP/' . $message->getProtocolVersion();
|
||||
} elseif ($message instanceof ResponseInterface) {
|
||||
return 'HTTP/' . $message->getProtocolVersion() . ' '
|
||||
. $message->getStatusCode() . ' '
|
||||
. $message->getReasonPhrase();
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Unknown message type');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts and modifies the options provided to the message in the
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Message;
|
||||
|
||||
use GuzzleHttp\Event\HasEmitterTrait;
|
||||
|
@ -179,12 +178,6 @@ class Request extends AbstractMessage implements RequestInterface
|
|||
}
|
||||
}
|
||||
|
||||
protected function getStartLine()
|
||||
{
|
||||
return trim($this->method . ' ' . $this->getResource())
|
||||
. ' HTTP/' . $this->getProtocolVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a subscriber that ensures a request's body is prepared before
|
||||
* sending.
|
||||
|
|
|
@ -150,7 +150,8 @@ class Response extends AbstractMessage implements ResponseInterface
|
|||
// Allow XML to be retrieved even if there is no response body
|
||||
$xml = new \SimpleXMLElement(
|
||||
(string) $this->getBody() ?: '<root />',
|
||||
LIBXML_NONET,
|
||||
isset($config['libxml_options']) ? $config['libxml_options'] : LIBXML_NONET,
|
||||
false,
|
||||
isset($config['ns']) ? $config['ns'] : '',
|
||||
isset($config['ns_is_prefix']) ? $config['ns_is_prefix'] : false
|
||||
);
|
||||
|
@ -193,10 +194,4 @@ class Response extends AbstractMessage implements ResponseInterface
|
|||
$this->reasonPhrase = $options['reason_phrase'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function getStartLine()
|
||||
{
|
||||
return 'HTTP/' . $this->getProtocolVersion()
|
||||
. " {$this->statusCode} {$this->reasonPhrase}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,9 @@ interface ResponseInterface extends MessageInterface
|
|||
* - ns: Set to a string to represent the namespace prefix or URI
|
||||
* - ns_is_prefix: Set to true to specify that the NS is a prefix rather
|
||||
* than a URI (defaults to false).
|
||||
* - libxml_options: Bitwise OR of the libxml option constants
|
||||
* listed at http://php.net/manual/en/libxml.constants.php
|
||||
* (defaults to LIBXML_NONET)
|
||||
*
|
||||
* @return \SimpleXMLElement
|
||||
* @throws \RuntimeException if the response body is not in XML format
|
||||
|
|
|
@ -9,13 +9,13 @@ use GuzzleHttp\Stream;
|
|||
*/
|
||||
class MultipartBody implements Stream\StreamInterface
|
||||
{
|
||||
/** @var Stream\StreamInterface */
|
||||
private $stream;
|
||||
use Stream\StreamDecoratorTrait;
|
||||
|
||||
private $boundary;
|
||||
|
||||
/**
|
||||
* @param array $fields Associative array of field names to values where
|
||||
* each value is a string.
|
||||
* each value is a string or array of strings.
|
||||
* @param array $files Associative array of PostFileInterface objects
|
||||
* @param string $boundary You can optionally provide a specific boundary
|
||||
* @throws \InvalidArgumentException
|
||||
|
@ -26,17 +26,7 @@ class MultipartBody implements Stream\StreamInterface
|
|||
$boundary = null
|
||||
) {
|
||||
$this->boundary = $boundary ?: uniqid();
|
||||
$this->createStream($fields, $files);
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->stream;
|
||||
}
|
||||
|
||||
public function getContents($maxLength = -1)
|
||||
{
|
||||
return $this->stream->getContents($maxLength);
|
||||
$this->stream = $this->createStream($fields, $files);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,63 +39,11 @@ class MultipartBody implements Stream\StreamInterface
|
|||
return $this->boundary;
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
$this->stream->close();
|
||||
$this->detach();
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
$this->stream->detach();
|
||||
$this->size = 0;
|
||||
}
|
||||
|
||||
public function eof()
|
||||
{
|
||||
return $this->stream->eof();
|
||||
}
|
||||
|
||||
public function tell()
|
||||
{
|
||||
return $this->stream->tell();
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isWritable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isSeekable()
|
||||
{
|
||||
return $this->stream->isSeekable();
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $this->stream->getSize();
|
||||
}
|
||||
|
||||
public function read($length)
|
||||
{
|
||||
return $this->stream->read($length);
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
return $this->stream->seek($offset, $whence);
|
||||
}
|
||||
|
||||
public function write($string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string needed to transfer a POST field
|
||||
*/
|
||||
|
@ -137,12 +75,14 @@ class MultipartBody implements Stream\StreamInterface
|
|||
*/
|
||||
private function createStream(array $fields, array $files)
|
||||
{
|
||||
$this->stream = new Stream\AppendStream();
|
||||
$stream = new Stream\AppendStream();
|
||||
|
||||
foreach ($fields as $name => $field) {
|
||||
$this->stream->addStream(
|
||||
Stream\create($this->getFieldString($name, $field))
|
||||
);
|
||||
foreach ($fields as $name => $fieldValues) {
|
||||
foreach ((array) $fieldValues as $value) {
|
||||
$stream->addStream(
|
||||
Stream\create($this->getFieldString($name, $value))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
|
@ -152,14 +92,16 @@ class MultipartBody implements Stream\StreamInterface
|
|||
. 'implement PostFieldInterface');
|
||||
}
|
||||
|
||||
$this->stream->addStream(
|
||||
$stream->addStream(
|
||||
Stream\create($this->getFileHeaders($file))
|
||||
);
|
||||
$this->stream->addStream($file->getContent());
|
||||
$this->stream->addStream(Stream\create("\r\n"));
|
||||
$stream->addStream($file->getContent());
|
||||
$stream->addStream(Stream\create("\r\n"));
|
||||
}
|
||||
|
||||
// Add the trailing boundary
|
||||
$this->stream->addStream(Stream\create("--{$this->boundary}--"));
|
||||
$stream->addStream(Stream\create("--{$this->boundary}--"));
|
||||
|
||||
return $stream;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Post;
|
||||
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
|
@ -253,22 +252,10 @@ class PostBody implements PostBodyInterface
|
|||
private function createMultipart()
|
||||
{
|
||||
// Flatten the nested query string values using the correct aggregator
|
||||
if (!$this->fields) {
|
||||
$fields = [];
|
||||
} else {
|
||||
$query = (string) (new Query($this->fields))
|
||||
->setEncodingType(false)
|
||||
->setAggregator($this->getAggregator());
|
||||
|
||||
// Convert the flattened query string back into an array
|
||||
$fields = [];
|
||||
foreach (explode('&', $query, 2) as $kvp) {
|
||||
$parts = explode('=', $kvp, 2);
|
||||
$fields[$parts[0]] = isset($parts[1]) ? $parts[1] : null;
|
||||
}
|
||||
}
|
||||
|
||||
return new MultipartBody($fields, $this->files);
|
||||
return new MultipartBody(
|
||||
call_user_func($this->getAggregator(), $this->fields),
|
||||
$this->files
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,7 +56,11 @@ class History implements SubscriberInterface, \IteratorAggregate, \Countable
|
|||
|
||||
public function onError(ErrorEvent $event)
|
||||
{
|
||||
$this->add($event->getRequest(), $event->getResponse());
|
||||
// Only track when no response is present, meaning this didn't ever
|
||||
// emit a complete event
|
||||
if (!$event->getResponse()) {
|
||||
$this->add($event->getRequest());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -555,7 +555,7 @@ class Url
|
|||
);
|
||||
}
|
||||
|
||||
if (!$parts['path']) {
|
||||
if (!$parts['path'] && $parts['path'] !== '0') {
|
||||
// The relative URL has no path, so check if it is just a query
|
||||
$path = $this->path ?: '';
|
||||
$query = count($parts['query']) ? $parts['query'] : $this->query;
|
||||
|
|
|
@ -51,8 +51,10 @@ class BatchContextTest extends \PHPUnit_Framework_TestCase
|
|||
new Request('GET', 'http://httbin.org')
|
||||
);
|
||||
$b->addTransaction($t, $h);
|
||||
$this->assertTrue($b->isActive());
|
||||
$this->assertSame($t, $b->findTransaction($h));
|
||||
$b->removeTransaction($t);
|
||||
$this->assertFalse($b->isActive());
|
||||
try {
|
||||
$this->assertEquals([], $b->findTransaction($h));
|
||||
$this->fail('Did not throw');
|
||||
|
|
|
@ -9,6 +9,7 @@ use GuzzleHttp\Adapter\Transaction;
|
|||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Event\ErrorEvent;
|
||||
use GuzzleHttp\Event\HeadersEvent;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use GuzzleHttp\Message\MessageFactory;
|
||||
use GuzzleHttp\Message\Request;
|
||||
use GuzzleHttp\Event\BeforeEvent;
|
||||
|
@ -117,4 +118,22 @@ class CurlAdapterTest extends AbstractCurl
|
|||
$a->send($transaction);
|
||||
$this->assertCount(2, $this->readAttribute($a, 'handles'));
|
||||
}
|
||||
|
||||
public function testDoesNotSaveToWhenFailed()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue([
|
||||
"HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n"
|
||||
]);
|
||||
|
||||
$tmp = tempnam('/tmp', 'test_save_to');
|
||||
unlink($tmp);
|
||||
$a = new CurlAdapter(new MessageFactory());
|
||||
$client = new Client(['base_url' => Server::$url, 'adapter' => $a]);
|
||||
try {
|
||||
$client->get('/', ['save_to' => $tmp]);
|
||||
} catch (ServerException $e) {
|
||||
$this->assertFileNotExists($tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace GuzzleHttp\Tests\Adapter\Curl {
|
|||
|
||||
use GuzzleHttp\Adapter\Curl\MultiAdapter;
|
||||
use GuzzleHttp\Event\BeforeEvent;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use GuzzleHttp\Message\RequestInterface;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
use GuzzleHttp\Adapter\Curl\CurlFactory;
|
||||
|
@ -308,5 +309,27 @@ namespace GuzzleHttp\Tests\Adapter\Curl {
|
|||
$event = new BeforeEvent(new Transaction(new Client(), $request));
|
||||
$request->getEmitter()->emit('before', $event);
|
||||
}
|
||||
|
||||
public function testDoesNotAlwaysAddContentType()
|
||||
{
|
||||
Server::flush();
|
||||
Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"]);
|
||||
$client = new Client();
|
||||
$client->put(Server::$url . '/foo', ['body' => 'foo']);
|
||||
$request = Server::received(true)[0];
|
||||
$this->assertEquals('', $request->getHeader('Content-Type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \GuzzleHttp\Exception\AdapterException
|
||||
*/
|
||||
public function testThrowsForStreamOption()
|
||||
{
|
||||
$request = new Request('GET', Server::$url . 'haha');
|
||||
$request->getConfig()->set('stream', true);
|
||||
$t = new Transaction(new Client(), $request);
|
||||
$f = new CurlFactory();
|
||||
$f($t, new MessageFactory());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ use GuzzleHttp\Event\ErrorEvent;
|
|||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Message\MessageFactory;
|
||||
use GuzzleHttp\Message\Request;
|
||||
use GuzzleHttp\Message\Response;
|
||||
use GuzzleHttp\Stream\NoSeekStream;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
use GuzzleHttp\Tests\Server;
|
||||
|
||||
/**
|
||||
|
@ -193,4 +196,127 @@ class MultiAdapterTest extends AbstractCurl
|
|||
$this->assertSame($request, $e->getRequest());
|
||||
}
|
||||
}
|
||||
|
||||
public function testEnsuresResponseWasSetForGet()
|
||||
{
|
||||
$client = new Client();
|
||||
$request = $client->createRequest('GET', Server::$url);
|
||||
$response = new Response(200, []);
|
||||
$er = null;
|
||||
|
||||
$request->getEmitter()->on(
|
||||
'error',
|
||||
function (ErrorEvent $e) use (&$er, $response) {
|
||||
$er = $e;
|
||||
}
|
||||
);
|
||||
|
||||
$transaction = $this->getMockBuilder('GuzzleHttp\Adapter\Transaction')
|
||||
->setMethods(['getResponse', 'setResponse'])
|
||||
->setConstructorArgs([$client, $request])
|
||||
->getMock();
|
||||
$transaction->expects($this->any())->method('setResponse');
|
||||
$transaction->expects($this->any())
|
||||
->method('getResponse')
|
||||
->will($this->returnCallback(function () use ($response) {
|
||||
$caller = debug_backtrace()[6]['function'];
|
||||
return $caller == 'addHandle' ||
|
||||
$caller == 'validateResponseWasSet'
|
||||
? null
|
||||
: $response;
|
||||
}));
|
||||
|
||||
$a = new MultiAdapter(new MessageFactory());
|
||||
Server::flush();
|
||||
Server::enqueue(["HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"]);
|
||||
$a->sendAll(new \ArrayIterator([$transaction]), 10);
|
||||
$this->assertNotNull($er);
|
||||
|
||||
$this->assertContains(
|
||||
'No response was received',
|
||||
$er->getException()->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
private function runConnectionTest(
|
||||
$queue,
|
||||
$stream,
|
||||
$msg,
|
||||
$statusCode = null
|
||||
) {
|
||||
$obj = new \stdClass();
|
||||
$er = null;
|
||||
$client = new Client();
|
||||
$request = $client->createRequest('PUT', Server::$url, [
|
||||
'body' => $stream
|
||||
]);
|
||||
|
||||
$request->getEmitter()->on(
|
||||
'error',
|
||||
function (ErrorEvent $e) use (&$er) {
|
||||
$er = $e;
|
||||
}
|
||||
);
|
||||
|
||||
$transaction = $this->getMockBuilder('GuzzleHttp\Adapter\Transaction')
|
||||
->setMethods(['getResponse', 'setResponse'])
|
||||
->setConstructorArgs([$client, $request])
|
||||
->getMock();
|
||||
|
||||
$transaction->expects($this->any())
|
||||
->method('setResponse')
|
||||
->will($this->returnCallback(function ($r) use (&$obj) {
|
||||
$obj->res = $r;
|
||||
}));
|
||||
|
||||
$transaction->expects($this->any())
|
||||
->method('getResponse')
|
||||
->will($this->returnCallback(function () use ($obj, &$called) {
|
||||
$caller = debug_backtrace()[6]['function'];
|
||||
if ($caller == 'addHandle') {
|
||||
return null;
|
||||
} elseif ($caller == 'validateResponseWasSet') {
|
||||
return ++$called == 2 ? $obj->res : null;
|
||||
} else {
|
||||
return $obj->res;
|
||||
}
|
||||
}));
|
||||
|
||||
$a = new MultiAdapter(new MessageFactory());
|
||||
Server::flush();
|
||||
Server::enqueue($queue);
|
||||
$a->sendAll(new \ArrayIterator([$transaction]), 10);
|
||||
|
||||
if ($msg) {
|
||||
$this->assertNotNull($er);
|
||||
$this->assertContains($msg, $er->getException()->getMessage());
|
||||
} else {
|
||||
$this->assertEquals(
|
||||
$statusCode,
|
||||
$transaction->getResponse()->getStatusCode()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testThrowsWhenTheBodyCannotBeRewound()
|
||||
{
|
||||
$this->runConnectionTest(
|
||||
["HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"],
|
||||
new NoSeekStream(Stream::factory('foo')),
|
||||
'attempting to rewind the request body failed'
|
||||
);
|
||||
}
|
||||
|
||||
public function testRetriesRewindableStreamsWhenClosedConnectionErrors()
|
||||
{
|
||||
$this->runConnectionTest(
|
||||
[
|
||||
"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
|
||||
"HTTP/1.1 201 OK\r\nContent-Length: 0\r\n\r\n",
|
||||
],
|
||||
Stream::factory('foo'),
|
||||
false,
|
||||
201
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,6 +168,15 @@ class EmitterTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertNotEmpty($this->emitter->listeners(self::postFoo));
|
||||
}
|
||||
|
||||
public function testAddSubscriberWithMultiple()
|
||||
{
|
||||
$eventSubscriber = new TestEventSubscriberWithMultiple();
|
||||
$this->emitter->attach($eventSubscriber);
|
||||
$listeners = $this->emitter->listeners('pre.foo');
|
||||
$this->assertNotEmpty($this->emitter->listeners(self::preFoo));
|
||||
$this->assertCount(2, $listeners);
|
||||
}
|
||||
|
||||
public function testAddSubscriberWithPriorities()
|
||||
{
|
||||
$eventSubscriber = new TestEventSubscriber();
|
||||
|
@ -343,3 +352,11 @@ class TestEventSubscriberWithPriorities extends TestEventListener implements Sub
|
|||
];
|
||||
}
|
||||
}
|
||||
|
||||
class TestEventSubscriberWithMultiple extends TestEventListener implements SubscriberInterface
|
||||
{
|
||||
public function getEvents()
|
||||
{
|
||||
return ['pre.foo' => [['preFoo', 10],['preFoo', 20]]];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ class RequestExceptionTest extends \PHPUnit_Framework_TestCase
|
|||
$e->emittedError(true);
|
||||
$e->emittedError(false);
|
||||
}
|
||||
|
||||
|
||||
public function testHasStatusCodeAsExceptionCode() {
|
||||
$e = RequestException::create(new Request('GET', '/'), new Response(442));
|
||||
$this->assertEquals(442, $e->getCode());
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace GuzzleHttp\Tests\Message;
|
||||
|
||||
use GuzzleHttp\Message\AbstractMessage;
|
||||
use GuzzleHttp\Message\Request;
|
||||
use GuzzleHttp\Message\Response;
|
||||
use GuzzleHttp\Stream\Stream;
|
||||
|
||||
/**
|
||||
|
@ -13,13 +13,13 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
public function testHasProtocolVersion()
|
||||
{
|
||||
$m = new Message();
|
||||
$m = new Request('GET', '/');
|
||||
$this->assertEquals(1.1, $m->getProtocolVersion());
|
||||
}
|
||||
|
||||
public function testHasHeaders()
|
||||
{
|
||||
$m = new Message();
|
||||
$m = new Request('GET', 'http://foo.com');
|
||||
$this->assertFalse($m->hasHeader('foo'));
|
||||
$m->addHeader('foo', 'bar');
|
||||
$this->assertTrue($m->hasHeader('foo'));
|
||||
|
@ -35,7 +35,7 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testHasBody()
|
||||
{
|
||||
$m = new Message();
|
||||
$m = new Request('GET', 'http://foo.com');
|
||||
$this->assertNull($m->getBody());
|
||||
$s = Stream::factory('test');
|
||||
$m->setBody($s);
|
||||
|
@ -45,7 +45,7 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testCanRemoveBodyBySettingToNullAndRemovesCommonBodyHeaders()
|
||||
{
|
||||
$m = new Message();
|
||||
$m = new Request('GET', 'http://foo.com');
|
||||
$m->setBody(Stream::factory('foo'));
|
||||
$m->setHeader('Content-Length', 3)->setHeader('Transfer-Encoding', 'chunked');
|
||||
$m->setBody(null);
|
||||
|
@ -56,10 +56,10 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testCastsToString()
|
||||
{
|
||||
$m = new Message();
|
||||
$m = new Request('GET', 'http://foo.com');
|
||||
$m->setHeader('foo', 'bar');
|
||||
$m->setBody(Stream::factory('baz'));
|
||||
$this->assertEquals("Foo!\r\nfoo: bar\r\n\r\nbaz", (string) $m);
|
||||
$this->assertEquals("GET / HTTP/1.1\r\nHost: foo.com\r\nfoo: bar\r\n\r\nbaz", (string) $m);
|
||||
}
|
||||
|
||||
public function parseParamsProvider()
|
||||
|
@ -115,12 +115,12 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
public function testParseParams($header, $result)
|
||||
{
|
||||
$request = new Request('GET', '/', ['foo' => $header]);
|
||||
$this->assertEquals($result, Message::parseHeader($request, 'foo'));
|
||||
$this->assertEquals($result, Request::parseHeader($request, 'foo'));
|
||||
}
|
||||
|
||||
public function testAddsHeadersWhenNotPresent()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->addHeader('foo', 'bar');
|
||||
$this->assertInternalType('string', $h->getHeader('foo'));
|
||||
$this->assertEquals('bar', $h->getHeader('foo'));
|
||||
|
@ -128,7 +128,7 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testAddsHeadersWhenPresentSameCase()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->addHeader('foo', 'bar')->addHeader('foo', 'baz');
|
||||
$this->assertEquals('bar, baz', $h->getHeader('foo'));
|
||||
$this->assertEquals(['bar', 'baz'], $h->getHeader('foo', true));
|
||||
|
@ -136,27 +136,28 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testAddsMultipleHeaders()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->addHeaders([
|
||||
'foo' => ' bar',
|
||||
'baz' => [' bam ', 'boo']
|
||||
]);
|
||||
$this->assertEquals([
|
||||
'foo' => ['bar'],
|
||||
'baz' => ['bam', 'boo']
|
||||
'baz' => ['bam', 'boo'],
|
||||
'Host' => ['foo.com']
|
||||
], $h->getHeaders());
|
||||
}
|
||||
|
||||
public function testAddsHeadersWhenPresentDifferentCase()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->addHeader('Foo', 'bar')->addHeader('fOO', 'baz');
|
||||
$this->assertEquals('bar, baz', $h->getHeader('foo'));
|
||||
}
|
||||
|
||||
public function testAddsHeadersWithArray()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->addHeader('Foo', ['bar', 'baz']);
|
||||
$this->assertEquals('bar, baz', $h->getHeader('foo'));
|
||||
}
|
||||
|
@ -166,12 +167,12 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testThrowsExceptionWhenInvalidValueProvidedToAddHeader()
|
||||
{
|
||||
(new Message())->addHeader('foo', false);
|
||||
(new Request('GET', 'http://foo.com'))->addHeader('foo', false);
|
||||
}
|
||||
|
||||
public function testGetHeadersReturnsAnArrayOfOverTheWireHeaderValues()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->addHeader('foo', 'bar');
|
||||
$h->addHeader('Foo', 'baz');
|
||||
$h->addHeader('boO', 'test');
|
||||
|
@ -186,7 +187,7 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testSetHeaderOverwritesExistingValues()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->setHeader('foo', 'bar');
|
||||
$this->assertEquals('bar', $h->getHeader('foo'));
|
||||
$h->setHeader('Foo', 'baz');
|
||||
|
@ -196,14 +197,14 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testSetHeaderOverwritesExistingValuesUsingHeaderArray()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->setHeader('foo', ['bar']);
|
||||
$this->assertEquals('bar', $h->getHeader('foo'));
|
||||
}
|
||||
|
||||
public function testSetHeaderOverwritesExistingValuesUsingArray()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->setHeader('foo', ['bar']);
|
||||
$this->assertEquals('bar', $h->getHeader('foo'));
|
||||
}
|
||||
|
@ -213,12 +214,12 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
*/
|
||||
public function testThrowsExceptionWhenInvalidValueProvidedToSetHeader()
|
||||
{
|
||||
(new Message())->setHeader('foo', false);
|
||||
(new Request('GET', 'http://foo.com'))->setHeader('foo', false);
|
||||
}
|
||||
|
||||
public function testSetHeadersOverwritesAllHeaders()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->setHeader('foo', 'bar');
|
||||
$h->setHeaders(['foo' => 'a', 'boo' => 'b']);
|
||||
$this->assertEquals(['foo' => ['a'], 'boo' => ['b']], $h->getHeaders());
|
||||
|
@ -226,7 +227,7 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testChecksIfCaseInsensitiveHeaderIsPresent()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->setHeader('foo', 'bar');
|
||||
$this->assertTrue($h->hasHeader('foo'));
|
||||
$this->assertTrue($h->hasHeader('Foo'));
|
||||
|
@ -236,7 +237,7 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testRemovesHeaders()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->setHeader('foo', 'bar');
|
||||
$h->removeHeader('foo');
|
||||
$this->assertFalse($h->hasHeader('foo'));
|
||||
|
@ -247,14 +248,14 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testReturnsCorrectTypeWhenMissing()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$this->assertInternalType('string', $h->getHeader('foo'));
|
||||
$this->assertInternalType('array', $h->getHeader('foo', true));
|
||||
}
|
||||
|
||||
public function testSetsIntegersAndFloatsAsHeaders()
|
||||
{
|
||||
$h = new Message();
|
||||
$h = new Request('GET', 'http://foo.com');
|
||||
$h->setHeader('foo', 10);
|
||||
$h->setHeader('bar', 10.5);
|
||||
$h->addHeader('foo', 10);
|
||||
|
@ -262,12 +263,20 @@ class AbstractMessageTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertSame('10, 10', $h->getHeader('foo'));
|
||||
$this->assertSame('10.5, 10.5', $h->getHeader('bar'));
|
||||
}
|
||||
}
|
||||
|
||||
class Message extends AbstractMessage
|
||||
{
|
||||
protected function getStartLine()
|
||||
public function testGetsResponseStartLine()
|
||||
{
|
||||
return 'Foo!';
|
||||
$m = new Response(200);
|
||||
$this->assertEquals('HTTP/1.1 200 OK', Response::getStartLine($m));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testThrowsWhenMessageIsUnknown()
|
||||
{
|
||||
$m = $this->getMockBuilder('GuzzleHttp\Message\AbstractMessage')
|
||||
->getMockForAbstractClass();
|
||||
AbstractMessage::getStartLine($m);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,10 @@ class PostBodyTest extends \PHPUnit_Framework_TestCase
|
|||
$b->forceMultipartUpload(true);
|
||||
$m = new Request('POST', '/');
|
||||
$b->applyRequestHeaders($m);
|
||||
$this->assertContains('multipart/form-data', (string) $m->getHeader('Content-Type'));
|
||||
$this->assertContains(
|
||||
'multipart/form-data',
|
||||
$m->getHeader('Content-Type')
|
||||
);
|
||||
}
|
||||
|
||||
public function testApplyingWithFilesAddsMultipartUpload()
|
||||
|
@ -49,7 +52,10 @@ class PostBodyTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertSame($p, $b->getFile('foo'));
|
||||
$m = new Request('POST', '/');
|
||||
$b->applyRequestHeaders($m);
|
||||
$this->assertContains('multipart/form-data', (string) $m->getHeader('Content-Type'));
|
||||
$this->assertContains(
|
||||
'multipart/form-data',
|
||||
$m->getHeader('Content-Type')
|
||||
);
|
||||
$this->assertTrue($m->hasHeader('Content-Length'));
|
||||
}
|
||||
|
||||
|
@ -60,7 +66,10 @@ class PostBodyTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(['foo' => 'bar'], $b->getFields());
|
||||
$m = new Request('POST', '/');
|
||||
$b->applyRequestHeaders($m);
|
||||
$this->assertContains('application/x-www-form', (string) $m->getHeader('Content-Type'));
|
||||
$this->assertContains(
|
||||
'application/x-www-form',
|
||||
$m->getHeader('Content-Type')
|
||||
);
|
||||
$this->assertTrue($m->hasHeader('Content-Length'));
|
||||
}
|
||||
|
||||
|
@ -72,7 +81,10 @@ class PostBodyTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(['foo' => ['bar' => 'baz']], $b->getFields());
|
||||
$m = new Request('POST', '/');
|
||||
$b->applyRequestHeaders($m);
|
||||
$this->assertContains('multipart/form-data', (string) $m->getHeader('Content-Type'));
|
||||
$this->assertContains(
|
||||
'multipart/form-data',
|
||||
$m->getHeader('Content-Type')
|
||||
);
|
||||
$this->assertTrue($m->hasHeader('Content-Length'));
|
||||
$contents = $b->getContents();
|
||||
$this->assertContains('name="foo[bar]"', $contents);
|
||||
|
@ -147,10 +159,20 @@ class PostBodyTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$b = new PostBody();
|
||||
$b->setField('testing', ['baz', 'bar']);
|
||||
$b->setField('other', 'hi');
|
||||
$b->setField('third', 'there');
|
||||
$b->addFile(new PostFile('foo', fopen(__FILE__, 'r')));
|
||||
$s = (string) $b;
|
||||
$this->assertContains(file_get_contents(__FILE__), $s);
|
||||
$this->assertContains('testing=bar', $s);
|
||||
$this->assertContains(
|
||||
'Content-Disposition: form-data; name="third"',
|
||||
$s
|
||||
);
|
||||
$this->assertContains(
|
||||
'Content-Disposition: form-data; name="other"',
|
||||
$s
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipartWithBase64Fields()
|
||||
|
@ -158,13 +180,40 @@ class PostBodyTest extends \PHPUnit_Framework_TestCase
|
|||
$b = new PostBody();
|
||||
$b->setField('foo64', '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc=');
|
||||
$b->forceMultipartUpload(true);
|
||||
$this->assertEquals(['foo64' => '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='], $b->getFields());
|
||||
$this->assertEquals(
|
||||
['foo64' => '/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc='],
|
||||
$b->getFields()
|
||||
);
|
||||
$m = new Request('POST', '/');
|
||||
$b->applyRequestHeaders($m);
|
||||
$this->assertContains('multipart/form-data', (string) $m->getHeader('Content-Type'));
|
||||
$this->assertContains(
|
||||
'multipart/form-data',
|
||||
$m->getHeader('Content-Type')
|
||||
);
|
||||
$this->assertTrue($m->hasHeader('Content-Length'));
|
||||
$contents = $b->getContents();
|
||||
$this->assertContains('name="foo64"', $contents);
|
||||
$this->assertContains('/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc=', $contents);
|
||||
$this->assertContains(
|
||||
'/xA2JhWEqPcgyLRDdir9WSRi/khpb2Lh3ooqv+5VYoc=',
|
||||
$contents
|
||||
);
|
||||
}
|
||||
|
||||
public function testMultipartWithAmpersandInValue()
|
||||
{
|
||||
$b = new PostBody();
|
||||
$b->setField('a', 'b&c=d');
|
||||
$b->forceMultipartUpload(true);
|
||||
$this->assertEquals(['a' => 'b&c=d'], $b->getFields());
|
||||
$m = new Request('POST', '/');
|
||||
$b->applyRequestHeaders($m);
|
||||
$this->assertContains(
|
||||
'multipart/form-data',
|
||||
$m->getHeader('Content-Type')
|
||||
);
|
||||
$this->assertTrue($m->hasHeader('Content-Length'));
|
||||
$contents = $b->getContents();
|
||||
$this->assertContains('name="a"', $contents);
|
||||
$this->assertContains('b&c=d', $contents);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,18 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
|
|||
$ev = new ErrorEvent($t, $e);
|
||||
$h = new History(2);
|
||||
$h->onError($ev);
|
||||
// Only tracks when no response is present
|
||||
$this->assertEquals([], $h->getRequests());
|
||||
}
|
||||
|
||||
public function testLogsConnectionErrors()
|
||||
{
|
||||
$request = new Request('GET', '/');
|
||||
$t = new Transaction(new Client(), $request);
|
||||
$e = new RequestException('foo', $request);
|
||||
$ev = new ErrorEvent($t, $e);
|
||||
$h = new History();
|
||||
$h->onError($ev);
|
||||
$this->assertEquals([$request], $h->getRequests());
|
||||
}
|
||||
|
||||
|
|
|
@ -162,7 +162,9 @@ class UrlTest extends \PHPUnit_Framework_TestCase
|
|||
['http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/'],
|
||||
['/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'],
|
||||
['http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'],
|
||||
['https://www.example.com/path', '//foo.com/abc', 'https://foo.com/abc'],
|
||||
['https://www.example.com/path', '//foo.com/abc', 'https://foo.com/abc'],
|
||||
['https://www.example.com/0/', 'relative/foo', 'https://www.example.com/0/relative/foo'],
|
||||
['', '0', '0'],
|
||||
// RFC 3986 test cases
|
||||
[self::RFC3986_BASE, 'g:h', 'g:h'],
|
||||
[self::RFC3986_BASE, 'g', 'http://a/b/c/g'],
|
||||
|
|
|
@ -7,6 +7,7 @@ php:
|
|||
- hhvm
|
||||
|
||||
before_script:
|
||||
- composer install
|
||||
- composer self-update
|
||||
- composer install --no-interaction --prefer-source --dev
|
||||
|
||||
script: vendor/bin/phpunit
|
||||
|
|
|
@ -2,6 +2,27 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
1.5.1 (2014-09-10)
|
||||
------------------
|
||||
|
||||
* Stream metadata is grabbed from the underlying stream each time
|
||||
``getMetadata`` is called rather than returning a value from a cache.
|
||||
* Properly closing all underlying streams when AppendStream is closed.
|
||||
* Seek functions no longer throw exceptions.
|
||||
* LazyOpenStream now correctly returns the underlying stream resource when
|
||||
detached.
|
||||
|
||||
1.5.0 (2014-08-07)
|
||||
------------------
|
||||
|
||||
* Added ``Stream\safe_open`` to open stream resources and throw exceptions
|
||||
instead of raising errors.
|
||||
|
||||
1.4.0 (2014-07-19)
|
||||
------------------
|
||||
|
||||
* Added a LazyOpenStream
|
||||
|
||||
1.3.0 (2014-07-15)
|
||||
------------------
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
all: clean coverage
|
||||
|
||||
release: tag
|
||||
git push origin --tags
|
||||
|
||||
tag:
|
||||
chag tag --sign --debug CHANGELOG.rst
|
||||
|
||||
test:
|
||||
vendor/bin/phpunit
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ Simply add the following to the composer.json file at the root of your project:
|
|||
|
||||
{
|
||||
"require": {
|
||||
"guzzlehttp/streams": "1.*"
|
||||
"guzzlehttp/streams": "~1.0"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2.x-dev"
|
||||
"dev-master": "1.5-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,7 +86,6 @@ class AppendStream implements StreamInterface
|
|||
*/
|
||||
public function detach()
|
||||
{
|
||||
$this->streams = [];
|
||||
$this->close();
|
||||
}
|
||||
|
||||
|
@ -132,12 +131,8 @@ class AppendStream implements StreamInterface
|
|||
*/
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
if (!$this->seekable) {
|
||||
if (!$this->seekable || $whence !== SEEK_SET) {
|
||||
return false;
|
||||
} elseif ($whence !== SEEK_SET) {
|
||||
throw new \InvalidArgumentException(
|
||||
'AppendStream only supports SEEK_SET'
|
||||
);
|
||||
}
|
||||
|
||||
$success = true;
|
||||
|
@ -207,6 +202,6 @@ class AppendStream implements StreamInterface
|
|||
|
||||
public function write($string)
|
||||
{
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,7 @@ class CachingStream implements StreamInterface, MetadataStreamInterface
|
|||
} elseif ($whence == SEEK_CUR) {
|
||||
$byte = $offset + $this->tell();
|
||||
} else {
|
||||
throw new \RuntimeException(__CLASS__ . ' supports only SEEK_SET'
|
||||
.' and SEEK_CUR seek operations');
|
||||
return false;
|
||||
}
|
||||
|
||||
// You cannot skip ahead past where you've read from the remote stream
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Stream;
|
||||
|
||||
/**
|
||||
* Lazily reads or writes to a file that is opened only after an IO operation
|
||||
* take place on the stream.
|
||||
*/
|
||||
class LazyOpenStream implements StreamInterface, MetadataStreamInterface
|
||||
{
|
||||
/** @var string File to open */
|
||||
private $filename;
|
||||
|
||||
/** @var string $mode */
|
||||
private $mode;
|
||||
|
||||
/** @var MetadataStreamInterface */
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* @param string $filename File to lazily open
|
||||
* @param string $mode fopen mode to use when opening the stream
|
||||
*/
|
||||
public function __construct($filename, $mode)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
$this->mode = $mode;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
try {
|
||||
return (string) $this->getStream();
|
||||
} catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
private function getStream()
|
||||
{
|
||||
if (!$this->stream) {
|
||||
$this->stream = create(safe_open($this->filename, $this->mode));
|
||||
}
|
||||
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
public function getContents($maxLength = -1)
|
||||
{
|
||||
return copy_to_string($this->getStream(), $maxLength);
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->stream) {
|
||||
$this->stream->close();
|
||||
}
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
$stream = $this->getStream();
|
||||
$result = $stream->detach();
|
||||
$this->close();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function tell()
|
||||
{
|
||||
return $this->stream ? $this->stream->tell() : 0;
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
{
|
||||
return $this->getStream()->getSize();
|
||||
}
|
||||
|
||||
public function eof()
|
||||
{
|
||||
return $this->getStream()->eof();
|
||||
}
|
||||
|
||||
public function seek($offset, $whence = SEEK_SET)
|
||||
{
|
||||
return $this->getStream()->seek($offset, $whence);
|
||||
}
|
||||
|
||||
public function read($length)
|
||||
{
|
||||
return $this->getStream()->read($length);
|
||||
}
|
||||
|
||||
public function isReadable()
|
||||
{
|
||||
return $this->getStream()->isReadable();
|
||||
}
|
||||
|
||||
public function isWritable()
|
||||
{
|
||||
return $this->getStream()->isWritable();
|
||||
}
|
||||
|
||||
public function isSeekable()
|
||||
{
|
||||
return $this->getStream()->isSeekable();
|
||||
}
|
||||
|
||||
public function write($string)
|
||||
{
|
||||
return $this->getStream()->write($string);
|
||||
}
|
||||
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return $this->getStream()->getMetadata($key);
|
||||
}
|
||||
}
|
|
@ -7,19 +7,12 @@ namespace GuzzleHttp\Stream;
|
|||
*/
|
||||
class Stream implements MetadataStreamInterface
|
||||
{
|
||||
/** @var resource Stream resource */
|
||||
private $stream;
|
||||
|
||||
/** @var int Size of the stream contents in bytes */
|
||||
private $size;
|
||||
|
||||
/** @var bool */
|
||||
private $seekable;
|
||||
private $readable;
|
||||
private $writable;
|
||||
|
||||
/** @var array Stream metadata */
|
||||
private $meta = [];
|
||||
private $uri;
|
||||
|
||||
/** @var array Hash of readable and writable stream types */
|
||||
private static $readWriteHash = [
|
||||
|
@ -66,10 +59,11 @@ class Stream implements MetadataStreamInterface
|
|||
|
||||
$this->size = $size;
|
||||
$this->stream = $stream;
|
||||
$this->meta = stream_get_meta_data($this->stream);
|
||||
$this->seekable = $this->meta['seekable'];
|
||||
$this->readable = isset(self::$readWriteHash['read'][$this->meta['mode']]);
|
||||
$this->writable = isset(self::$readWriteHash['write'][$this->meta['mode']]);
|
||||
$meta = stream_get_meta_data($this->stream);
|
||||
$this->seekable = $meta['seekable'];
|
||||
$this->readable = isset(self::$readWriteHash['read'][$meta['mode']]);
|
||||
$this->writable = isset(self::$readWriteHash['write'][$meta['mode']]);
|
||||
$this->uri = isset($meta['uri']) ? $meta['uri'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,14 +98,13 @@ class Stream implements MetadataStreamInterface
|
|||
fclose($this->stream);
|
||||
}
|
||||
|
||||
$this->meta = [];
|
||||
$this->stream = null;
|
||||
$this->detach();
|
||||
}
|
||||
|
||||
public function detach()
|
||||
{
|
||||
$result = $this->stream;
|
||||
$this->stream = $this->size = null;
|
||||
$this->stream = $this->size = $this->uri = null;
|
||||
$this->readable = $this->writable = $this->seekable = false;
|
||||
|
||||
return $result;
|
||||
|
@ -121,13 +114,15 @@ class Stream implements MetadataStreamInterface
|
|||
{
|
||||
if ($this->size !== null) {
|
||||
return $this->size;
|
||||
} elseif (!$this->stream) {
|
||||
}
|
||||
|
||||
if (!$this->stream) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the stream is a file based stream and local, then use fstat
|
||||
if (isset($this->meta['uri'])) {
|
||||
clearstatcache(true, $this->meta['uri']);
|
||||
// Clear the stat cache if the stream has a URI
|
||||
if ($this->uri) {
|
||||
clearstatcache(true, $this->uri);
|
||||
}
|
||||
|
||||
$stats = fstat($this->stream);
|
||||
|
@ -207,8 +202,8 @@ class Stream implements MetadataStreamInterface
|
|||
*/
|
||||
public function getMetadata($key = null)
|
||||
{
|
||||
return !$key
|
||||
? $this->meta
|
||||
: (isset($this->meta[$key]) ? $this->meta[$key] : null);
|
||||
$meta = $this->stream ? stream_get_meta_data($this->stream) : [];
|
||||
|
||||
return !$key ? $meta : (isset($meta[$key]) ? $meta[$key] : null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ trait StreamDecoratorTrait
|
|||
|
||||
public function close()
|
||||
{
|
||||
return $this->stream->close();
|
||||
$this->stream->close();
|
||||
}
|
||||
|
||||
public function getMetadata($key = null)
|
||||
|
@ -66,9 +66,7 @@ trait StreamDecoratorTrait
|
|||
|
||||
public function detach()
|
||||
{
|
||||
$this->stream->detach();
|
||||
|
||||
return $this;
|
||||
return $this->stream->detach();
|
||||
}
|
||||
|
||||
public function getSize()
|
||||
|
|
|
@ -171,4 +171,39 @@ if (!defined('GUZZLE_STREAMS_FUNCTIONS')) {
|
|||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely opens a PHP stream resource using a filename.
|
||||
*
|
||||
* When fopen fails, PHP normally raises a warning. This function adds an
|
||||
* error handler that checks for errors and throws an exception instead.
|
||||
*
|
||||
* @param string $filename File to open
|
||||
* @param string $mode Mode used to open the file
|
||||
*
|
||||
* @return resource
|
||||
* @throws \RuntimeException if the file cannot be opened
|
||||
*/
|
||||
function safe_open($filename, $mode)
|
||||
{
|
||||
$ex = null;
|
||||
set_error_handler(function () use ($filename, $mode, &$ex) {
|
||||
$ex = new \RuntimeException(sprintf(
|
||||
'Unable to open %s using mode %s: %s',
|
||||
$filename,
|
||||
$mode,
|
||||
func_get_args()[1]
|
||||
));
|
||||
});
|
||||
|
||||
$handle = fopen($filename, $mode);
|
||||
restore_error_handler();
|
||||
|
||||
if ($ex) {
|
||||
/** @var $ex \RuntimeException */
|
||||
throw $ex;
|
||||
}
|
||||
|
||||
return $handle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,14 +22,10 @@ class AppendStreamTest extends \PHPUnit_Framework_TestCase
|
|||
$a->addStream($s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage only supports SEEK_SET
|
||||
*/
|
||||
public function testValidatesSeekType()
|
||||
{
|
||||
$a = new AppendStream();
|
||||
$a->seek(100, SEEK_CUR);
|
||||
$this->assertFalse($a->seek(100, SEEK_CUR));
|
||||
}
|
||||
|
||||
public function testTriesToRewindOnSeek()
|
||||
|
@ -92,7 +88,7 @@ class AppendStreamTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertFalse($a->isWritable());
|
||||
$this->assertTrue($a->isSeekable());
|
||||
$this->assertTrue($a->isReadable());
|
||||
$this->assertSame(0, $a->write('foo'));
|
||||
$this->assertFalse($a->write('foo'));
|
||||
}
|
||||
|
||||
public function testDoesNotNeedStreams()
|
||||
|
|
|
@ -44,13 +44,9 @@ class CachingStreamTest extends \PHPUnit_Framework_TestCase
|
|||
$this->body->seek(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage supports only SEEK_SET and SEEK_CUR
|
||||
*/
|
||||
public function testCannotUseSeekEnd()
|
||||
{
|
||||
$this->body->seek(2, SEEK_END);
|
||||
$this->assertFalse($this->body->seek(2, SEEK_END));
|
||||
}
|
||||
|
||||
public function testRewindUsesSeek()
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
namespace GuzzleHttp\Tests\Stream;
|
||||
|
||||
use GuzzleHttp\Stream\LazyOpenStream;
|
||||
|
||||
class LazyOpenStreamTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $fname;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->fname = tempnam('/tmp', 'tfile');
|
||||
|
||||
if (file_exists($this->fname)) {
|
||||
unlink($this->fname);
|
||||
}
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
if (file_exists($this->fname)) {
|
||||
unlink($this->fname);
|
||||
}
|
||||
}
|
||||
|
||||
public function testOpensLazily()
|
||||
{
|
||||
$l = new LazyOpenStream($this->fname, 'w+');
|
||||
$l->write('foo');
|
||||
$this->assertInternalType('array', $l->getMetadata());
|
||||
$this->assertFileExists($this->fname);
|
||||
$this->assertEquals('foo', file_get_contents($this->fname));
|
||||
}
|
||||
|
||||
public function testProxiesToFile()
|
||||
{
|
||||
file_put_contents($this->fname, 'foo');
|
||||
$l = new LazyOpenStream($this->fname, 'r');
|
||||
$this->assertEquals('foo', $l->read(4));
|
||||
$this->assertTrue($l->eof());
|
||||
$this->assertEquals(3, $l->tell());
|
||||
$this->assertTrue($l->isReadable());
|
||||
$this->assertTrue($l->isSeekable());
|
||||
$this->assertFalse($l->isWritable());
|
||||
$l->seek(1);
|
||||
$this->assertEquals('oo', $l->getContents());
|
||||
$this->assertEquals('foo', (string) $l);
|
||||
$this->assertEquals(3, $l->getSize());
|
||||
$this->assertInternalType('array', $l->getMetadata());
|
||||
$l->close();
|
||||
}
|
||||
|
||||
public function testDetachesUnderlyingStream()
|
||||
{
|
||||
file_put_contents($this->fname, 'foo');
|
||||
$l = new LazyOpenStream($this->fname, 'r');
|
||||
$r = $l->detach();
|
||||
$this->assertInternalType('resource', $r);
|
||||
fclose($r);
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use GuzzleHttp\Stream\NoSeekStream;
|
|||
|
||||
/**
|
||||
* @covers GuzzleHttp\Stream\NoSeekStream
|
||||
* @covers GuzzleHttp\Stream\StreamDecoratorTrait
|
||||
*/
|
||||
class NoSeekStreamTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
@ -21,4 +22,12 @@ class NoSeekStreamTest extends \PHPUnit_Framework_TestCase
|
|||
$this->assertFalse($wrapped->isSeekable());
|
||||
$this->assertFalse($wrapped->seek(2));
|
||||
}
|
||||
|
||||
public function testHandlesClose()
|
||||
{
|
||||
$s = Stream::factory('foo');
|
||||
$wrapped = new NoSeekStream($s);
|
||||
$wrapped->close();
|
||||
$this->assertFalse($wrapped->write('foo'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -151,6 +151,19 @@ class StreamTest extends \PHPUnit_Framework_TestCase
|
|||
$stream->close();
|
||||
}
|
||||
|
||||
public function testCloseClearProperties()
|
||||
{
|
||||
$handle = fopen('php://temp', 'r+');
|
||||
$stream = new Stream($handle);
|
||||
$stream->close();
|
||||
|
||||
$this->assertEmpty($stream->getMetadata());
|
||||
$this->assertFalse($stream->isSeekable());
|
||||
$this->assertFalse($stream->isReadable());
|
||||
$this->assertFalse($stream->isWritable());
|
||||
$this->assertNull($stream->getSize());
|
||||
}
|
||||
|
||||
public function testCreatesWithFactory()
|
||||
{
|
||||
$stream = Stream::factory('foo');
|
||||
|
|
|
@ -116,6 +116,22 @@ class functionsTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
Stream\create(new \stdClass());
|
||||
}
|
||||
|
||||
public function testOpensFilesSuccessfully()
|
||||
{
|
||||
$r = Stream\safe_open(__FILE__, 'r');
|
||||
$this->assertInternalType('resource', $r);
|
||||
fclose($r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage Unable to open /path/to/does/not/exist using mode r
|
||||
*/
|
||||
public function testThrowsExceptionNotWarning()
|
||||
{
|
||||
Stream\safe_open('/path/to/does/not/exist', 'r');
|
||||
}
|
||||
}
|
||||
|
||||
class HasToString
|
||||
|
|
Loading…
Reference in New Issue