diff --git a/core/composer.json b/core/composer.json index 64928b1..06d4939 100644 --- a/core/composer.json +++ b/core/composer.json @@ -7,7 +7,8 @@ "symfony/http-kernel": "2.1.*", "symfony/routing": "2.1.*", "symfony/yaml": "2.1.*", - "twig/twig": "1.8.*" + "twig/twig": "1.8.*", + "guzzle/guzzle": "2.7.*" }, "minimum-stability": "beta" } diff --git a/core/composer.lock b/core/composer.lock index c861d34..cf61d04 100644 --- a/core/composer.lock +++ b/core/composer.lock @@ -1,7 +1,11 @@ { - "hash": "ec77094fc475b57afb7a27f63983ead1", + "hash": "c235ef14fe1d633ad7944e049c65b57e", "packages": [ { + "package": "guzzle/guzzle", + "version": "v2.7.2" + }, + { "package": "symfony/class-loader", "version": "v2.1.0-BETA1" }, diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 95adc95..cfb54e1 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -3047,6 +3047,7 @@ function drupal_classloader() { 'Symfony\Component\HttpKernel' => DRUPAL_ROOT . '/core/vendor/symfony/http-kernel', 'Symfony\Component\Routing' => DRUPAL_ROOT . '/core/vendor/symfony/routing', 'Symfony\Component\Yaml' => DRUPAL_ROOT . '/core/vendor/symfony/yaml', + 'Guzzle' => DRUPAL_ROOT . '/core/vendor/guzzle/guzzle/src' )); // Register PEAR-style vendor namespaces. $loader->registerPrefixes(array( diff --git a/core/includes/common.inc b/core/includes/common.inc index 0b95dcd..834b33f 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -3,6 +3,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Drupal\Core\Database\Database; +use Guzzle\Http\Message\RequestInterface; /** * @file @@ -1020,6 +1021,26 @@ function drupal_http_request($url, array $options = array()) { } /** + * Performs an HTTP request using the default Guzzle HTTP Client. + * + * @param $url + * A string containing a fully qualified URI. + * @param string $method + * A method name supported by Guzzle. + * @param array $headers + * An array of headers to set for this request. Additional headers can be added + * to the returned response. + * @param mixed $body + * Data to be sent with the request. + * + * @return Guzzle\Http\Message\Request + * The prepared request, ready to be sent or modified. + */ +function drupal_http_request_guzzle($uri, $method = RequestInterface::GET, array $headers = array(), $body = NULL) { + return drupal_container()->get('http_default_client')->createRequest($method, $uri, $headers, $body); +} + +/** * Helper function for determining hosts excluded from needing a proxy. * * @return diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php index a82f5b6..3b1ffe2 100644 --- a/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php +++ b/core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php @@ -73,6 +73,16 @@ class ContainerBuilder extends BaseContainerBuilder { $this->register('httpkernel', 'Symfony\Component\HttpKernel\HttpKernel') ->addArgument(new Reference('dispatcher')) ->addArgument(new Reference('resolver')); + + $this->register('http_client_simpletest_subscriber', 'Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber'); + $this->register('http_default_client', 'Guzzle\Http\Client') + ->addArgument(NULL) + ->addArgument(array( + 'curl.CURLOPT_TIMEOUT' => 30.0, + 'curl.CURLOPT_MAXREDIRS' => 3, + )) + ->addMethodCall('addSubscriber', array(new Reference('http_client_simpletest_subscriber'))) + ->addMethodCall('setUserAgent', array('Drupal (+http://drupal.org/)')); } /** diff --git a/core/lib/Drupal/Core/Http/Plugin/SimpletestHttpRequestSubscriber.php b/core/lib/Drupal/Core/Http/Plugin/SimpletestHttpRequestSubscriber.php new file mode 100644 index 0000000..6f5bfd1 --- /dev/null +++ b/core/lib/Drupal/Core/Http/Plugin/SimpletestHttpRequestSubscriber.php @@ -0,0 +1,42 @@ + 'onBeforeSendRequest'); + } + + + /** + * Event callback for request.before_send + */ + public function onBeforeSendRequest(Event $event) { + // If the database prefix is being used by SimpleTest to run the tests in a copied + // database then set the user-agent header to the database prefix so that any + // calls to other Drupal pages will run the SimpleTest prefixed database. The + // user-agent is used to ensure that multiple testing sessions running at the + // same time won't interfere with each other as they would if the database + // prefix were stored statically in a file or database variable. + $test_info = &$GLOBALS['drupal_test_info']; + if (!empty($test_info['test_run_id'])) { + $event['request']->setHeader('User-Agent', drupal_generate_test_ua($test_info['test_run_id'])); + } + } +} diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php index e4b663b..53b7b25 100644 --- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php +++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/aggregator/fetcher/DefaultFetcher.php @@ -20,44 +20,34 @@ class DefaultFetcher implements FetcherInterface { * Implements Drupal\aggregator\Plugin\FetcherInterface::fetch(). */ function fetch(&$feed) { + $request = drupal_http_request_guzzle($feed->url); + $feed->source_string = FALSE; // Generate conditional GET headers. - $headers = array(); if ($feed->etag) { - $headers['If-None-Match'] = $feed->etag; + $request->addHeader('If-None-Match', $feed->etag); } if ($feed->modified) { - $headers['If-Modified-Since'] = gmdate(DATE_RFC1123, $feed->modified); + $request->addHeader('If-Modified-Since', gmdate(DATE_RFC1123, $feed->modified)); } - // Request feed. - $result = drupal_http_request($feed->url, array('headers' => $headers)); - - // Process HTTP response code. - switch ($result->code) { - case 304: - break; - case 301: - $feed->url = $result->redirect_url; - // Do not break here. - case 200: - case 302: - case 307: - if (!isset($result->data)) { - $result->data = ''; - } - if (!isset($result->headers)) { - $result->headers = array(); - } - $feed->source_string = $result->data; - $feed->http_headers = $result->headers; - break; - default: - watchdog('aggregator', 'The feed from %site seems to be broken due to "%error".', array('%site' => $feed->title, '%error' => $result->code . ' ' . $result->error), WATCHDOG_WARNING); - drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->title, '%error' => $result->code . ' ' . $result->error))); - } + try { + $result = $request->send(); + $feed->source_string = $result->getBody(TRUE); - return !($feed->source_string === FALSE); + //Guzzle HTTP Headers are a slightly different format from what aggregator expects: + $feed->http_headers = array( + 'last-modified' => $result->getLastModified(), + 'etag' => $result->getEtag() + ); + return TRUE; + } + catch (\Guzzle\Http\Exception\BadResponseException $e) { + $result = $e->getResponse(); + watchdog('aggregator', 'The feed from %site seems to be broken due to "%error".', array('%site' => $feed->title, '%error' => $result->getStatusCode() . ' ' . $result->getReasonPhrase()), WATCHDOG_WARNING); + drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->title, '%error' => $result->getStatusCode() . ' ' . $result->getReasonPhrase()))); + return FALSE; + } } } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index a76e3ae..c60cfa0 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -3755,17 +3755,20 @@ function system_retrieve_file($url, $destination = NULL, $managed = FALSE, $repl $path = $destination; } } - $result = drupal_http_request($url); - if ($result->code != 200) { - drupal_set_message(t('HTTP error @errorcode occurred when trying to fetch @remote.', array('@errorcode' => $result->code, '@remote' => $url)), 'error'); - return FALSE; - } - $local = $managed ? file_save_data($result->data, $path, $replace) : file_unmanaged_save_data($result->data, $path, $replace); - if (!$local) { - drupal_set_message(t('@remote could not be saved to @path.', array('@remote' => $url, '@path' => $path)), 'error'); + try { + $result = drupal_http_request_guzzle($url)->send(); + $local = $managed ? file_save_data($result->getBody(TRUE), $path, $replace) : file_unmanaged_save_data($result->getBody(TRUE), $path, $replace); + if (!$local) { + drupal_set_message(t('@remote could not be saved to @path.', array('@remote' => $url, '@path' => $path)), 'error'); + } + return $local; } + catch (\Guzzle\Http\Exception\BadResponseException $e) { + $result = $e->getResponse(); + drupal_set_message(t('HTTP error @errorcode occurred when trying to fetch @remote.', array('@errorcode' => $result->getStatusCode(), '@remote' => $url)), 'error'); - return $local; + return FALSE; + } } /** diff --git a/core/vendor/composer/ClassLoader.php b/core/vendor/composer/ClassLoader.php index 146276e..d1c2cd5 100644 --- a/core/vendor/composer/ClassLoader.php +++ b/core/vendor/composer/ClassLoader.php @@ -102,7 +102,7 @@ class ClassLoader /** * Turns on searching the include path for class files. * - * @param Boolean $useIncludePath + * @param bool $useIncludePath */ public function setUseIncludePath($useIncludePath) { @@ -113,7 +113,7 @@ class ClassLoader * Can be used to check if the autoloader uses the include path to check * for classes. * - * @return Boolean + * @return bool */ public function getUseIncludePath() { @@ -123,7 +123,7 @@ class ClassLoader /** * Registers this instance as an autoloader. * - * @param Boolean $prepend Whether to prepend the autoloader or not + * @param bool $prepend Whether to prepend the autoloader or not */ public function register($prepend = false) { @@ -141,13 +141,13 @@ class ClassLoader /** * Loads the given class or interface. * - * @param string $class The name of the class - * @return Boolean|null True, if loaded + * @param string $class The name of the class + * @return bool|null True, if loaded */ public function loadClass($class) { if ($file = $this->findFile($class)) { - require $file; + include $file; return true; } diff --git a/core/vendor/composer/autoload_namespaces.php b/core/vendor/composer/autoload_namespaces.php index 6ae0e91..459fb82 100644 --- a/core/vendor/composer/autoload_namespaces.php +++ b/core/vendor/composer/autoload_namespaces.php @@ -14,4 +14,6 @@ return array( 'Symfony\\Component\\EventDispatcher' => $vendorDir . '/symfony/event-dispatcher/', 'Symfony\\Component\\DependencyInjection' => $vendorDir . '/symfony/dependency-injection/', 'Symfony\\Component\\ClassLoader' => $vendorDir . '/symfony/class-loader/', + 'Guzzle\\Tests' => $vendorDir . '/guzzle/guzzle/tests/', + 'Guzzle' => $vendorDir . '/guzzle/guzzle/src/', ); diff --git a/core/vendor/composer/installed.json b/core/vendor/composer/installed.json index 955de68..00fe23f 100644 --- a/core/vendor/composer/installed.json +++ b/core/vendor/composer/installed.json @@ -3,7 +3,7 @@ "name": "twig/twig", "version": "v1.8.3", "version_normalized": "1.8.3.0", - "time": "2012-06-17 18:48:16", + "time": "2012-06-17 22:48:16", "source": { "type": "git", "url": "git://github.com/fabpot/Twig.git", @@ -58,7 +58,7 @@ "version": "v2.1.0-BETA1", "version_normalized": "2.1.0.0-beta1", "target-dir": "Symfony/Component/DependencyInjection", - "time": "2012-06-12 11:59:42", + "time": "2012-06-12 15:59:42", "source": { "type": "git", "url": "https://github.com/symfony/DependencyInjection", @@ -108,7 +108,7 @@ "version": "v2.1.0-BETA1", "version_normalized": "2.1.0.0-beta1", "target-dir": "Symfony/Component/EventDispatcher", - "time": "2012-05-15 16:56:32", + "time": "2012-05-15 20:56:32", "source": { "type": "git", "url": "https://github.com/symfony/EventDispatcher", @@ -155,7 +155,7 @@ "version": "v2.1.0-BETA1", "version_normalized": "2.1.0.0-beta1", "target-dir": "Symfony/Component/HttpFoundation", - "time": "2012-06-12 06:10:53", + "time": "2012-06-12 10:10:53", "source": { "type": "git", "url": "https://github.com/symfony/HttpFoundation", @@ -202,7 +202,7 @@ "version": "v2.1.0-BETA1", "version_normalized": "2.1.0.0-beta1", "target-dir": "Symfony/Component/HttpKernel", - "time": "2012-06-12 11:59:42", + "time": "2012-06-12 15:59:42", "source": { "type": "git", "url": "https://github.com/symfony/HttpKernel", @@ -259,7 +259,7 @@ "version": "v2.1.0-BETA1", "version_normalized": "2.1.0.0-beta1", "target-dir": "Symfony/Component/Routing", - "time": "2012-06-12 11:59:42", + "time": "2012-06-12 15:59:42", "source": { "type": "git", "url": "https://github.com/symfony/Routing", @@ -310,7 +310,7 @@ "version": "v2.1.0-BETA1", "version_normalized": "2.1.0.0-beta1", "target-dir": "Symfony/Component/Yaml", - "time": "2012-06-09 15:04:17", + "time": "2012-06-09 19:04:17", "source": { "type": "git", "url": "https://github.com/symfony/Yaml", @@ -357,7 +357,7 @@ "version": "v2.1.0-BETA1", "version_normalized": "2.1.0.0-beta1", "target-dir": "Symfony/Component/ClassLoader", - "time": "2012-04-23 05:37:21", + "time": "2012-04-23 09:37:21", "source": { "type": "git", "url": "https://github.com/symfony/ClassLoader", @@ -398,5 +398,81 @@ "Symfony\\Component\\ClassLoader": "" } } + }, + { + "name": "guzzle/guzzle", + "version": "v2.7.2", + "version_normalized": "2.7.2.0", + "time": "2012-07-02 21:04:37", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle", + "reference": "v2.7.2" + }, + "dist": { + "type": "zip", + "url": "https://github.com/guzzle/guzzle/zipball/v2.7.2", + "reference": "v2.7.2", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "ext-curl": "*", + "symfony/event-dispatcher": "2.*" + }, + "replace": { + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/parser": "self.version" + }, + "require-dev": { + "doctrine/common": "*", + "symfony/class-loader": "*", + "monolog/monolog": "1.*", + "zendframework/zend-cache": "2.0.*", + "zendframework/zend-log": "2.0.*", + "zendframework/zend-loader": "2.0.*", + "zendframework/zend-stdlib": "2.0.*", + "zendframework/zend-eventmanager": "2.0.*", + "zendframework/zend-servicemanager": "2.0.*", + "zend/zend-log1": "1.11", + "zend/zend-cache1": "1.11" + }, + "type": "library", + "installation-source": "dist", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling", + "role": null + }, + { + "name": "Guzzle Community", + "email": null, + "homepage": "https://github.com/guzzle/guzzle/contributors", + "role": null + } + ], + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "homepage": "http://guzzlephp.org/", + "keywords": [ + "framework", + "curl", + "http", + "rest", + "http client", + "client", + "web service" + ], + "autoload": { + "psr-0": { + "Guzzle\\Tests": "tests/", + "Guzzle": "src/" + } + } } ] diff --git a/core/vendor/guzzle/guzzle/.gitignore b/core/vendor/guzzle/guzzle/.gitignore new file mode 100644 index 0000000..a206157 --- /dev/null +++ b/core/vendor/guzzle/guzzle/.gitignore @@ -0,0 +1,19 @@ +# Ingore common cruft +.DS_STORE +coverage + +# Ignore binary files +guzzle.phar +guzzle-min.phar + +# Ignore potentially sensitive phpunit file +phpunit.xml + +# Ignore composer generated files +composer.phar +composer.lock +composer-test.lock +vendor/ + +# Ignore build files +build/ diff --git a/core/vendor/guzzle/guzzle/.travis.yml b/core/vendor/guzzle/guzzle/.travis.yml new file mode 100644 index 0000000..8775355 --- /dev/null +++ b/core/vendor/guzzle/guzzle/.travis.yml @@ -0,0 +1,19 @@ +language: php +php: + - 5.3 + - 5.4 +before_script: + - curl --version + - echo 'Installing pecl_http' + - wget --quiet http://pecl.php.net/get/pecl_http-1.7.4.tgz + - tar -xzf pecl_http-1.7.4.tgz + - sh -c "cd pecl_http-1.7.4 && phpize && ./configure && make && sudo make install" > /dev/null + - echo "extension=http.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"` + - echo 'Installing composer' + - wget --quiet http://getcomposer.org/composer.phar + - php composer.phar install --dev + - cp phpunit.xml.dist phpunit.xml + - echo 'Ensuring the correct version of node is running' + - ~/.nvm/nvm.sh install v0.6.14 + - ~/.nvm/nvm.sh run v0.6.14 tests/Guzzle/Tests/Http/server.js & +script: phpunit diff --git a/core/vendor/guzzle/guzzle/CHANGELOG.md b/core/vendor/guzzle/guzzle/CHANGELOG.md new file mode 100644 index 0000000..e85fa9f --- /dev/null +++ b/core/vendor/guzzle/guzzle/CHANGELOG.md @@ -0,0 +1,151 @@ +CHANGELOG +========= + +* 2.7.2 (07-02-2012) + + * BC: Moving things to get ready for subtree splits. Moving Inflection into Common. Moving Guzzle\Http\Parser to Guzzle\Parser. + * BC: Removing Guzzle\Common\Batch\Batch::count() and replacing it with isEmpty() + * CachePlugin now allows for a custom request parameter function to check if a request can be cached + * Bug fix: CachePlugin now only caches GET and HEAD requests by default + * Bug fix: Using header glue when transferring headers over the wire + * Allowing deeply nested arrays for composite variables in URI templates + * Batch divisors can now return iterators or arrays + +* 2.7.1 (06-26-2012) + + * Minor patch to update version number in UA string + * Updating build process + +* 2.7.0 (06-25-2012) + + * BC: Inflection classes moved to Guzzle\Inflection. No longer static methods. Can now inject custom inflectors into classes. + * BC: Removed magic setX methods from commands + * BC: Magic methods mapped to service description commands are now inflected in the command factory rather than the client __call() method + * Verbose cURL options are no longer enabled by default. Set curl.debug to true on a client to enable. + * Bug: Now allowing colons in a response start-line (e.g. HTTP/1.1 503 Service Unavailable: Back-end server is at capacity) + * Guzzle\Service\Resource\ResourceIteratorApplyBatched now internally uses the Guzzle\Common\Batch namespace + * Added Guzzle\Service\Plugin namespace and a PluginCollectionPlugin + * Added the ability to set POST fields and files in a service description + * Guzzle\Http\EntityBody::factory() now accepts objects with a __toString() method + * Adding a command.before_prepare event to clients + * Added BatchClosureTransfer and BatchClosureDivisor + * BatchTransferException now includes references to the batch divisor and transfer strategies + * Fixed some tests so that they pass more reliably + * Added Guzzle\Common\Log\ArrayLogAdapter + +* 2.6.6 (06-10-2012) + + * BC: Removing Guzzle\Http\Plugin\BatchQueuePlugin + * BC: Removing Guzzle\Service\Command\CommandSet + * Adding generic batching system (replaces the batch queue plugin and command set) + * Updating ZF cache and log adapters and now using ZF's composer repository + * Bug: Setting the name of each ApiParam when creating through an ApiCommand + * Adding result_type, result_doc, deprecated, and doc_url to service descriptions + * Bug: Changed the default cookie header casing back to 'Cookie' + +* 2.6.5 (06-03-2012) + + * BC: Renaming Guzzle\Http\Message\RequestInterface::getResourceUri() to getResource() + * BC: Removing unused AUTH_BASIC and AUTH_DIGEST constants from + * BC: Guzzle\Http\Cookie is now used to manage Set-Cookie data, not Cookie data + * BC: Renaming methods in the CookieJarInterface + * Moving almost all cookie logic out of the CookiePlugin and into the Cookie or CookieJar implementations + * Making the default glue for HTTP headers ';' instead of ',' + * Adding a removeValue to Guzzle\Http\Message\Header + * Adding getCookies() to request interface. + * Making it easier to add event subscribers to HasDispatcherInterface classes. Can now directly call addSubscriber() + +* 2.6.4 (05-30-2012) + + * BC: Cleaning up how POST files are stored in EntityEnclosingRequest objects. Adding PostFile class. + * BC: Moving ApiCommand specific functionality from the Inspector and on to the ApiCommand + * Bug: Fixing magic method command calls on clients + * Bug: Email constraint only validates strings + * Bug: Aggregate POST fields when POST files are present in curl handle + * Bug: Fixing default User-Agent header + * Bug: Only appending or prepending parameters in commands if they are specified + * Bug: Not requiring response reason phrases or status codes to match a predefined list of codes + * Allowing the use of dot notation for class namespaces when using instance_of constraint + * Added any_match validation constraint + * Added an AsyncPlugin + * Passing request object to the calculateWait method of the ExponentialBackoffPlugin + * Allowing the result of a command object to be changed + * Parsing location and type sub values when instantiating a service description rather than over and over at runtime + +* 2.6.3 (05-23-2012) + + * [BC] Guzzle\Common\FromConfigInterface no longer requires any config options. + * [BC] Refactoring how POST files are stored on an EntityEnclosingRequest. They are now separate from POST fields. + * You can now use an array of data when creating PUT request bodies in the request factory. + * Removing the requirement that HTTPS requests needed a Cache-Control: public directive to be cacheable. + * [Http] Adding support for Content-Type in multipart POST uploads per upload + * [Http] Added support for uploading multiple files using the same name (foo[0], foo[1]) + * Adding more POST data operations for easier manipulation of POST data. + * You can now set empty POST fields. + * The body of a request is only shown on EntityEnclosingRequest objects that do not use POST files. + * Split the Guzzle\Service\Inspector::validateConfig method into two methods. One to initialize when a command is created, and one to validate. + * CS updates + +* 2.6.2 (05-19-2012) + + * [Http] Better handling of nested scope requests in CurlMulti. Requests are now always prepares in the send() method rather than the addRequest() method. + +* 2.6.1 (05-19-2012) + + * [BC] Removing 'path' support in service descriptions. Use 'uri'. + * [BC] Guzzle\Service\Inspector::parseDocBlock is now protected. Adding getApiParamsForClass() with cache. + * [BC] Removing Guzzle\Common\NullObject. Use https://github.com/mtdowling/NullObject if you need it. + * [BC] Removing Guzzle\Common\XmlElement. + * All commands, both dynamic and concrete, have ApiCommand objects. + * Adding a fix for CurlMulti so that if all of the connections encounter some sort of curl error, then the loop exits. + * Adding checks to EntityEnclosingRequest so that empty POST files and fields are ignored. + * Making the method signature of Guzzle\Service\Builder\ServiceBuilder::factory more flexible. + +* 2.6.0 (05-15-2012) + + * [BC] Moving Guzzle\Service\Builder to Guzzle\Service\Builder\ServiceBuilder + * [BC] Executing a Command returns the result of the command rather than the command + * [BC] Moving all HTTP parsing logic to Guzzle\Http\Parsers. Allows for faster C implementations if needed. + * [BC] Changing the Guzzle\Http\Message\Response::setProtocol() method to accept a protocol and version in separate args. + * [BC] Moving ResourceIterator* to Guzzle\Service\Resource + * [BC] Completely refactored ResourceIterators to iterate over a cloned command object + * [BC] Moved Guzzle\Http\UriTemplate to Guzzle\Http\Parser\UriTemplate\UriTemplate + * [BC] Guzzle\Guzzle is now deprecated + * Moving Guzzle\Common\Guzzle::inject to Guzzle\Common\Collection::inject + * Adding Guzzle\Version class to give version information about Guzzle + * Adding Guzzle\Http\Utils class to provide getDefaultUserAgent() and getHttpDate() + * Adding Guzzle\Curl\CurlVersion to manage caching curl_version() data + * ServiceDescription and ServiceBuilder are now cacheable using similar configs + * Changing the format of XML and JSON service builder configs. Backwards compatible. + * Cleaned up Cookie parsing + * Trimming the default Guzzle User-Agent header + * Adding a setOnComplete() method to Commands that is called when a command completes + * Keeping track of requests that were mocked in the MockPlugin + * Fixed a caching bug in the CacheAdapterFactory + * Inspector objects can be injected into a Command object + * Refactoring a lot of code and tests to be case insensitive when dealing with headers + * Adding Guzzle\Http\Message\HeaderComparison for easy comparison of HTTP headers using a DSL + * Adding the ability to set global option overrides to service builder configs + * Adding the ability to include other service builder config files from within XML and JSON files + * Moving the parseQuery method out of Url and on to QueryString::fromString() as a static factory method. + +* 2.5.0 (05-08-2012) + + * Major performance improvements + * [BC] Simplifying Guzzle\Common\Collection. Please check to see if you are using features that are now deprecated. + * [BC] Using a custom validation system that allows a flyweight implementation for much faster validation. No longer using Symfony2 Validation component. + * [BC] No longer supporting "{{ }}" for injecting into command or UriTemplates. Use "{}" + * Added the ability to passed parameters to all requests created by a client + * Added callback functionality to the ExponentialBackoffPlugin + * Using microtime in ExponentialBackoffPlugin to allow more granular backoff stategies. + * Rewinding request stream bodies when retrying requests + * Exception is thrown when JSON response body cannot be decoded + * Added configurable magic method calls to clients and commands. This is off by default. + * Fixed a defect that added a hash to every parsed URL part + * Fixed duplicate none generation for OauthPlugin. + * Emitting an event each time a client is generated by a ServiceBuilder + * Using an ApiParams object instead of a Collection for parameters of an ApiCommand + * cache.* request parameters should be renamed to params.cache.* + * Added the ability to set arbitrary curl options on requests (disable_wire, progress, etc). See CurlHandle. + * Added the ability to disable type validation of service descriptions + * ServiceDescriptions and ServiceBuilders are now Serializable diff --git a/core/vendor/guzzle/guzzle/LICENSE b/core/vendor/guzzle/guzzle/LICENSE new file mode 100644 index 0000000..d51aa69 --- /dev/null +++ b/core/vendor/guzzle/guzzle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/core/vendor/guzzle/guzzle/README.md b/core/vendor/guzzle/guzzle/README.md new file mode 100644 index 0000000..4ea0c18 --- /dev/null +++ b/core/vendor/guzzle/guzzle/README.md @@ -0,0 +1,222 @@ +Guzzle, PHP HTTP client and webservice framework +================================================ + +Guzzle is a PHP HTTP client and framework for building RESTful web service clients. + +- Extremely powerful API provides all the power of cURL with a simple interface. +- Truly take advantage of HTTP/1.1 with persistent connections, connection pooling, and parallel requests. +- Service description DSL allows you build awesome web service clients faster. +- Symfony2 event-based plugin system allows you to completely modify the behavior of a request. +- Includes a custom node.js webserver to test your clients. +- Unit-tested with PHPUnit with 100% code coverage. + +Getting started +--------------- + +- [Download the phar](http://guzzlephp.org/guzzle.phar) and include it in your project ([minimal phar](http://guzzlephp.org/guzzle-min.phar)) +- Docs: [www.guzzlephp.org](http://www.guzzlephp.org/) +- Forum: https://groups.google.com/forum/?hl=en#!forum/guzzle +- IRC: [#guzzlephp](irc://irc.freenode.net/#guzzlephp) channel on irc.freenode.net + +### Installing via Composer + +The recommended way to install Guzzle is through [Composer](http://getcomposer.org). + +1. Add ``guzzle/guzzle`` as a dependency in your project's ``composer.json`` file: + + { + "require": { + "guzzle/guzzle": "*" + } + } + + Consider tightening your dependencies to a known version when deploying mission critical applications (e.g. ``2.7.*``). + +2. Download and install Composer: + + curl -s http://getcomposer.org/installer | php + +3. Install your dependencies: + + php composer.phar install + +4. Require Composer's autoloader + + Composer also prepares an autoload file that's capable of autoloading all of the classes in any of the libraries that it downloads. To use it, just add the following line to your code's bootstrap process: + + require 'vendor/autoload.php'; + +You can find out more on how to install Composer, configure autoloading, and other best-practices for defining dependencies at [getcomposer.org](http://getcomposer.org). + +Features +-------- + +- Supports GET, HEAD, POST, DELETE, PUT, PATCH, OPTIONS, and any custom verbs +- Allows full access to request and response headers +- Persistent connections are implicitly managed by Guzzle, resulting in huge performance benefits +- [Send requests in parallel](http://guzzlephp.org/tour/http.html#send-http-requests-in-parallel) +- Cookie sessions can be maintained between requests using the [CookiePlugin](http://guzzlephp.org/tour/http.html#cookie-session-plugin) +- Allows custom [entity bodies](http://guzzlephp.org/tour/http.html#entity-bodies), including sending data from a PHP stream and downloading data to a PHP stream +- Responses can be cached and served from cache using the [caching forward proxy plugin](http://guzzlephp.org/tour/http.html#php-based-caching-forward-proxy) +- Failed requests can be retried using [truncated exponential backoff](http://guzzlephp.org/tour/http.html#truncated-exponential-backoff) with custom retry policies +- Entity bodies can be validated automatically using Content-MD5 headers and the [MD5 hash validator plugin](http://guzzlephp.org/tour/http.html#md5-hash-validator-plugin) +- All data sent over the wire can be logged using the [LogPlugin](http://guzzlephp.org/tour/http.html#over-the-wire-logging) +- Subject/Observer signal slot system for unobtrusively [modifying request behavior](http://guzzlephp.org/guide/http/creating_plugins.html) +- Supports all of the features of libcurl including authentication, compression, redirects, SSL, proxies, etc +- Web service client framework for building future-proof interfaces to web services +- Includes a [service description DSL](http://guzzlephp.org/guide/service/service_descriptions.html) for quickly building webservice clients +- Full support for [URI templates](http://tools.ietf.org/html/rfc6570) +- Advanced batching functionality to efficiently send requests or commands in parallel with customizable batch sizes and transfer strategies + +HTTP basics +----------- + +```php + '***' +)); + +// Issue a path using a relative URL to the client's base URL +// Sends to http://www.example.com/api/v1/key/***/users +$request = $client->get('users'); +$response = $request->send(); + +// Relative URL that overwrites the path of the base URL +$request = $client->get('/test/123.php?a=b'); + +// Issue a head request on the base URL +$response = $client->head()->send(); +// Delete user 123 +$response = $client->delete('users/123')->send(); + +// Send a PUT request with custom headers +$response = $client->put('upload/text', array( + 'X-Header' => 'My Header' +), 'body of the request')->send(); + +// Send a PUT request using the contents of a PHP stream as the body +// Send using an absolute URL (overrides the base URL) +$response = $client->put('http://www.example.com/upload', array( + 'X-Header' => 'My Header' +), fopen('http://www.test.com/', 'r')); + +// Create a POST request with a file upload (notice the @ symbol): +$request = $client->post('http://localhost:8983/solr/update', null, array ( + 'custom_field' => 'my value', + 'file' => '@/path/to/documents.xml' +)); + +// Create a POST request and add the POST files manually +$request = $client->post('http://localhost:8983/solr/update') + ->addPostFiles(array( + 'file' => '/path/to/documents.xml' + )); + +// Responses are objects +echo $response->getStatusCode() . ' ' . $response->getReasonPhrase() . "\n"; + +// Requests and responses can be cast to a string to show the raw HTTP message +echo $request . "\n\n" . $response; + +// Create a request based on an HTTP message +$request = RequestFactory::fromMessage( + "PUT / HTTP/1.1\r\n" . + "Host: test.com:8081\r\n" . + "Content-Type: text/plain" + "Transfer-Encoding: chunked\r\n" . + "\r\n" . + "this is the body" +); +``` + +Send requests in parallel +------------------------- + +```php +send(array( + $client->get('users'), + $client->head('messages/123'), + $client->delete('orders/123') + )); +} catch (Guzzle\Common\Exception\ExceptionCollection $e) { + echo "The following requests encountered an exception: \n"; + foreach ($e as $exception) { + echo $exception->getRequest() . "\n" . $exception->getMessage() . "\n"; + } +} +``` + +URI templates +------------- + +Guzzle supports the entire [URI templates RFC](http://tools.ietf.org/html/rfc6570). + +```php + '/path/to', + 'a' => 'hi', + 'data' => array( + 'foo' => 'bar', + 'mesa' => 'jarjar' + ) +)); + +$request = $client->get('http://www.test.com{+path}{?a,data*}'); +``` + +The generated request URL would become: ``http://www.test.com/path/to?a=hi&foo=bar&mesa=jarajar`` + +You can specify URI templates and an array of additional template variables to use when creating requests: + +```php + 'hi' +)); + +$request = $client->get(array('/{?a,b}', array( + 'b' => 'there' +)); +``` + +The resulting URL would become ``http://test.com?a=hi&b=there`` + +Unit testing +------------ + +[![Build Status](https://secure.travis-ci.org/guzzle/guzzle.png)](http://travis-ci.org/guzzle/guzzle) + +You will first need to clone the GitHub repository: + +``` +git clone git@github.com:guzzle/guzzle.git +cd guzzle +``` + +Next you will need to make sure PHPUnit is configured, Composer is installed, and you have installed Guzzle's +testing dependencies. This can be achieved using the `test-init` Phing task. After running this task, run `phpunit`. + +``` +phing test-init +phpunit +``` + +If you do not have Phing installed, you will need to perform the installation steps manually: + +``` +curl -s http://getcomposer.org/installer | php +php composer.phar install --dev +cp phpunit.xml.dist phpunit.xml +phpunit +``` diff --git a/core/vendor/guzzle/guzzle/build.xml b/core/vendor/guzzle/guzzle/build.xml new file mode 100644 index 0000000..2fbe2e6 --- /dev/null +++ b/core/vendor/guzzle/guzzle/build.xml @@ -0,0 +1,93 @@ + + + + + + + Not yet implemented + + + + + + + + + + + + + + + Composer is installed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/vendor/guzzle/guzzle/composer.json b/core/vendor/guzzle/guzzle/composer.json new file mode 100644 index 0000000..529c98c --- /dev/null +++ b/core/vendor/guzzle/guzzle/composer.json @@ -0,0 +1,92 @@ +{ + "name": "guzzle/guzzle", + "type": "library", + "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"], + "homepage": "http://guzzlephp.org/", + "license": "MIT", + + "authors": [ + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" + } + ], + + "replace": { + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/parser": "self.version" + }, + + "require": { + "php": ">=5.3.2", + "ext-curl": "*", + "symfony/event-dispatcher": "2.*" + }, + + "autoload": { + "psr-0": { + "Guzzle\\Tests": "tests/", + "Guzzle": "src/" + } + }, + + "require-dev": { + "doctrine/common": "*", + "symfony/class-loader": "*", + "monolog/monolog": "1.*", + "zendframework/zend-cache": "2.0.*", + "zendframework/zend-log": "2.0.*", + "zendframework/zend-loader": "2.0.*", + "zendframework/zend-stdlib": "2.0.*", + "zendframework/zend-eventmanager": "2.0.*", + "zendframework/zend-servicemanager": "2.0.*", + "zend/zend-log1": "1.11", + "zend/zend-cache1": "1.11" + }, + + "repositories": [ + { + "type": "composer", + "url": "http://packages.zendframework.com/" + }, + { + "type":"package", + "package": { + "name": "zend/zend-log1", + "version": "1.11", + "source": { + "url": "http://framework.zend.com/svn", + "type": "svn", + "reference": "framework/standard/trunk/library/Zend/Log/" + }, + "target-dir": "Zend/Log", + "autoload": { + "psr-0": { "Zend_Log": "/" } + } + } + }, + { + "type":"package", + "package": { + "name": "zend/zend-cache1", + "version": "1.11", + "source": { + "url": "http://framework.zend.com/svn", + "type": "svn", + "reference": "framework/standard/trunk/library/Zend/Cache/" + }, + "target-dir": "Zend/Cache", + "autoload": { + "psr-0": { "Zend_Cache": "/" } + } + } + } + ] +} diff --git a/core/vendor/guzzle/guzzle/phar-stub-min.php b/core/vendor/guzzle/guzzle/phar-stub-min.php new file mode 100644 index 0000000..8fe23f1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/phar-stub-min.php @@ -0,0 +1,2 @@ +registerNamespaces(array( + 'Guzzle' => 'phar://guzzle.phar/src', + 'Symfony\\Component\\EventDispatcher' => 'phar://guzzle.phar/vendor/symfony/event-dispatcher', + 'Doctrine' => 'phar://guzzle.phar/vendor/doctrine/common/lib', + 'Monolog' => 'phar://guzzle.phar/vendor/monolog/monolog/src' +)); +$classLoader->register(); + +__HALT_COMPILER(); diff --git a/core/vendor/guzzle/guzzle/phpunit.xml.dist b/core/vendor/guzzle/guzzle/phpunit.xml.dist new file mode 100644 index 0000000..f2375ae --- /dev/null +++ b/core/vendor/guzzle/guzzle/phpunit.xml.dist @@ -0,0 +1,46 @@ + + + + + + ./tests/Guzzle/Tests + + + + + + + + + + ./src/Guzzle + + ./src/Guzzle + ./src/Guzzle/Common/GuzzleException.php + ./src/Guzzle/Http/HttpException.php + ./src/Guzzle/Http/Exception/ServerErrorResponseException.php + ./src/Guzzle/Http/Exception/ClientErrorResponseException.php + ./src/Guzzle/Common/Exception/BadMethodCallException.php + ./src/Guzzle/Common/Exception/InvalidArgumentException.php + ./src/Guzzle/Common/Exception/RuntimeException.php + ./src/Guzzle/Common/Exception/UnexpectedValueException.php + ./src/Guzzle/Service/Exception/ClientNotFoundException.php + ./src/Guzzle/Service/Exception/CommandException.php + ./src/Guzzle/Service/Exception/DescriptionBuilderException.php + ./src/Guzzle/Service/Exception/ServiceBuilderException.php + ./src/Guzzle/Service/Exception/ServiceNotFoundException.php + ./src/Guzzle/Service/Exception/ValidationException.php + ./src/Guzzle/Service/Exception/JsonException.php + + + + + diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php new file mode 100644 index 0000000..f022555 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/AbstractHasDispatcher.php @@ -0,0 +1,66 @@ +eventDispatcher = $eventDispatcher; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->eventDispatcher = new EventDispatcher(); + } + + return $this->eventDispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, array $context = array()) + { + $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + /** + * {@inheritdoc} + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/AbstractBatchDecorator.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/AbstractBatchDecorator.php new file mode 100644 index 0000000..06e290f --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/AbstractBatchDecorator.php @@ -0,0 +1,79 @@ +decoratedBatch = $decoratedBatch; + } + + /** + * Allow decorators to implement custom methods + * + * @param string $method Missing method name + * @param array $args Method arguments + * + * @return mixed + * @codeCoverageIgnore + */ + public function __call($method, array $args = null) + { + return call_user_func_array(array($this->decoratedBatch, $method), $args); + } + + /** + * {@inheritdoc} + */ + public function add($item) + { + $this->decoratedBatch->add($item); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function flush() + { + return $this->decoratedBatch->flush(); + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + return $this->decoratedBatch->isEmpty(); + } + + /** + * Trace the decorators associated with the batch + * + * @return array + */ + public function getDecorators(array $found = array()) + { + $found[] = $this; + if (method_exists($this->decoratedBatch, 'getDecorators')) { + return $this->decoratedBatch->getDecorators($found); + } + + return $found; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/Batch.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/Batch.php new file mode 100644 index 0000000..4bfbf95 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/Batch.php @@ -0,0 +1,111 @@ +transferStrategy = $transferStrategy; + $this->divisionStrategy = $divisionStrategy; + $this->queue = new \SplQueue(); + $this->queue->setIteratorMode(\SplQueue::IT_MODE_DELETE); + $this->dividedBatches = array(); + } + + /** + * {@inheritdoc} + */ + public function add($item) + { + $this->queue->enqueue($item); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function flush() + { + $this->createBatches(); + + $items = array(); + foreach ($this->dividedBatches as $batchIndex => $dividedBatch) { + while ($dividedBatch->valid()) { + $batch = $dividedBatch->current(); + $dividedBatch->next(); + try { + $this->transferStrategy->transfer($batch); + $items = array_merge($items, $batch); + } catch (\Exception $e) { + throw new BatchTransferException($batch, $items, $e, $this->transferStrategy, $this->divisionStrategy); + } + } + // Keep the divided batch down to a minimum in case of a later exception + unset($this->dividedBatches[$batchIndex]); + } + + return $items; + } + + /** + * {@inheritdoc} + */ + public function isEmpty() + { + return count($this->queue) == 0 && count($this->dividedBatches) == 0; + } + + /** + * Create batches for any queued items + */ + protected function createBatches() + { + if (count($this->queue)) { + if ($batches = $this->divisionStrategy->createBatches($this->queue)) { + // Convert arrays into iterators + if (is_array($batches)) { + $batches = new \ArrayIterator($batches); + } + $this->dividedBatches[] = $batches; + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchBuilder.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchBuilder.php new file mode 100644 index 0000000..41777fa --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchBuilder.php @@ -0,0 +1,219 @@ + 'Guzzle\Http\BatchRequestTransfer', + 'command' => 'Guzzle\Service\Command\BatchCommandTransfer' + ); + + /** + * Create a new instance of the BatchBuilder + * + * @return BatchBuilder + */ + public static function factory() + { + return new self(); + } + + /** + * Automatically flush the batch when the size of the queue reaches a + * certain threshold. Enables the {@see FlushingBatch} decorator. + * + * @param $threshold Number of items to allow in the queue before a flush + * + * @return BatchBuilder + */ + public function autoFlushAt($threshold) + { + $this->autoFlush = $threshold; + + return $this; + } + + /** + * Maintain a history of all items that have been transferred using the + * batch. Enables the {@see HistoryBatch} decorator. + * + * @return BatchBuilder + */ + public function keepHistory() + { + $this->history = true; + + return $this; + } + + /** + * Buffer exceptions thrown during transfer so that you can transfer as + * much as possible, and after a transfer completes, inspect each exception + * that was thrown. Enables the {@see ExceptionBufferingBatch} decorator + * + * @return BatchBuilder + */ + public function bufferExceptions() + { + $this->exceptionBuffering = true; + + return $this; + } + + /** + * Notify a callable each time a batch flush completes. Enables the + * {@see NotifyingBatch} decorator. + * + * @param mixed $callable Callable function to notify + * + * @return BatchBuilder + * @throws InvalidArgumentException if the argument is not callable + */ + public function notify($callable) + { + $this->afterFlush = $callable; + + return $this; + } + + /** + * Configures the batch to transfer batches of requests. Associates a + * {@see \Guzzle\Http\BatchRequestTransfer} object as both the transfer and + * divisor strategy. + * + * @param int $batchSize Batch size for each batch of requests + * + * @return BatchBuilder + */ + public function transferRequests($batchSize = 50) + { + $className = self::$mapping['request']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Configures the batch to transfer batches commands. Associates as + * {@see \Guzzle\Service\Command\BatchCommandTransfer} as both the transfer + * and divisor strategy. + * + * @param int $batchSize Batch size for each batch of commands + * + * @return BatchBuilder + */ + public function transferCommands($batchSize = 50) + { + $className = self::$mapping['command']; + $this->transferStrategy = new $className($batchSize); + $this->divisorStrategy = $this->transferStrategy; + + return $this; + } + + /** + * Specify the strategy used to divide the queue into an array of batches + * + * @param BatchDivisorInterface $divisorStrategy Strategy used to divide a batch queue into batches + * + * @return BatchBuilder + */ + public function createBatchesWith(BatchDivisorInterface $divisorStrategy) + { + $this->divisorStrategy = $divisorStrategy; + + return $this; + } + + /** + * Specify the strategy used to transport the items when flush is called + * + * @param BatchTransferInterface $transferStrategy How items are transferred + * + * @return BatchBuilder + */ + public function transferWith(BatchTransferInterface $transferStrategy) + { + $this->transferStrategy = $transferStrategy; + + return $this; + } + + /** + * Create and return the instantiated batch + * + * @return BatchInterface + * @throws RuntimeException if no transfer strategy has been specified + */ + public function build() + { + if (!$this->transferStrategy) { + throw new RuntimeException('No transfer strategy has been specified'); + } + + if (!$this->divisorStrategy) { + throw new RuntimeException('No divisor strategy has been specified'); + } + + $batch = new Batch($this->transferStrategy, $this->divisorStrategy); + + if ($this->exceptionBuffering) { + $batch = new ExceptionBufferingBatch($batch); + } + + if ($this->afterFlush) { + $batch = new NotifyingBatch($batch, $this->afterFlush); + } + + if ($this->autoFlush) { + $batch = new FlushingBatch($batch, $this->autoFlush); + } + + if ($this->history) { + $batch = new HistoryBatch($batch); + } + + return $batch; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchClosureDivisor.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchClosureDivisor.php new file mode 100644 index 0000000..9939ca3 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchClosureDivisor.php @@ -0,0 +1,45 @@ +callable = $callable; + $this->context = $context; + } + + /** + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + return call_user_func($this->callable, $queue, $this->context); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchClosureTransfer.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchClosureTransfer.php new file mode 100644 index 0000000..63c891a --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchClosureTransfer.php @@ -0,0 +1,47 @@ +callable = $callable; + $this->context = $context; + } + + /** + * {@inheritDoc} + */ + public function transfer(array $batch) + { + return empty($batch) ? null : call_user_func($this->callable, $batch, $this->context); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchDivisorInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchDivisorInterface.php new file mode 100644 index 0000000..c111e53 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchDivisorInterface.php @@ -0,0 +1,20 @@ +size = $size; + } + + /** + * Set the size of each batch + * + * @param int $size Size of each batch + * + * @return BatchSizeDivisor + */ + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + /** + * Get the size of each batch + * + * @return int + */ + public function getSize() + { + return $this->size; + } + + /** + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + return array_chunk(iterator_to_array($queue, false), $this->size); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchTransferInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchTransferInterface.php new file mode 100644 index 0000000..b9829b3 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/BatchTransferInterface.php @@ -0,0 +1,16 @@ +decoratedBatch->isEmpty()) { + try { + $transferredItems = $this->decoratedBatch->flush(); + } catch (BatchTransferException $e) { + $this->exceptions[] = $e; + $transferredItems = $e->getTransferredItems(); + } + $items = array_merge($items, $transferredItems); + } + + return $items; + } + + /** + * Get the buffered exceptions + * + * @return array Array of BatchTransferException objects + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Clear the buffered exceptions + */ + public function clearExceptions() + { + $this->exceptions = array(); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/FlushingBatch.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/FlushingBatch.php new file mode 100644 index 0000000..6e27e89 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/FlushingBatch.php @@ -0,0 +1,67 @@ +threshold = $threshold; + parent::__construct($decoratedBatch); + } + + /** + * Set the auto-flush threshold + * + * @param int $threshold The auto-flush threshold + * @return FlushingBatch + */ + public function setThreshold($threshold) + { + $this->threshold = $threshold; + + return $this; + } + + /** + * Get the auto-flush threshold + * + * @return int + */ + public function getThreshold() + { + return $this->threshold; + } + + /** + * {@inheritdoc} + */ + public function add($item) + { + $this->decoratedBatch->add($item); + if (++$this->currentTotal >= $this->threshold) { + $this->currentTotal = 0; + $this->decoratedBatch->flush(); + } + + return $this; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/HistoryBatch.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/HistoryBatch.php new file mode 100644 index 0000000..fab8c5d --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/HistoryBatch.php @@ -0,0 +1,45 @@ +history[] = $item; + $this->decoratedBatch->add($item); + + return $this; + } + + /** + * Get the batch history + * + * @return array + */ + public function getHistory() + { + return $this->history; + } + + /** + * Clear the batch history + */ + public function clearHistory() + { + $this->history = array(); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/NotifyingBatch.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/NotifyingBatch.php new file mode 100644 index 0000000..87e1169 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Batch/NotifyingBatch.php @@ -0,0 +1,43 @@ +callable = $callable; + parent::__construct($decoratedBatch); + } + + /** + * {@inheritdoc} + */ + public function flush() + { + $items = $this->decoratedBatch->flush(); + call_user_func($this->callable, $items); + + return $items; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/AbstractCacheAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/AbstractCacheAdapter.php new file mode 100644 index 0000000..3727c41 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/AbstractCacheAdapter.php @@ -0,0 +1,21 @@ +cache; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/CacheAdapterFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/CacheAdapterFactory.php new file mode 100644 index 0000000..8639b82 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/CacheAdapterFactory.php @@ -0,0 +1,80 @@ +newInstanceArgs($args); + } + } catch (\Exception $e) { + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/CacheAdapterInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/CacheAdapterInterface.php new file mode 100644 index 0000000..8435a4c --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/CacheAdapterInterface.php @@ -0,0 +1,61 @@ +callables = $callables; + } + + /** + * {@inheritdoc} + */ + public function contains($id, array $options = null) + { + return call_user_func($this->callables['contains'], $id, $options); + } + + /** + * {@inheritdoc} + */ + public function delete($id, array $options = null) + { + return call_user_func($this->callables['delete'], $id, $options); + } + + /** + * {@inheritdoc} + */ + public function fetch($id, array $options = null) + { + return call_user_func($this->callables['fetch'], $id, $options); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = false, array $options = null) + { + return call_user_func($this->callables['save'], $id, $data, $lifeTime, $options); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/DoctrineCacheAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/DoctrineCacheAdapter.php new file mode 100644 index 0000000..b241ea5 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/DoctrineCacheAdapter.php @@ -0,0 +1,55 @@ +cache = $cache; + } + + /** + * {@inheritdoc} + */ + public function contains($id, array $options = null) + { + return $this->cache->contains($id); + } + + /** + * {@inheritdoc} + */ + public function delete($id, array $options = null) + { + return $this->cache->delete($id); + } + + /** + * {@inheritdoc} + */ + public function fetch($id, array $options = null) + { + return $this->cache->fetch($id); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($id, $data, $lifeTime); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/NullCacheAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/NullCacheAdapter.php new file mode 100644 index 0000000..0ce70de --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/NullCacheAdapter.php @@ -0,0 +1,50 @@ +cache = $cache; + } + + /** + * {@inheritdoc} + */ + public function contains($id, array $options = null) + { + return $this->cache->test($id); + } + + /** + * {@inheritdoc} + */ + public function delete($id, array $options = null) + { + return $this->cache->remove($id); + } + + /** + * {@inheritdoc} + */ + public function fetch($id, array $options = null) + { + return $this->cache->load($id); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->save($data, $id, array(), $lifeTime); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/Zf2CacheAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/Zf2CacheAdapter.php new file mode 100644 index 0000000..e029a63 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Cache/Zf2CacheAdapter.php @@ -0,0 +1,83 @@ +cache = $cache; + $this->defaultOptions = array_merge(array( + 'contains' => array(), + 'delete' => array(), + 'fetch' => array(), + 'save' => array() + ), $defaultOptions); + } + + /** + * {@inheritdoc} + */ + public function contains($id, array $options = null) + { + $options = $options + ? array_merge($this->defaultOptions['contains'], $options) + : $this->defaultOptions['contains']; + + return $this->cache->hasItem($id, $options); + } + + /** + * {@inheritdoc} + */ + public function delete($id, array $options = null) + { + $options = $options + ? array_merge($this->defaultOptions['delete'], $options) + : $this->defaultOptions['delete']; + + return $this->cache->removeItem($id, $options); + } + + /** + * {@inheritdoc} + */ + public function fetch($id, array $options = null) + { + $options = $options + ? array_merge($this->defaultOptions['fetch'], $options) + : $this->defaultOptions['fetch']; + + return $this->cache->getItem($id, $options); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = false, array $options = null) + { + return $this->cache->setItem($id, $data, array_merge($this->defaultOptions['save'], $options ?: array(), array( + 'ttl' => $lifeTime + ))); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php new file mode 100644 index 0000000..69afccf --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Collection.php @@ -0,0 +1,387 @@ +data = array(); + } else { + $this->data = $data; + } + } + + /** + * Convert the object to a string + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * Add a value to a key. If a key of the same name has + * already been added, the key value will be converted into an array + * and the new value will be pushed to the end of the array. + * + * @param string $key Key to add + * @param mixed $value Value to add to the key + * + * @return Collection Returns a reference to the object. + */ + public function add($key, $value) + { + if (!array_key_exists($key, $this->data)) { + $this->data[$key] = $value; + } else { + if (!is_array($this->data[$key])) { + $this->data[$key] = array($this->data[$key], $value); + } else { + $this->data[$key][] = $value; + } + } + + return $this; + } + + /** + * Removes all key value pairs + * + * @return Collection + */ + public function clear() + { + $this->data = array(); + + return $this; + } + + /** + * Return the number of keys + * + * @return integer + */ + public function count() + { + return count($this->data); + } + + /** + * Iterates over each key value pair in the collection passing them to the + * Closure. If the Closure function returns true, the current value from + * input is returned into the result Collection. The Closure must accept + * three parameters: (string) $key, (string) $value and + * return Boolean TRUE or FALSE for each value. + * + * @param Closure $closure Closure evaluation function + * @param bool $static Set to TRUE to use the same class as the return + * rather than returning a Collection object + * + * @return Collection + */ + public function filter(\Closure $closure, $static = true) + { + $collection = ($static) ? new static() : new self(); + foreach ($this->data as $key => $value) { + if ($closure($key, $value)) { + $collection->add($key, $value); + } + } + + return $collection; + } + + /** + * Get an iterator object + * + * @return array + */ + public function getIterator() + { + return new \ArrayIterator($this->data); + } + + /** + * Get a specific key value. + * + * @param string $key Key to retrieve. + * @param mixed $default If the key is not found, set this + * value to specify a default + * + * @return mixed|null Value of the key or NULL + */ + public function get($key, $default = null) + { + if (array_key_exists($key, $this->data)) { + return $this->data[$key]; + } + + return $default; + } + + /** + * Get all or a subset of matching key value pairs + * + * @param array $keys Pass a single key or an array of keys to retrieve + * only a particular subset of key value pair. + * + * @return array Returns an array of all matching key value pairs + */ + public function getAll(array $keys = null) + { + if (!$keys) { + return $this->data; + } + + $matches = array(); + $allKeys = $this->getKeys(); + foreach ($keys as $key) { + if (in_array($key, $allKeys)) { + $matches[$key] = $this->data[$key]; + } + } + + return $matches; + } + + /** + * Get all keys in the collection + * + * @return array + */ + public function getKeys() + { + return array_keys($this->data); + } + + /** + * Returns whether or not the specified key is present. + * + * @param string $key The key for which to check the existence. + * + * @return bool + */ + public function hasKey($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Case insensitive search the keys in the collection + * + * @param string $key Key to search for + * + * @return false|string Returns false if not found, otherwise returns the key + */ + public function keySearch($key) + { + foreach (array_keys($this->data) as $k) { + if (!strcasecmp($k, $key)) { + return $k; + } + } + + return false; + } + + /** + * Checks if any keys contains a certain value + * + * @param string $value Value to search for + * + * @return mixed Returns the key if the value was found FALSE if the value + * was not found. + */ + public function hasValue($value) + { + return array_search($value, $this->data); + } + + /** + * Returns a Collection containing all the elements of the collection after + * applying the callback function to each one. The Closure should accept + * three parameters: (string) $key, (string) $value, (array) $context and + * return a modified value + * + * @param Closure $closure Closure to apply + * @param array $context Context to pass to the closure + * @param bool $static Set to TRUE to use the same class as the return + * rather than returning a Collection object + * + * @return Collection + */ + public function map(\Closure $closure, array $context = array(), $static = true) + { + $collection = $static ? new static() : new self(); + foreach ($this as $key => $value) { + $collection->add($key, $closure($key, $value, $context)); + } + + return $collection; + } + + /** + * Add and merge in a Collection or array of key value pair data. + * + * Invalid $data arguments will silently fail. + * + * @param Collection|array $data Associative array of key value pair data + * + * @return Collection Returns a reference to the object. + */ + public function merge($data) + { + if ($data instanceof self) { + $data = $data->getAll(); + } elseif (!is_array($data)) { + return $this; + } + + if (!empty($data)) { + if (empty($this->data)) { + $this->data = $data; + } else { + foreach ($data as $key => $value) { + $this->add($key, $value); + } + } + } + + return $this; + } + + /** + * ArrayAccess implementation of offsetExists() + * + * @param string $offset Array key + * + * @return bool + */ + public function offsetExists($offset) + { + return $this->hasKey($offset) !== false; + } + + /** + * ArrayAccess implementation of offsetGet() + * + * @param string $offset Array key + * + * @return null|mixed + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * ArrayAccess implementation of offsetGet() + * + * @param string $offset Array key + * @param mixed $value Value to set + */ + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + /** + * ArrayAccess implementation of offsetUnset() + * + * @param string $offset Array key + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } + + /** + * Remove a specific key value pair + * + * @param string $key A key to remove + * + * @return Collection + */ + public function remove($key) + { + unset($this->data[$key]); + + return $this; + } + + /** + * Replace the data of the object with the value of an array + * + * @param array $data Associative array of data + * + * @return Collection Returns a reference to the object + */ + public function replace(array $data) + { + $this->data = $data; + + return $this; + } + + /** + * Set a key value pair + * + * @param string $key Key to set + * @param mixed $value Value to set + * + * @return Collection Returns a reference to the object + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Inject configuration settings into an input string + * + * @param string $input Input to inject + * @param Collection $config Configuration data to inject into the input + * + * @return string + */ + public function inject($input) + { + // Only perform the preg callback if needed + if (strpos($input, '{') === false) { + return $input; + } + + return preg_replace_callback('/{\s*([A-Za-z_\-\.0-9]+)\s*}/', array($this, 'getPregMatchValue'), $input); + } + + /** + * Return a collection value for a match array of a preg_replace function + * + * @param array $matches preg_replace* matches + * + * @return mixed + */ + public function getPregMatchValue(array $matches) + { + return $this->get($matches[1]); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php new file mode 100644 index 0000000..e02546c --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Event.php @@ -0,0 +1,66 @@ +context = $context; + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->context); + } + + /** + * {@inheritdoc} + */ + public function offsetGet($offset) + { + return array_key_exists($offset, $this->context) ? $this->context[$offset] : null; + } + + /** + * {@inheritdoc} + */ + public function offsetSet($offset, $value) + { + $this->context[$offset] = $value; + } + + /** + * {@inheritdoc} + */ + public function offsetExists($offset) + { + return array_key_exists($offset, $this->context); + } + + /** + * {@inheritdoc} + */ + public function offsetUnset($offset) + { + unset($this->context[$offset]); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php new file mode 100644 index 0000000..a78cfd7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/BadMethodCallException.php @@ -0,0 +1,7 @@ +batch = $batch; + $this->transferredItems = $transferredItems; + $this->transferStrategy = $transferStrategy; + $this->divisorStrategy = $divisorStrategy; + parent::__construct('Exception encountered while transferring batch', $exception->getCode(), $exception); + } + + /** + * Get the batch that we being sent when the exception occurred + * + * @return array + */ + public function getBatch() + { + return $this->batch; + } + + /** + * Get the items transferred at the point in which the exception was encountered + * + * @return array + */ + public function getTransferredItems() + { + return $this->transferredItems; + } + + /** + * Get the transfer strategy + * + * @return TransferStrategy + */ + public function getTransferStrategy() + { + return $this->transferStrategy; + } + + /** + * Get the divisor strategy + * + * @return DivisorStrategy + */ + public function getDivisorStrategy() + { + return $this->divisorStrategy; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php new file mode 100644 index 0000000..37c52d8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/ExceptionCollection.php @@ -0,0 +1,60 @@ +exceptions[] = $exception; + } + } elseif ($e instanceof \Exception) { + $this->exceptions[] = $e; + } + + $this->message = implode("\n", array_map(function($e) { + return $e->getMessage(); + }, $this->exceptions)); + + return $this; + } + + /** + * Get the total number of request exceptions + * + * @return int + */ + public function count() + { + return count($this->exceptions); + } + + /** + * Allows array-like iteration over the request exceptions + * + * @return ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->exceptions); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..6392a9e --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Exception/InvalidArgumentException.php @@ -0,0 +1,7 @@ + array(), + 'camel' => array() + ); + + /** + * @var int Max entries per cache + */ + protected $maxCacheSize; + + /** + * @var InflectorInterface Decorated inflector + */ + protected $decoratedInflector; + + /** + * @param InflectorInterface $inflector Inflector being decorated + * @param int $maxCacheSize Maximum number of cached items to hold per cache + */ + public function __construct(InflectorInterface $inflector, $maxCacheSize = 500) + { + $this->decoratedInflector = $inflector; + $this->maxCacheSize = $maxCacheSize; + } + + /** + * {@inheritdoc} + */ + public function snake($word) + { + if (!isset($this->cache['snake'][$word])) { + $this->pruneCache('snake'); + $this->cache['snake'][$word] = $this->decoratedInflector->snake($word); + } + + return $this->cache['snake'][$word]; + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + if (!isset($this->cache['camel'][$word])) { + $this->pruneCache('camel'); + $this->cache['camel'][$word] = $this->decoratedInflector->camel($word); + } + + return $this->cache['camel'][$word]; + } + + /** + * Prune one of the named caches by removing 20% of the cache if it is full + * + * @param string $cache Type of cache to prune + */ + protected function pruneCache($cache) + { + if (count($this->cache[$cache]) == $this->maxCacheSize) { + $this->cache[$cache] = array_slice($this->cache[$cache], $this->maxCacheSize * 0.2); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Inflection/PreComputedInflector.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Inflection/PreComputedInflector.php new file mode 100644 index 0000000..119e26d --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Inflection/PreComputedInflector.php @@ -0,0 +1,68 @@ + array(), + 'camel' => array() + ); + + /** + * @var InflectorInterface Decorated inflector + */ + protected $decoratedInflector; + + /** + * Decorate using a pre-computed map. + * + * @param InflectorInterface $inflector Inflector being decorated + * @param array $snake Hash of pre-computed camel to snake + * @param array $camel Hash of pre-computed snake to camel + * @param bool $mirror Mirror snake and camel reflections + */ + public function __construct(InflectorInterface $inflector, array $snake = array(), array $camel = array(), $mirror = false) + { + if ($mirror) { + $camel = array_merge(array_flip($snake), $camel); + $snake = array_merge(array_flip($camel), $snake); + } + + $this->decoratedInflector = $inflector; + $this->mapping = array( + 'snake' => $snake, + 'camel' => $camel + ); + } + + /** + * {@inheritdoc} + */ + public function snake($word) + { + return isset($this->mapping['snake'][$word]) + ? $this->mapping['snake'][$word] + : $this->decoratedInflector->snake($word); + } + + /** + * Converts strings from snake_case to upper CamelCase + * + * @param string $word Value to convert into upper CamelCase + * + * @return string + */ + public function camel($word) + { + return isset($this->mapping['camel'][$word]) + ? $this->mapping['camel'][$word] + : $this->decoratedInflector->camel($word); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/AbstractLogAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/AbstractLogAdapter.php new file mode 100644 index 0000000..9dcaa6b --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/AbstractLogAdapter.php @@ -0,0 +1,21 @@ +log; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/ArrayLogAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/ArrayLogAdapter.php new file mode 100644 index 0000000..0c5b3e7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/ArrayLogAdapter.php @@ -0,0 +1,41 @@ +logs[] = array('message' => $message, 'priority' => $priority, 'extras' => $extras); + } + + /** + * Get logged entries + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Clears logged entries + */ + public function clearLogs() + { + $this->logs = array(); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/ClosureLogAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/ClosureLogAdapter.php new file mode 100644 index 0000000..cc0eaa3 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/ClosureLogAdapter.php @@ -0,0 +1,32 @@ +log = $logObject; + } + + /** + * {@inheritdoc} + */ + public function log($message, $priority = LOG_INFO, $extras = null) + { + call_user_func($this->log, $message, $priority, $extras); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/LogAdapterInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/LogAdapterInterface.php new file mode 100644 index 0000000..1f2dd33 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/LogAdapterInterface.php @@ -0,0 +1,20 @@ + Logger::DEBUG, + LOG_INFO => Logger::INFO, + LOG_WARNING => Logger::WARNING, + LOG_ERR => Logger::ERROR, + LOG_CRIT => Logger::CRITICAL, + LOG_ALERT => Logger::ALERT + ); + + /** + * {@inheritdoc} + */ + public function __construct(Logger $logObject) + { + $this->log = $logObject; + } + + /** + * {@inheritdoc} + */ + public function log($message, $priority = LOG_INFO, $extras = null) + { + $this->log->addRecord(self::$mapping[$priority], $message); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/Zf1LogAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/Zf1LogAdapter.php new file mode 100644 index 0000000..3250060 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/Zf1LogAdapter.php @@ -0,0 +1,25 @@ +log = $logObject; + } + + /** + * {@inheritdoc} + */ + public function log($message, $priority = LOG_INFO, $extras = null) + { + $this->log->log($message, $priority, $extras); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/Zf2LogAdapter.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/Zf2LogAdapter.php new file mode 100644 index 0000000..d1aca61 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Log/Zf2LogAdapter.php @@ -0,0 +1,27 @@ +log = $logObject; + } + + /** + * {@inheritdoc} + */ + public function log($message, $priority = LOG_INFO, $extras = null) + { + $this->log->log($priority, $message, $extras ?: array()); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Stream.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Stream.php new file mode 100644 index 0000000..714806b --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Stream.php @@ -0,0 +1,315 @@ + array( + 'r' => true, 'w+' => true, 'r+' => true, 'x+' => true, 'c+', 'x+' => true, + 'rb' => true, 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b', 'x+' => true, + 'rt' => true, 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t', 'x+' => true + ), + 'write' => array( + 'w' => true, 'w+' => true, 'rw' => true, 'r+' => true, 'x+' => true, 'c+', 'x+' => true, + 'w+b' => true, 'r+b' => true, 'x+b' => true, 'c+b', 'x+' => true, + 'w+t' => true, 'r+t' => true, 'x+t' => true, 'c+t', 'x+' => true + ) + ); + + /** + * Construct a new Stream + * + * @param resource $stream Stream resource to wrap + * @param int $size Size of the stream in bytes. Only pass this + * parameter if the size cannot be obtained from + * the stream. + * + * @throws InvalidArgumentException if the stream is not a stream resource + */ + public function __construct($stream, $size = null) + { + if (!is_resource($stream)) { + throw new InvalidArgumentException('Stream must be a resource'); + } + + $this->size = $size; + $this->stream = $stream; + $this->rebuildCache(); + } + + /** + * Closes the stream when the helper is destructed + */ + public function __destruct() + { + if (is_resource($this->stream)) { + fclose($this->stream); + } + } + + /** + * Reprocess stream metadata + */ + protected function rebuildCache() + { + $this->cache = stream_get_meta_data($this->stream); + $this->cache[self::STREAM_TYPE] = strtolower($this->cache[self::STREAM_TYPE]); + $this->cache[self::WRAPPER_TYPE] = strtolower($this->cache[self::WRAPPER_TYPE]); + $this->cache[self::IS_LOCAL] = stream_is_local($this->stream); + $this->cache[self::IS_READABLE] = isset(self::$readWriteHash['read'][$this->cache['mode']]); + $this->cache[self::IS_WRITABLE] = isset(self::$readWriteHash['write'][$this->cache['mode']]); + } + + /** + * Convert the stream to a string if the stream is readable and the stream + * is seekable. This logic is enforced to ensure that outputting the stream + * as a string does not affect an actual cURL request using non-repeatable + * streams. + * + * @return string + */ + public function __toString() + { + if (!$this->isReadable() || (!$this->isSeekable() && $this->isConsumed())) { + return ''; + } + + $body = stream_get_contents($this->stream, -1, 0); + $this->seek(0); + + return $body; + } + + /** + * Get stream metadata + * + * @param string $key Specific metdata to retrieve + * + * @return array|mixed|null + */ + public function getMetaData($key = null) + { + $meta = stream_get_meta_data($this->stream); + + return !$key ? $meta : (array_key_exists($key, $meta) ? $meta[$key] : null); + } + + /** + * Get the stream resource + * + * @return resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * Get the stream wrapper type + * + * @return string + */ + public function getWrapper() + { + return $this->cache[self::WRAPPER_TYPE]; + } + + /** + * Wrapper specific data attached to this stream. + * + * @return string + */ + public function getWrapperData() + { + return $this->getMetaData('wrapper_data') ?: array(); + } + + /** + * Get a label describing the underlying implementation of the stream + * + * @return string + */ + public function getStreamType() + { + return $this->cache[self::STREAM_TYPE]; + } + + /** + * Get the URI/filename associated with this stream + * + * @return string + */ + public function getUri() + { + return $this->cache['uri']; + } + + /** + * Get the size of the stream if able + * + * @return int|false + */ + public function getSize() + { + if ($this->size !== null) { + return $this->size; + } + + // If the stream is a file based stream and local, then check the filesize + if ($this->isLocal() && $this->getWrapper() == 'plainfile' && $this->getUri() && file_exists($this->getUri())) { + return filesize($this->getUri()); + } + + // Only get the size based on the content if the the stream is readable + // and seekable so as to not interfere with actually reading the data + if (!$this->cache[self::IS_READABLE] || !$this->cache[self::SEEKABLE]) { + return false; + } else { + $this->size = strlen((string) $this); + $this->seek(0); + return $this->size; + } + } + + /** + * Check if the stream is readable + * + * @return bool + */ + public function isReadable() + { + return $this->cache[self::IS_READABLE]; + } + + /** + * Check if the stream is writable + * + * @return bool + */ + public function isWritable() + { + return $this->cache[self::IS_WRITABLE]; + } + + /** + * Check if the stream has been consumed + * + * @return bool + */ + public function isConsumed() + { + return feof($this->stream); + } + + /** + * Check if the stream is a local stream vs a remote stream + * + * @return bool + */ + public function isLocal() + { + return $this->cache[self::IS_LOCAL]; + } + + /** + * Check if the string is repeatable + * + * @return bool + */ + public function isSeekable() + { + return $this->cache[self::SEEKABLE]; + } + + /** + * Specify the size of the stream in bytes + * + * @param int $size Size of the stream contents in bytes + * + * @return Stream + */ + public function setSize($size) + { + $this->size = $size; + + return $this; + } + + /** + * Seek to a position in the stream + * + * @param int $offset Stream offset + * @param int $whence Where the offset is applied + * + * @return bool Returns TRUE on success or FALSE on failure + * @link http://www.php.net/manual/en/function.fseek.php + */ + public function seek($offset, $whence = SEEK_SET) + { + return $this->cache[self::SEEKABLE] ? fseek($this->stream, $offset, $whence) === 0 : false; + } + + /** + * Read data from the stream + * + * @param int $length Up to length number of bytes read. + * + * @return string|bool Returns the data read from the stream or FALSE on + * failure or EOF + */ + public function read($length) + { + return $this->cache[self::IS_READABLE] ? fread($this->stream, $length) : false; + } + + /** + * Write data to the stream + * + * @param string $string The string that is to be written. + * + * @return int|bool Returns the number of bytes written to the stream on + * success or FALSE on failure. + */ + public function write($string) + { + if (!$this->cache[self::IS_WRITABLE]) { + return 0; + } + + $bytes = fwrite($this->stream, $string); + $this->size += $bytes; + + return $bytes; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/AbstractConstraint.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/AbstractConstraint.php new file mode 100644 index 0000000..86ce3f5 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/AbstractConstraint.php @@ -0,0 +1,46 @@ +default && isset($options[0])) { + // Add the default configuration option if an enumerated array + // is passed + $options[$this->default] = $options[0]; + } + + // Ensure that required options are present + if ($this->required && !isset($options[$this->required])) { + throw new InvalidArgumentException("{$this->required} is a required validation option"); + } + + return $this->validateValue($value, $options); + } + + /** + * Perform the actual validation in a concrete class + * + * @param mixed $value Value to validate + * @param array $options Validation options + * + * @return bool|string + */ + abstract protected function validateValue($value, array $options = array()); +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/AbstractType.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/AbstractType.php new file mode 100644 index 0000000..100c6fa --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/AbstractType.php @@ -0,0 +1,35 @@ +validateConstraint($constraint, $value, $args)) { + return true; + } + } + + return 'Value type must match one of ' . implode(' OR ' , explode(';', $options['constraints'])); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Blank.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Blank.php new file mode 100644 index 0000000..3cf5db9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Blank.php @@ -0,0 +1,21 @@ + 'ctype_alnum', + 'alpha' => 'ctype_alpha', + 'cntrl' => 'ctype_cntrl', + 'digit' => 'ctype_digit', + 'graph' => 'ctype_graph', + 'lower' => 'ctype_lower', + 'print' => 'ctype_print', + 'punct' => 'ctype_punct', + 'space' => 'ctype_space', + 'upper' => 'ctype_upper', + 'xdigit' => 'ctype_xdigit' + ); +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Email.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Email.php new file mode 100644 index 0000000..023aa9e --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Email.php @@ -0,0 +1,25 @@ + 'is_array', + 'long' => 'is_long', + 'bool' => 'is_bool', + 'boolean' => 'is_bool', + 'scalar' => 'is_scalar', + 'string' => 'is_string', + 'nan' => 'is_nan', + 'float' => 'is_float', + 'file' => 'is_file', + 'callable' => 'is_callable', + 'null' => 'is_null', + 'resource' => 'is_resource', + 'numeric' => 'is_numeric', + 'object' => 'is_object' + ); +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Url.php b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Url.php new file mode 100644 index 0000000..230a32a --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Common/Validation/Url.php @@ -0,0 +1,24 @@ +=5.3.2", + "symfony/event-dispatcher": "2.*" + }, + "autoload": { + "psr-0": { "Guzzle\\Common": "" } + }, + "target-dir": "Guzzle/Common" +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/BatchRequestTransfer.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/BatchRequestTransfer.php new file mode 100644 index 0000000..c7d51b7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/BatchRequestTransfer.php @@ -0,0 +1,79 @@ +batchSize = $batchSize; + } + + /** + * Creates batches of requests by grouping requests by their associated + * curl multi object. + * + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + // Create batches by curl multi object groups + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof RequestInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Http\Message\RequestInterface'); + } + $multi = $item->getClient()->getCurlMulti(); + if (!$groups->contains($multi)) { + $groups->attach($multi, new \ArrayObject(array($item))); + } else { + $groups[$multi]->append($item); + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize)); + } + + return $batches; + } + + /** + * {@inheritdoc} + */ + public function transfer(array $batch) + { + if (empty($batch)) { + return; + } + + $multi = reset($batch)->getClient()->getCurlMulti(); + + // Prepare each request for their respective curl multi objects + foreach ($batch as $request) { + $multi->add($request); + } + + $multi->send(); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php new file mode 100644 index 0000000..b3d888a --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Client.php @@ -0,0 +1,530 @@ +setConfig($config ?: new Collection()); + $this->setBaseUrl($baseUrl); + $this->defaultHeaders = new Collection(); + $this->setRequestFactory(RequestFactory::getInstance()); + } + + /** + * Cast to a string + */ + public function __toString() + { + return spl_object_hash($this); + } + + /** + * Set the configuration object to use with the client + * + * @param array|Collection|string $config Parameters that define how the client + * behaves and connects to a webservice. + * Pass an array or a Collection object. + * + * @return Client + */ + public final function setConfig($config) + { + // Set the configuration object + if ($config instanceof Collection) { + $this->config = $config; + } elseif (is_array($config)) { + $this->config = new Collection($config); + } else { + throw new InvalidArgumentException( + 'Config must be an array or Collection' + ); + } + + return $this; + } + + /** + * Get a configuration setting or all of the configuration settings + * + * @param bool|string $key Configuration value to retrieve. Set to FALSE + * to retrieve all values of the client. The + * object return can be modified, and modifications + * will affect the client's config. + * + * @return mixed|Collection + */ + public final function getConfig($key = false) + { + return $key ? $this->config->get($key) : $this->config; + } + + /** + * Get the default HTTP headers to add to each request created by the client + * + * @return Collection + */ + public function getDefaultHeaders() + { + return $this->defaultHeaders; + } + + /** + * Set the default HTTP headers to add to each request created by the client + * + * @param array|Collection $headers Default HTTP headers + * + * @return Client + */ + public function setDefaultHeaders($headers) + { + if ($headers instanceof Collection) { + $this->defaultHeaders = $headers; + } elseif (is_array($headers)) { + $this->defaultHeaders = new Collection($headers); + } else { + throw new InvalidArgumentException('Headers must be an array or Collection'); + } + + return $this; + } + + /** + * Expand a URI template using client configuration data + * + * @param string $template URI template to expand + * @param array $variables Additional variables to use in the expansion + * + * @return string + */ + public function expandTemplate($template, array $variables = null) + { + $expansionVars = $this->getConfig()->getAll(); + if ($variables) { + $expansionVars = array_merge($expansionVars, $variables); + } + + return $this->getUriTemplate()->expand($template, $expansionVars); + } + + /** + * Set the URI template expander to use with the client + * + * @param UriTemplateInterface $uriTemplate URI template expander + * + * @return Client + */ + public function setUriTemplate(UriTemplateInterface $uriTemplate) + { + $this->uriTemplate = $uriTemplate; + + return $this; + } + + /** + * Get the URI template expander used by the client. A default UriTemplate + * object will be created if one does not exist. + * + * @return UriTemplateInterface + */ + public function getUriTemplate() + { + if (!$this->uriTemplate) { + $this->uriTemplate = ParserRegistry::get('uri_template'); + } + + return $this->uriTemplate; + } + + /** + * Create and return a new {@see RequestInterface} configured for the client. + * + * Use an absolute path to override the base path of the client, or a + * relative path to append to the base path of the client. The URI can + * contain the query string as well. Use an array to provide a URI + * template and additional variables to use in the URI template expansion. + * + * @param string $method HTTP method. Defaults to GET + * @param string|array $uri Resource URI. + * @param array|Collection $headers HTTP headers + * @param string|resource|array|EntityBody $body Entity body of request (POST/PUT) or response (GET) + * + * @return RequestInterface + * @throws InvalidArgumentException if a URI array is passed that does not + * contain exactly two elements: the URI + * followed by template variables + */ + public function createRequest($method = RequestInterface::GET, $uri = null, $headers = null, $body = null) + { + if (!is_array($uri)) { + $templateVars = null; + } else { + if (count($uri) != 2 || !is_array($uri[1])) { + throw new InvalidArgumentException( + 'You must provide a URI template followed by an array of template variables ' + . 'when using an array for a URI template' + ); + } + list($uri, $templateVars) = $uri; + } + + if (!$uri) { + $url = $this->getBaseUrl(); + } elseif (strpos($uri, 'http') === 0) { + // Use absolute URLs as-is + $url = $this->expandTemplate($uri, $templateVars); + } else { + $url = Url::factory($this->getBaseUrl())->combine($this->expandTemplate($uri, $templateVars)); + } + + // If default headers are provided, then merge them into existing headers + // If a collision occurs, the header is completely replaced + if (count($this->defaultHeaders)) { + if ($headers instanceof Collection) { + $headers = array_merge($this->defaultHeaders->getAll(), $headers->getAll()); + } elseif (is_array($headers)) { + $headers = array_merge($this->defaultHeaders->getAll(), $headers); + } elseif ($headers === null) { + $headers = $this->defaultHeaders; + } + } + + return $this->prepareRequest( + $this->requestFactory->create($method, (string) $url, $headers, $body) + ); + } + + /** + * Prepare a request to be sent from the Client by adding client specific + * behaviors and properties to the request. + * + * @param RequestInterface $request Request to prepare for the client + * + * @return RequestInterface + */ + protected function prepareRequest(RequestInterface $request) + { + $request->setClient($this); + + foreach ($this->getConfig()->getAll() as $key => $value) { + if ($key == 'curl.blacklist') { + continue; + } + // Add any curl options that might in the config to the request + if (strpos($key, 'curl.') === 0) { + $curlOption = substr($key, 5); + // Convert constants represented as string to constant int values + if (defined($curlOption)) { + $value = is_string($value) && defined($value) ? constant($value) : $value; + $curlOption = constant($curlOption); + } + $request->getCurlOptions()->set($curlOption, $value); + } elseif (strpos($key, 'params.') === 0) { + // Add request specific parameters to all requests (prefix with 'params.') + $request->getParams()->set(substr($key, 7), $value); + } + } + + // Attach client observers to the request + $request->setEventDispatcher(clone $this->getEventDispatcher()); + + $this->dispatch('client.create_request', array( + 'client' => $this, + 'request' => $request + )); + + return $request; + } + + /** + * Get the client's base URL as either an expanded or raw URI template + * + * @param bool $expand Set to FALSE to get the raw base URL without URI + * template expansion + * + * @return string|null + */ + public function getBaseUrl($expand = true) + { + return $expand ? $this->expandTemplate($this->baseUrl) : $this->baseUrl; + } + + /** + * Set the base URL of the client + * + * @param string $url The base service endpoint URL of the webservice + * + * @return Client + */ + public function setBaseUrl($url) + { + $this->baseUrl = $url; + + return $this; + } + + /** + * Set the name of your application and application version that will be + * appended to the User-Agent header of all requests. + * + * @param string $userAgent User agent string + * @param bool $includeDefault Set to TRUE to append the default Guzzle use agent + * + * @return Client + */ + public function setUserAgent($userAgent, $includeDefault = false) + { + if ($includeDefault) { + $userAgent .= ' ' . Utils::getDefaultUserAgent(); + } + $this->defaultHeaders->set('User-Agent', $userAgent); + + return $this; + } + + /** + * Create a GET request for the client + * + * @param string|array $uri Resource URI + * @param array|Collection $headers HTTP headers + * @param string|resource|array|EntityBody $body Where to store the response entity body + * + * @return Request + * @see Guzzle\Http\Client::createRequest() + */ + public function get($uri = null, $headers = null, $body = null) + { + return $this->createRequest('GET', $uri, $headers, $body); + } + + /** + * Create a HEAD request for the client + * + * @param string|array $uri Resource URI + * @param array|Collection $headers HTTP headers + * + * @return Request + * @see Guzzle\Http\Client::createRequest() + */ + public function head($uri = null, $headers = null) + { + return $this->createRequest('HEAD', $uri, $headers); + } + + /** + * Create a DELETE request for the client + * + * @param string|array $uri Resource URI + * @param array|Collection $headers HTTP headers + * + * @return Request + * @see Guzzle\Http\Client::createRequest() + */ + public function delete($uri = null, $headers = null) + { + return $this->createRequest('DELETE', $uri, $headers); + } + + /** + * Create a PUT request for the client + * + * @param string|array $uri Resource URI + * @param array|Collection $headers HTTP headers + * @param string|resource|EntityBody $body Body to send in the request + * + * @return EntityEnclosingRequest + * @see Guzzle\Http\Client::createRequest() + */ + public function put($uri = null, $headers = null, $body = null) + { + return $this->createRequest('PUT', $uri, $headers, $body); + } + + /** + * Create a PATCH request for the client + * + * @param string|array $uri Resource URI + * @param array|Collection $headers HTTP headers + * @param string|resource|EntityBody $body Body to send in the request + * + * @return EntityEnclosingRequest + * @see Guzzle\Http\Client::createRequest() + */ + public function patch($uri = null, $headers = null, $body = null) + { + return $this->createRequest('PATCH', $uri, $headers, $body); + } + + /** + * Create a POST request for the client + * + * @param string|array $uri Resource URI + * @param array|Collection $headers HTTP headers + * @param array|Collection|string|EntityBody $postBody POST body. Can be a string, EntityBody, + * or associative array of POST fields to + * send in the body of the request. Prefix + * a value in the array with the @ symbol + * reference a file. + * + * @return EntityEnclosingRequest + * @see Guzzle\Http\Client::createRequest() + */ + public function post($uri = null, $headers = null, $postBody = null) + { + return $this->createRequest('POST', $uri, $headers, $postBody); + } + + /** + * Create an OPTIONS request for the client + * + * @param string|array $uri Resource URI + * + * @return Request + * @see Guzzle\Http\Client::createRequest() + */ + public function options($uri = null) + { + return $this->createRequest('OPTIONS', $uri); + } + + /** + * Sends a single request or an array of requests in parallel + * + * @param array $requests Request(s) to send + * + * @return array Returns the response(s) + */ + public function send($requests) + { + $curlMulti = $this->getCurlMulti(); + $multipleRequests = !($requests instanceof RequestInterface); + if (!$multipleRequests) { + $requests = array($requests); + } + + foreach ($requests as $request) { + $curlMulti->add($request); + } + + try { + $curlMulti->send(); + } catch (ExceptionCollection $e) { + throw $multipleRequests ? $e : $e->getIterator()->offsetGet(0); + } + + if (!$multipleRequests) { + return end($requests)->getResponse(); + } + + return array_map(function($request) { + return $request->getResponse(); + }, $requests); + } + + /** + * Set a curl multi object to be used internally by the client for + * transferring requests. + * + * @param CurlMultiInterface $curlMulti multi object + * + * @return Client + */ + public function setCurlMulti(CurlMultiInterface $curlMulti) + { + $this->curlMulti = $curlMulti; + + return $this; + } + + /** + * Get the curl multi object used with the client + * + * @return CurlMultiInterface + */ + public function getCurlMulti() + { + if (!$this->curlMulti) { + $this->curlMulti = CurlMulti::getInstance(); + } + + return $this->curlMulti; + } + + /** + * Set the request factory to use with the client when creating requests + * + * @param RequestFactoryInterface $factory Request factory + * + * @return Client + */ + public function setRequestFactory(RequestFactoryInterface $factory) + { + $this->requestFactory = $factory; + + return $this; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php new file mode 100644 index 0000000..ff95e04 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/ClientInterface.php @@ -0,0 +1,251 @@ + '', + 'value' => '', + 'domain' => '', + 'path' => '/', + 'expires' => null, + 'max_age' => 0, + 'comment' => null, + 'comment_url' => null, + 'port' => array(), + 'version' => null, + 'secure' => false, + 'discard' => false, + 'http_only' => false + ); + + $this->data = array_merge($defaults, $data); + // Extract the expires value and turn it into a UNIX timestamp if needed + if (!$this->getExpires() && $this->getMaxAge()) { + // Calculate the expires date + $this->setExpires(time() + (int) $this->getMaxAge()); + } elseif ($this->getExpires() && !is_numeric($this->getExpires())) { + $this->setExpires(strtotime($this->getExpires())); + } + } + + /** + * Get the cookie as an array + * + * @return array + */ + public function toArray() + { + return $this->data; + } + + /** + * Get the cookie name + * + * @return string + */ + public function getName() + { + return $this->data['name']; + } + + /** + * Set the cookie name + * + * @param string $name Cookie name + * + * @return Cookie + */ + public function setName($name) + { + return $this->setData('name', $name); + } + + /** + * Get the cookie value + * + * @return string + */ + public function getValue() + { + return $this->data['value']; + } + + /** + * Set the cookie value + * + * @param string $value Cookie value + * + * @return Cookie + */ + public function setValue($value) + { + return $this->setData('value', $value); + } + + /** + * Get the domain + * + * @return string|null + */ + public function getDomain() + { + return $this->data['domain']; + } + + /** + * Set the domain of the cookie + * + * @param string $domain + * + * @return Cookie + */ + public function setDomain($domain) + { + return $this->setData('domain', $domain); + } + + /** + * Get the path + * + * @return string + */ + public function getPath() + { + return $this->data['path']; + } + + /** + * Set the path of the cookie + * + * @param string $path Path of the cookie + * + * @return Cookie + */ + public function setPath($path) + { + return $this->setData('path', $path); + } + + /** + * Maximum lifetime of the cookie in seconds + * + * @return int|null + */ + public function getMaxAge() + { + return $this->data['max_age']; + } + + /** + * Set the max-age of the cookie + * + * @param int $maxAge Max age of the cookie in seconds + * + * @return Cookie + */ + public function setMaxAge($maxAge) + { + return $this->setData('max_age', $maxAge); + } + + /** + * The UNIX timestamp when the cookie expires + * + * @return mixed + */ + public function getExpires() + { + return $this->data['expires']; + } + + /** + * Set the unix timestamp for which the cookie will expire + * + * @param int $timestamp Unix timestamp + * + * @return Cookie + */ + public function setExpires($timestamp) + { + return $this->setData('expires', $timestamp); + } + + /** + * Version of the cookie specification. RFC 2965 is 1 + * + * @return mixed + */ + public function getVersion() + { + return $this->data['version']; + } + + /** + * Set the cookie version + * + * @param string|int $version Version to set + * + * @return Cookie + */ + public function setVersion($version) + { + return $this->setData('version', $version); + } + + /** + * Get whether or not this is a secure cookie + * + * @return null|bool + */ + public function getSecure() + { + return $this->data['secure']; + } + + /** + * Set whether or not the cookie is secure + * + * @param bool $secure Set to true or false if secure + * + * @return Cookie + */ + public function setSecure($secure) + { + return $this->setData('secure', (bool) $secure); + } + + /** + * Get whether or not this is a session cookie + * + * @return null|bool + */ + public function getDiscard() + { + return $this->data['discard']; + } + + /** + * Set whether or not this is a session cookie + * + * @param bool $discard Set to true or false if this is a session cookie + * + * @return Cookie + */ + public function setDiscard($discard) + { + return $this->setData('discard', $discard); + } + + /** + * Get the comment + * + * @return string|null + */ + public function getComment() + { + return $this->data['comment']; + } + + /** + * Set the comment of the cookie + * + * @param string $comment Cookie comment + * + * @return Cookie + */ + public function setComment($comment) + { + return $this->setData('comment', $comment); + } + + /** + * Get the comment URL of the cookie + * + * @return string|null + */ + public function getCommentUrl() + { + return $this->data['comment_url']; + } + + /** + * Set the comment URL of the cookie + * + * @param string $commentUrl Cookie comment URL for more information + * + * @return Cookie + */ + public function setCommentUrl($commentUrl) + { + return $this->setData('comment_url', $commentUrl); + } + + /** + * Get an array of acceptable ports this cookie can be used with + * + * @return array + */ + public function getPorts() + { + return $this->data['port']; + } + + /** + * Set a list of acceptable ports this cookie can be used with + * + * @param array $ports Array of acceptable ports + * + * @return Cookie + */ + public function setPorts(array $ports) + { + return $this->setData('port', $ports); + } + + /** + * Get whether or not this is an HTTP only cookie + * + * @return bool + */ + public function getHttpOnly() + { + return $this->data['http_only']; + } + + /** + * Set whether or not this is an HTTP only cookie + * + * @param bool $httpOnly Set to true or false if this is HTTP only + * + * @return Cookie + */ + public function setHttpOnly($httpOnly) + { + return $this->setData('http_only', $httpOnly); + } + + /** + * Get an array of extra cookie data + * + * @return array + */ + public function getAttributes() + { + return $this->data['data']; + } + + /** + * Get a specific data point from the extra cookie data + * + * @param $name Name of the data point to retrieve + * + * @return null|string + */ + public function getAttribute($name) + { + return array_key_exists($name, $this->data['data']) ? $this->data['data'][$name] : null; + } + + /** + * Set a cookie data attribute + * + * @param string $name Name of the attribute to set + * @param string $value Value to set + * + * @return Cookie + */ + public function setAttribute($name, $value) + { + $this->data['data'][$name] = $value; + + return $this; + } + + /** + * Check if the cookie matches a path value + * + * @param string $path Path to check against + * + * @return bool + */ + public function matchesPath($path) + { + return !$this->getPath() || 0 === stripos($path, $this->getPath()); + } + + /** + * Check if the cookie matches a domain value + * + * @param string $domain Domain to check against + * + * @return bool + */ + public function matchesDomain($domain) + { + return !$this->getDomain() || !strcasecmp($domain, $this->getDomain()) || + (strpos($this->getDomain(), '.') === 0 && preg_match('/' . preg_quote($this->getDomain()) . '$/i', $domain)); + } + + /** + * Check if the cookie is compatible with a specific port + * + * @param int $port Port to check + * + * @return bool + */ + public function matchesPort($port) + { + return count($this->getPorts()) == 0 || in_array($port, $this->getPorts()); + } + + /** + * Check if the cookie is expired + * + * @return bool + */ + public function isExpired() + { + return $this->getExpires() && time() > $this->getExpires(); + } + + /** + * Set a value and return the cookie object + * + * @param string $key Key to set + * @param string $value Value to set + * + * @return Cookie + */ + private function setData($key, $value) + { + $this->data[$key] = $value; + + return $this; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/CookieJar/ArrayCookieJar.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/CookieJar/ArrayCookieJar.php new file mode 100644 index 0000000..e9eb1f8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/CookieJar/ArrayCookieJar.php @@ -0,0 +1,222 @@ +all($domain, $path, $name, false, false); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($cookies) { + return !in_array($cookie, $cookies, true); + }); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function removeTemporary() + { + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) { + return !$cookie->getDiscard() && $cookie->getExpires(); + }); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function removeExpired() + { + $currentTime = time(); + $this->cookies = array_filter($this->cookies, function (Cookie $cookie) use ($currentTime) { + return !$cookie->getExpires() || $currentTime < $cookie->getExpires(); + }); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function all($domain = null, $path = null, $name = null, $skipDiscardable = false, $skipExpired = true) + { + return array_values(array_filter($this->cookies, function (Cookie $cookie) use ( + $domain, + $path, + $name, + $skipDiscardable, + $skipExpired + ) { + return false === (($name && $cookie->getName() != $name) || + ($skipExpired && $cookie->isExpired()) || + ($skipDiscardable && ($cookie->getDiscard() || !$cookie->getExpires())) || + ($path && !$cookie->matchesPath($path)) || + ($domain && !$cookie->matchesDomain($domain))); + })); + } + + /** + * {@inheritdoc} + */ + public function add(Cookie $cookie) + { + if (!$cookie->getValue() || !$cookie->getName() || !$cookie->getDomain()) { + return false; + } + + // Resolve conflicts with previously set cookies + foreach ($this->cookies as $i => $c) { + + // Check the regular comparison fields + if ($c->getPath() != $cookie->getPath() || $c->getMaxAge() != $cookie->getMaxAge() || + $c->getDomain() != $cookie->getDomain() || $c->getHttpOnly() != $cookie->getHttpOnly() || + $c->getPorts() != $cookie->getPorts() || $c->getSecure() != $cookie->getSecure() || + $c->getName() != $cookie->getName() + ) { + continue; + } + + // The previously set cookie is a discard cookie and this one is not + // so allow the new cookie to be set + if (!$cookie->getDiscard() && $c->getDiscard()) { + unset($this->cookies[$i]); + continue; + } + + // If the new cookie's expiration is further into the future, then + // replace the old cookie + if ($cookie->getExpires() > $c->getExpires()) { + unset($this->cookies[$i]); + continue; + } + + // If the value has changed, we better change it + if ($cookie->getValue() !== $c->getValue()) { + unset($this->cookies[$i]); + continue; + } + + // The cookie exists, so no need to continue + return false; + } + + $this->cookies[] = $cookie; + + return true; + } + + /** + * Serializes the cookie cookieJar + * + * @return string + */ + public function serialize() + { + // Only serialize long term cookies and unexpired cookies + return json_encode(array_map(function (Cookie $cookie) { + return $cookie->toArray(); + }, $this->all(null, null, null, true, true))); + } + + /** + * Unserializes the cookie cookieJar + */ + public function unserialize($data) + { + $data = json_decode($data, true); + if (empty($data)) { + $this->cookies = array(); + } else { + $this->cookies = array_map(function (array $cookie) { + return new Cookie($cookie); + }, $data); + } + } + + /** + * Returns the total number of stored cookies + * + * @return int + */ + public function count() + { + return count($this->cookies); + } + + /** + * Returns an iterator + * + * @return ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->cookies); + } + + /** + * {@inheritdoc} + */ + public function addCookiesFromResponse(Response $response) + { + if ($cookieHeader = $response->getSetCookie()) { + + $request = $response->getRequest(); + $parser = ParserRegistry::get('cookie'); + + foreach ($cookieHeader as $cookie) { + + $parsed = $request + ? $parser->parseCookie($cookie, $request->getHost(), $request->getPath()) + : $parser->parseCookie($cookie); + + if ($parsed) { + // Break up cookie v2 into multiple cookies + foreach ($parsed['cookies'] as $key => $value) { + $row = $parsed; + $row['name'] = $key; + $row['value'] = $value; + unset($row['cookies']); + $this->add(new Cookie($row)); + } + } + } + } + } + + /** + * {@inheritdoc} + */ + public function getMatchingCookies(RequestInterface $request) + { + // Find cookies that match this request + $cookies = $this->all($request->getHost(), $request->getPath()); + // Remove ineligible cookies + foreach ($cookies as $index => $cookie) { + if (!$cookie->matchesPort($request->getPort()) || ($cookie->getSecure() && $request->getScheme() != 'https')) { + unset($cookies[$index]); + } + }; + + return $cookies; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/CookieJar/CookieJarInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/CookieJar/CookieJarInterface.php new file mode 100644 index 0000000..cc752de --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/CookieJar/CookieJarInterface.php @@ -0,0 +1,87 @@ +filename = $cookieFile; + $this->load(); + } + + /** + * Saves the file when shutting down + */ + public function __destruct() + { + $this->persist(); + } + + /** + * Save the contents of the data array to the file + * + * @throws RuntimeException if the file cannot be found or created + */ + protected function persist() + { + if (false === file_put_contents($this->filename, $this->serialize())) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + } + + /** + * Load the contents of the json formatted file into the data array and + * discard the unsaved state of the cookieJar object + */ + protected function load() + { + $json = file_get_contents($this->filename); + if (false === $json) { + // @codeCoverageIgnoreStart + throw new RuntimeException('Unable to open file ' . $this->filename); + // @codeCoverageIgnoreEnd + } + + $this->unserialize($json); + $this->cookies = $this->cookies ?: array(); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php new file mode 100644 index 0000000..1cb4dca --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlHandle.php @@ -0,0 +1,439 @@ +getCurlOptions(); + $tempHeaders = array(); + + // Array of default cURL options. + $curlOptions = array( + CURLOPT_URL => $request->getUrl(), + CURLOPT_CUSTOMREQUEST => $request->getMethod(), + CURLOPT_CONNECTTIMEOUT => 10, // Connect timeout in seconds + CURLOPT_RETURNTRANSFER => false, // Streaming the return, so no need + CURLOPT_HEADER => false, // Retrieve the received headers + CURLOPT_USERAGENT => (string) $request->getHeader('User-Agent'), + CURLOPT_ENCODING => '', // Supports all encodings + CURLOPT_PORT => $request->getPort(), + CURLOPT_HTTP_VERSION => $request->getProtocolVersion() === '1.0' ? CURL_HTTP_VERSION_1_0 : CURL_HTTP_VERSION_1_1, + CURLOPT_HTTPHEADER => array(), + CURLOPT_HEADERFUNCTION => array($mediator, 'receiveResponseHeader') + ); + + // Enable the progress function if the 'progress' param was set + if ($requestCurlOptions->get('progress')) { + $curlOptions[CURLOPT_PROGRESSFUNCTION] = array($mediator, 'progress'); + $curlOptions[CURLOPT_NOPROGRESS] = false; + } + + // Enable curl debug information if the 'debug' param was set + if ($requestCurlOptions->get('debug')) { + $curlOptions[CURLOPT_STDERR] = fopen('php://temp', 'r+'); + // @codeCoverageIgnoreStart + if (false === $curlOptions[CURLOPT_STDERR]) { + throw new RuntimeException('Unable to create a stream for CURLOPT_STDERR'); + } + // @codeCoverageIgnoreEnd + $curlOptions[CURLOPT_VERBOSE] = true; + } + + // HEAD requests need no response body, everything else might + if ($request->getMethod() != 'HEAD') { + $curlOptions[CURLOPT_WRITEFUNCTION] = array($mediator, 'writeResponseBody'); + } + + // Account for PHP installations with safe_mode or open_basedir enabled + // @codeCoverageIgnoreStart + if (CurlVersion::getInstance()->get('follow_location')) { + $curlOptions[CURLOPT_FOLLOWLOCATION] = true; + $curlOptions[CURLOPT_MAXREDIRS] = 5; + } + // @codeCoverageIgnoreEnd + + // Specify settings according to the HTTP method + switch ($request->getMethod()) { + case 'GET': + $curlOptions[CURLOPT_HTTPGET] = true; + break; + case 'HEAD': + $curlOptions[CURLOPT_NOBODY] = true; + break; + case 'POST': + $curlOptions[CURLOPT_POST] = true; + + // Special handling for POST specific fields and files + if (count($request->getPostFiles())) { + + $fields = $request->getPostFields()->urlEncode(); + foreach ($request->getPostFiles() as $key => $data) { + $prefixKeys = count($data) > 1; + foreach ($data as $index => $file) { + // Allow multiple files in the same key + $fieldKey = $prefixKeys ? "{$key}[{$index}]" : $key; + $fields[$fieldKey] = $file->getCurlString(); + } + } + + $curlOptions[CURLOPT_POSTFIELDS] = $fields; + $request->removeHeader('Content-Length'); + + } elseif (count($request->getPostFields())) { + $curlOptions[CURLOPT_POSTFIELDS] = (string) $request->getPostFields(); + $request->removeHeader('Content-Length'); + } + break; + case 'PUT': + case 'PATCH': + $curlOptions[CURLOPT_UPLOAD] = true; + // Let cURL handle setting the Content-Length header + $contentLength = $request->getHeader('Content-Length'); + if ($contentLength != null) { + $contentLength = (int) (string) $contentLength; + $curlOptions[CURLOPT_INFILESIZE] = $contentLength; + $tempHeaders['Content-Length'] = $contentLength; + $request->removeHeader('Content-Length'); + } + break; + } + + // Special handling for requests sending raw data + if ($request instanceof EntityEnclosingRequestInterface) { + + // Don't modify POST requests using POST fields and files via cURL + if (!isset($curlOptions[CURLOPT_POSTFIELDS])) { + if ($request->getBody()) { + // Add a callback for curl to read data to send with the request + // only if a body was specified + $curlOptions[CURLOPT_READFUNCTION] = array($mediator, 'readRequestBody'); + } else { + // If no body is being sent, always send Content-Length of 0 + $request->setHeader('Content-Length', 0); + $request->removeHeader('Transfer-Encoding'); + // Need to remove CURLOPT_UPLOAD to prevent chunked encoding + unset($curlOptions[CURLOPT_UPLOAD]); + unset($curlOptions[CURLOPT_POST]); + } + } + + // If the Expect header is not present, prevent curl from adding it + if (!$request->hasHeader('Expect')) { + $curlOptions[CURLOPT_HTTPHEADER][] = 'Expect:'; + } + } + + // Set custom cURL options + foreach ($requestCurlOptions as $key => $value) { + if (is_numeric($key)) { + $curlOptions[$key] = $value; + } + } + + // Check if any headers or cURL options are blacklisted + $client = $request->getClient(); + if ($client) { + $blacklist = $client->getConfig('curl.blacklist'); + if ($blacklist) { + foreach ($blacklist as $value) { + if (strpos($value, 'header.') === 0) { + // Remove headers that may have previously been set + // but are supposed to be blacklisted + $key = substr($value, 7); + $request->removeHeader($key); + $curlOptions[CURLOPT_HTTPHEADER][] = $key . ':'; + } else { + unset($curlOptions[$value]); + } + } + } + } + + // Add any custom headers to the request. Empty headers will cause curl to + // not send the header at all. + foreach ($request->getHeaderLines() as $line) { + $curlOptions[CURLOPT_HTTPHEADER][] = $line; + } + + // Apply the options to a new cURL handle. + $handle = curl_init(); + curl_setopt_array($handle, $curlOptions); + $request->getParams()->set('curl.last_options', $curlOptions); + + // Some fields need to be removed from the request in order to properly + // send a cURL request message. The fields that were removed for this + // purpose (e.g. Content-Length) should be aggregated in this array and + // added back to the request. Does not apply to blacklisted headers. + foreach ($tempHeaders as $key => $value) { + $request->setHeader($key, $value); + } + + $handle = new static($handle, $curlOptions); + $mediator->setCurlHandle($handle); + + return $handle; + } + + /** + * Construct a new CurlHandle object that wraps a cURL handle + * + * @param resource $handle Configured cURL handle resource + * @param Collection|array $options Curl options to use with the handle + * + * @throws InvalidArgumentException + */ + public function __construct($handle, $options) + { + if (!is_resource($handle)) { + throw new InvalidArgumentException('Invalid handle provided'); + } + if (is_array($options)) { + $this->options = new Collection($options); + } elseif ($options instanceof Collection) { + $this->options = $options; + } else { + throw new InvalidArgumentException('Expected array or Collection'); + } + $this->handle = $handle; + } + + /** + * Destructor + */ + public function __destruct() + { + $this->close(); + } + + /** + * Close the curl handle + */ + public function close() + { + if (is_resource($this->handle)) { + curl_close($this->handle); + } + $this->handle = null; + } + + /** + * Check if the handle is available and still OK + * + * @return bool + */ + public function isAvailable() + { + return is_resource($this->handle); + } + + /** + * Get the last error that occurred on the cURL handle + * + * @return string + */ + public function getError() + { + return $this->isAvailable() ? curl_error($this->handle) : ''; + } + + /** + * Get the last error number that occurred on the cURL handle + * + * @return int + */ + public function getErrorNo() + { + if ($this->errorNo) { + return $this->errorNo; + } + + return $this->isAvailable() ? curl_errno($this->handle) : 0; + } + + /** + * Set the curl error number + * + * @param int $error Error number to set + * + * @return CurlHandle + */ + public function setErrorNo($error) + { + $this->errorNo = $error; + + return $this; + } + + /** + * Get cURL curl_getinfo data + * + * @param int $option Option to retrieve. Pass null to retrieve + * retrieve all data as an array or pass a CURLINFO_* + * constant + * + * @return array|mixed + */ + public function getInfo($option = null) + { + if (!is_resource($this->handle)) { + return null; + } + + if (null !== $option) { + return curl_getinfo($this->handle, $option) ?: null; + } + + return curl_getinfo($this->handle) ?: array(); + } + + /** + * Get the stderr output + * + * @param bool $asResource Set to TRUE to get an fopen resource + * + * @return string|resource|null + */ + public function getStderr($asResource = false) + { + $stderr = $this->getOptions()->get(CURLOPT_STDERR); + if (!$stderr) { + return null; + } + + if ($asResource) { + return $stderr; + } + + fseek($stderr, 0); + $e = stream_get_contents($stderr); + fseek($stderr, 0, SEEK_END); + + return $e; + } + + /** + * Get the URL that this handle is connecting to + * + * @return Url + */ + public function getUrl() + { + return Url::factory($this->options->get(CURLOPT_URL)); + } + + /** + * Get the wrapped curl handle + * + * @return handle|null Returns the cURL handle or null if it was closed + */ + public function getHandle() + { + return $this->isAvailable() ? $this->handle : null; + } + + /** + * Get the cURL setopt options of the handle. Changing values in the return + * object will have no effect on the curl handle after it is created. + * + * @return Collection + */ + public function getOptions() + { + return $this->options; + } + + /** + * Update a request based on the log messages of the CurlHandle + * + * @param RequestInterface $request Request to update + */ + public function updateRequestFromTransfer(RequestInterface $request) + { + $log = $this->getStderr(true); + + if (!$log || !$request->getResponse()) { + return; + } + + // Update the transfer stats of the response + $request->getResponse()->setInfo($this->getInfo()); + + // Parse the cURL stderr output for outgoing requests + $headers = ''; + fseek($log, 0); + while (($line = fgets($log)) !== false) { + if ($line && $line[0] == '>') { + $headers = substr(trim($line), 2) . "\r\n"; + while (($line = fgets($log)) !== false) { + if ($line[0] == '*' || $line[0] == '<') { + break; + } else { + $headers .= trim($line) . "\r\n"; + } + } + } + } + + // Add request headers to the request exactly as they were sent + if ($headers) { + $parsed = ParserRegistry::get('message')->parseRequest($headers); + if (!empty($parsed['headers'])) { + $request->setHeaders(array()); + foreach ($parsed['headers'] as $name => $value) { + $request->setHeader($name, $value); + } + } + if (!empty($parsed['version'])) { + $request->setProtocolVersion($parsed['version']); + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php new file mode 100644 index 0000000..3fcee59 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMulti.php @@ -0,0 +1,647 @@ + array('CURLM_BAD_HANDLE', 'The passed-in handle is not a valid CURLM handle.'), + CURLM_BAD_EASY_HANDLE => array('CURLM_BAD_EASY_HANDLE', "An easy handle was not good/valid. It could mean that it isn't an easy handle at all, or possibly that the handle already is in used by this or another multi handle."), + CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), + CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!') + ); + + /** + * @var CurlMulti + */ + private static $instance; + + /** + * @var int + */ + private $scope = -1; + + /** + * Get a cached instance of the curl mutli object + * + * @return CurlMulti + */ + public static function getInstance() + { + // @codeCoverageIgnoreStart + if (!self::$instance) { + self::$instance = new self(); + } + // @codeCoverageIgnoreEnd + + return self::$instance; + } + + /** + * {@inheritdoc} + */ + public static function getAllEvents() + { + return array( + // A request was added + self::ADD_REQUEST, + // A request was removed + self::REMOVE_REQUEST, + // Requests are about to be sent + self::BEFORE_SEND, + // The pool finished sending the requests + self::COMPLETE, + // A request is still polling (sent to request's event dispatchers) + self::POLLING_REQUEST, + // A request exception occurred + 'curl_multi.exception' + ); + } + + /** + * {@inheritdoc} + */ + public function __construct() + { + // You can get some weird "Too many open files" errors when sending + // a large amount of requests in parallel. These two statements + // autoload classes before a system runs out of file descriptors so + // that you can get back valuable error messages if you run out. + class_exists('Guzzle\Http\Message\Response'); + class_exists('Guzzle\Http\Exception\CurlException'); + + $this->createMutliHandle(); + } + + /** + * {@inheritdoc} + */ + public function __destruct() + { + $this->requestCache = null; + if (is_resource($this->multiHandle)) { + curl_multi_close($this->multiHandle); + } + } + + /** + * {@inheritdoc} + * + * Adds a request to a batch of requests to be sent in parallel. + * + * Async requests adds a request to the current scope to be executed in + * parallel with any currently executing cURL handles. You may only add an + * async request while other requests are transferring. Attempting to add + * an async request while no requests are transferring will add the request + * normally in the next available scope (typically 0). + * + * @param RequestInterface $request Request to add + * @param bool $async Set to TRUE to add to the current scope + * + * @return CurlMutli + */ + public function add(RequestInterface $request, $async = false) + { + if ($async && $this->state != self::STATE_SENDING) { + $async = false; + } + + $this->requestCache = null; + $scope = $async ? $this->scope : $this->scope + 1; + + if (!isset($this->requests[$scope])) { + $this->requests[$scope] = array(); + } + $this->requests[$scope][] = $request; + $this->dispatch(self::ADD_REQUEST, array( + 'request' => $request + )); + + // If requests are currently transferring and this is async, then the + // request must be prepared now as the send() method is not called. + if ($this->state == self::STATE_SENDING && $async) { + $this->beforeSend($request); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function all() + { + if (!$this->requestCache) { + $this->requestCache = array(); + foreach ($this->requests as &$scopedRequests) { + foreach ($scopedRequests as $request) { + $this->requestCache[] = $request; + } + } + } + + return $this->requestCache; + } + + /** + * {@inheritdoc} + */ + public function getState() + { + return $this->state; + } + + /** + * {@inheritdoc} + */ + public function remove(RequestInterface $request) + { + $this->removeHandle($request); + + $this->requestCache = null; + foreach ($this->requests as $scope => $scopedRequests) { + foreach ($scopedRequests as $i => $scopedRequest) { + if ($scopedRequest === $request) { + unset($this->requests[$scope][$i]); + break; + } + } + } + + $this->dispatch(self::REMOVE_REQUEST, array( + 'request' => $request + )); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function reset($hard = false) + { + // Remove each request + foreach ($this->all() as $request) { + $this->remove($request); + } + + $this->requests = array(); + $this->exceptions = array(); + $this->state = self::STATE_IDLE; + $this->scope = -1; + $this->requestCache = null; + + // Remove any curl handles that were queued for removal + if ($this->scope == -1 || $hard) { + foreach ($this->removeHandles as $handle) { + curl_multi_remove_handle($this->multiHandle, $handle->getHandle()); + $handle->close(); + } + $this->removeHandles = array(); + } + + if ($hard) { + $this->createMutliHandle(); + } + } + + /** + * {@inheritdoc} + */ + public function send() + { + $this->scope++; + $this->state = self::STATE_SENDING; + + // Only prepare and send requests that are in the current recursion scope + // Only enter the main perform() loop if there are requests in scope + if (!empty($this->requests[$this->scope])) { + + // Any exceptions thrown from this event should break the entire + // flow of sending requests in parallel to prevent weird errors + $this->dispatch(self::BEFORE_SEND, array( + 'requests' => $this->requests[$this->scope] + )); + + foreach ($this->requests[$this->scope] as $request) { + if ($request->getState() != RequestInterface::STATE_TRANSFER) { + $this->beforeSend($request); + } + } + + try { + $this->perform(); + } catch (\Exception $e) { + $this->exceptions[] = $e; + } + } + + $this->scope--; + + // Aggregate exceptions into an ExceptionCollection + $exceptionCollection = null; + if (!empty($this->exceptions)) { + $exceptionCollection = new ExceptionCollection('Errors during multi transfer'); + while ($e = array_shift($this->exceptions)) { + $exceptionCollection->add($e); + } + } + + // Complete the transfer if this is the bottom scope and the state + // of the curl multi handle is not already complete + if ($this->scope == -1) { + $this->state = self::STATE_COMPLETE; + $this->dispatch(self::COMPLETE); + $this->reset(); + } + + // Throw any exceptions that were encountered + if ($exceptionCollection) { + throw $exceptionCollection; + } + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->all()); + } + + /** + * Prepare for sending + * + * @param RequestInterface $request Request to prepare + */ + protected function beforeSend(RequestInterface $request) + { + try { + $request->setState(RequestInterface::STATE_TRANSFER); + $request->dispatch('request.before_send', array( + 'request' => $request + )); + if ($request->getState() != RequestInterface::STATE_TRANSFER) { + // Requests might decide they don't need to be sent just before + // transfer (e.g. CachePlugin) + $this->remove($request); + } elseif ($request->getParams()->get('queued_response')) { + // Queued responses do not need to be sent using curl + $this->remove($request); + $request->setState(RequestInterface::STATE_COMPLETE); + } else { + // Add the request's curl handle to the multi handle + $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $this->createCurlHandle($request)->getHandle())); + } + } catch (\Exception $e) { + $this->removeErroredRequest($request, $e); + } + } + + /** + * Create a curl handle for a request + * + * @param RequestInterface $request Request + * + * @return CurlHandle + */ + protected function createCurlHandle(RequestInterface $request) + { + $wrapper = CurlHandle::factory($request); + $this->handles[spl_object_hash($request)] = $wrapper; + $request->getParams()->set('curl_handle', $wrapper); + + return $wrapper; + } + + /** + * Get the data from the multi handle + */ + protected function perform() + { + // @codeCoverageIgnoreStart + // Weird things can happen when making HTTP requests in __destruct methods + if (!$this->multiHandle) { + return; + } + // @codeCoverageIgnoreEnd + + // If there are no requests to send, then exit from the function + if ($this->scope <= 0) { + if ($this->count() == 0) { + return; + } + } elseif (empty($this->requests[$this->scope])) { + return; + } + + // Create the polling event external to the loop + $event = array('curl_multi' => $this); + + while (1) { + + $active = $this->executeHandles(); + + // Get messages from curl handles + while ($done = curl_multi_info_read($this->multiHandle)) { + foreach ($this->all() as $request) { + $handle = $this->getRequestHandle($request); + if ($handle && $handle->getHandle() === $done['handle']) { + try { + $this->processResponse($request, $handle, $done); + } catch (\Exception $e) { + $this->removeErroredRequest($request, $e); + } + break; + } + } + } + + // Notify each request as polling and handled queued responses + if ($this->scope <= 0) { + $scopedPolling = $this->all(); + } else { + $scopedPolling = $this->requests[$this->scope]; + } + + // Exit the function if there are no more requests to send + if (empty($scopedPolling)) { + break; + } + + // Notify all requests that requests are being polled + foreach ($scopedPolling as $request) { + $event['request'] = $request; + $request->dispatch(self::POLLING_REQUEST, $event); + } + + if ($active) { + + // Select the curl handles until there is any activity on any + // of the open file descriptors. + // See https://github.com/php/php-src/blob/master/ext/curl/multi.c#L170 + $active = $this->executeHandles(true, 0.25); + + } else { + // Requests are not actually pending a cURL select call, so + // we need to delay in order to prevent eating too much CPU + usleep(500); + } + } + } + + /** + * Execute and select curl handles until there is activity + * + * @param bool $select Set to TRUE to select the file descriptors + * @param int $timeout Select timeout in seconds + * + * @return int Returns the number of active handles + */ + private function executeHandles($select = false, $timeout = 1) + { + $active = 0; + $selectResult = 0; + + do { + + if ($select) { + $selectResult = curl_multi_select($this->multiHandle, $timeout); + } + + if ($selectResult == 0) { + while ($mrc = curl_multi_exec($this->multiHandle, $active) == CURLM_CALL_MULTI_PERFORM); + // Check the return value to ensure an error did not occur + $this->checkCurlResult($mrc); + } + + // Poll once if not selecting, or poll until there are no handles with activity + } while ($select && $active && $selectResult == 0); + + return $active; + } + + /** + * Remove a request that encountered an exception + * + * @param RequestInterface $request Request to remove + * @param Exception $e Exception encountered + */ + protected function removeErroredRequest(RequestInterface $request, \Exception $e) + { + $this->remove($request); + $request->setState(RequestInterface::STATE_ERROR); + $this->dispatch(self::MULTI_EXCEPTION, array( + 'exception' => $e, + 'all_exceptions' => $this->exceptions + )); + $this->exceptions[] = $e; + } + + /** + * Check for errors and fix headers of a request based on a curl response + * + * @param RequestInterface $request Request to process + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @throws CurlException on Curl error + */ + protected function processResponse(RequestInterface $request, CurlHandle $handle, array $curl) + { + // Set the transfer stats on the response + $handle->updateRequestFromTransfer($request); + + // Check if a cURL exception occurred, and if so, notify things + $e = $this->isCurlException($request, $handle, $curl); + + // Always remove completed curl handles. They can be added back again + // via events if needed (e.g. ExponentialBackoffPlugin) + $this->removeHandle($request); + + if ($e) { + // Set the state of the request to an error + $request->setState(RequestInterface::STATE_ERROR); + // Notify things that listen to the request of the failure + $request->dispatch('request.exception', array( + 'request' => $this, + 'exception' => $e + )); + + // Allow things to ignore the error if possible + $state = $request->getState(); + if ($state != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + // The error was not handled, so fail + if ($state == RequestInterface::STATE_ERROR) { + throw $e; + } + } else { + $request->setState(RequestInterface::STATE_COMPLETE); + // Allow things to ignore the error if possible + if ($request->getState() != RequestInterface::STATE_TRANSFER) { + $this->remove($request); + } + } + } + + /** + * Remove a curl handle from the curl multi object + * + * @param RequestInterface $request Request that owns the handle + */ + protected function removeHandle(RequestInterface $request) + { + $handle = $this->getRequestHandle($request); + if ($handle) { + unset($this->handles[spl_object_hash($request)]); + // Nasty things (Bus errors segmentation faults) can sometimes + // happen when removing cURL handles from inside the context of a + // callback or in a recursive scope. Because of this, here we are + // queueing all curl handles that need to be removed and closed + // so that this happens only in the outermost scope when everything + // has completed sending. + $this->removeHandles[] = $handle; + } + } + + /** + * Check if a cURL transfer resulted in what should be an exception + * + * @param RequestInterface $request Request to check + * @param CurlHandle $handle Curl handle object + * @param array $curl Array returned from curl_multi_info_read + * + * @return Exception|bool + */ + private function isCurlException(RequestInterface $request, CurlHandle $handle, array $curl) + { + if (CURLE_OK == $curl['result']) { + return false; + } + + $handle->setErrorNo($curl['result']); + $e = new CurlException(sprintf('[curl] %s: %s [url] %s [info] %s [debug] %s', + $handle->getErrorNo(), $handle->getError(), $handle->getUrl(), + var_export($handle->getInfo(), true), $handle->getStderr())); + $e->setCurlHandle($handle) + ->setRequest($request) + ->setError($handle->getError(), $handle->getErrorNo()); + + return $e; + } + + /** + * Get the curl handle associated with a request + * + * @param RequestInterface $request Request + * + * @return CurlHandle|null + */ + private function getRequestHandle(RequestInterface $request) + { + $hash = spl_object_hash($request); + + return isset($this->handles[$hash]) ? $this->handles[$hash] : null; + } + + /** + * Throw an exception for a cURL multi response if needed + * + * @param int $code Curl response code + * + * @throws CurlException + */ + private function checkCurlResult($code) + { + if ($code <= 0) { + return; + } + + if (isset($this->multiErrors[$code])) { + $message = "cURL error: {$code} ({$this->multiErrors[$code][0]}): cURL message: {$this->multiErrors[$code][1]}"; + } else { + $message = 'Unexpected cURL error: ' . $code; + } + + throw new CurlException($message); + } + + /** + * Create the new cURL multi handle with error checking + */ + private function createMutliHandle() + { + if ($this->multiHandle && is_resource($this->multiHandle)) { + curl_multi_close($this->multiHandle); + } + + $this->multiHandle = curl_multi_init(); + + // @codeCoverageIgnoreStart + if ($this->multiHandle === false) { + throw new CurlException('Unable to create multi handle'); + } + // @codeCoverageIgnoreEnd + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php new file mode 100644 index 0000000..58042ff --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/CurlMultiInterface.php @@ -0,0 +1,77 @@ +version) { + $this->version = curl_version(); + // Check if CURLOPT_FOLLOWLOCATION is available + $this->version['follow_location'] = !ini_get('open_basedir'); + } + + return $this->version; + } + + /** + * Get a specific type of curl information + * + * @param $type Version information to retrieve. This value is one of: + * + * - version_number: cURL 24 bit version number + * - version: cURL version number, as a string + * - ssl_version_number: OpenSSL 24 bit version number + * - ssl_version: OpenSSL version number, as a string + * - libz_version: zlib version number, as a string + * - host: Information about the host where cURL was built + * - features: A bitmask of the CURL_VERSION_XXX constants + * - protocols: An array of protocols names supported by cURL + * + * @return string|float|bool if the $type is found, and false if not found + */ + public function get($type) + { + $version = $this->getAll(); + + return isset($version[$type]) ? $version[$type] : false; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php new file mode 100644 index 0000000..c90c202 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Curl/RequestMediator.php @@ -0,0 +1,128 @@ +request = $request; + $this->emitIo = $request->getParams()->get('curl.emit_io'); + } + + /** + * Set the associated CurlHandle object + * + * @param CurlHandle $handle Curl handle + * + * @return RequestMediator + */ + public function setCurlHandle(CurlHandle $handle) + { + $this->curlHandle = $handle; + + return $this; + } + + /** + * Receive a response header from curl + * + * @param resource $curl Curl handle + * @param string $header Received header + * + * @return int + */ + public function receiveResponseHeader ($curl, $header) + { + return $this->request->receiveResponseHeader($header); + } + + /** + * Received a progress notification + * + * @param int $downloadSize Total download size + * @param int $downloaded Amount of bytes downloaded + * @param int $uploadSize Total upload size + * @param int $uploaded Amount of bytes uploaded + */ + public function progress($downloadSize, $downloaded, $uploadSize, $uploaded) + { + $this->request->dispatch('curl.callback.progress', array( + 'request' => $this->request, + 'handle' => $this->curlHandle, + 'download_size' => $downloadSize, + 'downloaded' => $downloaded, + 'upload_size' => $uploadSize, + 'uploaded' => $uploaded + )); + } + + /** + * Write data to the response body of a request + * + * @param resource $curl Curl handle + * @param string $write Data that was received + * + * @return int + */ + public function writeResponseBody($curl, $write) + { + if ($this->emitIo) { + $this->request->dispatch('curl.callback.write', array( + 'request' => $this->request, + 'write' => $write + )); + } + + return $this->request->getResponse()->getBody()->write($write); + } + + /** + * Read data from the request body and send it to curl + * + * @param resource $ch Curl handle + * @param resource $fd File descriptor + * @param int $length Amount of data to read + * + * @return string + */ + public function readRequestBody($ch, $fd, $length) + { + $read = ''; + + if ($this->request->getBody()) { + $read = $this->request->getBody()->read($length); + if ($this->emitIo) { + $this->request->dispatch('curl.callback.read', array( + 'request' => $this->request, + 'read' => $read + )); + } + } + + return !$read ? '' : $read; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php new file mode 100644 index 0000000..76bca99 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/EntityBody.php @@ -0,0 +1,237 @@ +handleCompression($filter); + $this->contentEncoding = $result ? $filter : false; + + return $result; + } + + /** + * Uncompress a deflated string. Once uncompressed, the uncompressed + * string is then used as the wrapped stream. + * + * @param string $filter De-compression filter + * + * @return bool Returns TRUE on success or FALSE on failure + */ + public function uncompress($filter = 'zlib.inflate') + { + $offsetStart = 0; + + // When inflating gzipped data, the first 10 bytes must be stripped + // if a gzip header is present + if ($filter == 'zlib.inflate') { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + $this->seek(0); + if (fread($this->stream, 3) == "\x1f\x8b\x08") { + $offsetStart = 10; + } + } + + $this->contentEncoding = false; + + return $this->handleCompression($filter, $offsetStart); + } + + /** + * Get the Content-Length of the entity body if possible (alias of getSize) + * + * @return int|false + */ + public function getContentLength() + { + return $this->getSize(); + } + + /** + * Guess the Content-Type or return the default application/octet-stream + * + * @return string + * @see http://www.php.net/manual/en/function.finfo-open.php + */ + public function getContentType() + { + if (!class_exists('finfo', false) || !($this->isLocal() && $this->getWrapper() == 'plainfile' && file_exists($this->getUri()))) { + return 'application/octet-stream'; + } + + $finfo = new \finfo(FILEINFO_MIME_TYPE); + + return $finfo->file($this->getUri()); + } + + /** + * Get an MD5 checksum of the stream's contents + * + * @param bool $rawOutput Whether or not to use raw output + * @param bool $base64Encode Whether or not to base64 encode raw output + * (only if raw output is true) + * + * @return bool|string Returns an MD5 string on success or FALSE on failure + */ + public function getContentMd5($rawOutput = false, $base64Encode = false) + { + if (!$this->seek(0)) { + return false; + } + + $ctx = hash_init('md5'); + while ($data = $this->read(1024)) { + hash_update($ctx, $data); + } + + $out = hash_final($ctx, (bool) $rawOutput); + $this->seek(0); + + return ((bool) $base64Encode && (bool) $rawOutput) ? base64_encode($out) : $out; + } + + /** + * Set the type of encoding stream that was used on the entity body + * + * @param string $streamFilterContentEncoding Stream filter used + * + * @return EntityBody + */ + public function setStreamFilterContentEncoding($streamFilterContentEncoding) + { + $this->contentEncoding = $streamFilterContentEncoding; + + return $this; + } + + /** + * Get the Content-Encoding of the EntityBody + * + * @return bool|string + */ + public function getContentEncoding() + { + return strtr($this->contentEncoding, array( + 'zlib.deflate' => 'gzip', + 'bzip2.compress' => 'compress' + )) ?: false; + } + + /** + * Handles compression or uncompression of stream data + * + * @param string $filter Name of the filter to use (zlib.deflate or zlib.inflate) + * @param int $offsetStart Number of bytes to skip from start + * + * @return bool Returns TRUE on success or FALSE on failure + */ + protected function handleCompression($filter, $offsetStart = null) + { + // @codeCoverageIgnoreStart + if (!$this->isReadable() || ($this->isConsumed() && !$this->isSeekable())) { + return false; + } + // @codeCoverageIgnoreEnd + + $handle = fopen('php://temp', 'r+'); + $filter = @stream_filter_append($handle, $filter, STREAM_FILTER_WRITE); + if (!$filter) { + return false; + } + + // Seek to the beginning of the stream if possible + $this->seek(0); + + if ($offsetStart) { + fread($this->stream, $offsetStart); + } + + while ($data = fread($this->stream, 8096)) { + fwrite($handle, $data); + } + + fclose($this->stream); + $this->stream = $handle; + stream_filter_remove($filter); + $stat = fstat($this->stream); + $this->size = $stat['size']; + $this->rebuildCache(); + $this->seek(0); + + return true; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php new file mode 100644 index 0000000..e340095 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/BadResponseException.php @@ -0,0 +1,74 @@ +isClientError()) { + $label = 'Client error response'; + $class = __NAMESPACE__ . '\\ClientErrorResponseException'; + } elseif ($response->isServerError()) { + $label = 'Server error response'; + $class = __NAMESPACE__ . '\\ServerErrorResponseException'; + } else { + $label = 'Unsuccessful response'; + $class = __CLASS__; + $e = new self(); + } + + $message = $label . PHP_EOL . implode(PHP_EOL, array( + '[status code] ' . $response->getStatusCode(), + '[reason phrase] ' . $response->getReasonPhrase(), + '[url] ' . $request->getUrl(), + '[request] ' . (string) $request, + '[response] ' . (string) $response + )); + + $e = new $class($message); + $e->setResponse($response); + $e->setRequest($request); + + return $e; + } + + /** + * Set the response that caused the exception + * + * @param Response $response Response to set + */ + public function setResponse(Response $response) + { + $this->response = $response; + } + + /** + * Get the response that caused the exception + * + * @return Response + */ + public function getResponse() + { + return $this->response; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php new file mode 100644 index 0000000..04d7ddc --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ClientErrorResponseException.php @@ -0,0 +1,8 @@ +curlError = $error; + $this->curlErrorNo = $number; + + return $this; + } + + /** + * Set the associated curl handle + * + * @param CurlHandle $handle Curl handle + * + * @return self + */ + public function setCurlHandle(CurlHandle $handle) + { + $this->handle = $handle; + + return $this; + } + + /** + * Get the associated cURL handle + * + * @return CurlHandle|null + */ + public function getCurlHandle() + { + return $this->handle; + } + + /** + * Get the associated cURL error message + * + * @return string + */ + public function getError() + { + return $this->curlError; + } + + /** + * Get the associated cURL error number + * + * @return int + */ + public function getErrorNo() + { + return $this->curlErrorNo; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php new file mode 100644 index 0000000..05e6f2d --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/RequestException.php @@ -0,0 +1,42 @@ +request = $request; + + return $this; + } + + /** + * Get the request that caused the exception + * + * @return RequestInterface + */ + public function getRequest() + { + return $this->request; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php new file mode 100644 index 0000000..f0f7cfe --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Exception/ServerErrorResponseException.php @@ -0,0 +1,8 @@ +params; + } + + /** + * Add a header to an existing collection of headers. + * + * @param string $header Header name to add + * @param string $value Value of the header + * + * @return AbstractMessage + */ + public function addHeader($header, $value) + { + $key = strtolower($header); + if (!isset($this->headers[$key])) { + $this->headers[$key] = new Header($header, $value); + } else { + $this->headers[$key]->add($value, $header); + } + $this->changedHeader('set', $key); + + return $this; + } + + /** + * Add and merge in an array of HTTP headers. + * + * @param array $headers Associative array of header data. + * + * @return AbstractMessage + */ + public function addHeaders(array $headers) + { + foreach ($headers as $key => $value) { + $this->addHeader($key, $value); + } + + return $this; + } + + /** + * Retrieve an HTTP header by name. Performs a case-insensitive search of + * all headers. + * + * @param string $header Header to retrieve. + * @param bool $string Set to true to get the header as a string + * + * @return string|Header|null Returns NULL if no matching header is found. + * Returns a string if $string is set to TRUE. Returns a Header object + * if a matching header is found. + */ + public function getHeader($header, $string = false) + { + $key = strtolower($header); + if (!isset($this->headers[$key])) { + return null; + } + + return $string ? (string) $this->headers[$key] : $this->headers[$key]; + } + + /** + * Get all headers as a collection + * + * @param $asObjects Set to true to retrieve a collection of Header objects + * + * @return Collection Returns a {@see Collection} of all headers + */ + public function getHeaders($asObjects = false) + { + if ($asObjects) { + $result = $this->headers; + } else { + $result = array(); + // Convert all of the headers into a collection + foreach ($this->headers as $header) { + foreach ($header->raw() as $key => $value) { + $result[$key] = $value; + } + } + } + + return new Collection($result); + } + + /** + * Get an array of message header lines + * + * @return array + */ + public function getHeaderLines() + { + $headers = array(); + foreach ($this->headers as $value) { + $glue = $value->getGlue(); + foreach ($value->raw() as $key => $v) { + $headers[] = rtrim($key . ': ' . implode($glue, $v)); + } + } + + return $headers; + } + + /** + * Set an HTTP header + * + * @param string $header Name of the header to set. + * @param mixed $value Value to set. + * + * @return AbstractMessage + */ + public function setHeader($header, $value) + { + // Remove any existing header + $key = strtolower($header); + unset($this->headers[$key]); + + if ($value instanceof Header) { + $this->headers[$key] = $value; + } else { + // Allow for 0, '', and NULL to be set + if (!$value) { + $value = array($value); + } + $this->headers[$key] = new Header($header, $value); + } + $this->changedHeader('set', $key); + + return $this; + } + + /** + * Overwrite all HTTP headers with the supplied array of headers + * + * @param array $headers Associative array of header data. + * + * @return AbstractMessage + */ + public function setHeaders(array $headers) + { + // Get the keys that are changing + $changed = array_keys($this->headers); + // Erase the old headers + $this->headers = array(); + // Add the new headers + foreach ($headers as $key => $value) { + $changed[] = $key; + $this->addHeader($key, $value); + } + + // Notify of the changed headers + foreach (array_unique($changed) as $header) { + $this->changedHeader('set', strtolower($header)); + } + + return $this; + } + + /** + * Check if the specified header is present. + * + * @param string $header The header to check. + * + * @return bool Returns TRUE or FALSE if the header is present + */ + public function hasHeader($header) + { + return array_key_exists(strtolower($header), $this->headers); + } + + /** + * Remove a specific HTTP header. + * + * @param string $header HTTP header to remove. + * + * @return AbstractMessage + */ + public function removeHeader($header) + { + $key = strtolower($header); + unset($this->headers[$key]); + $this->changedHeader('remove', $key); + + return $this; + } + + /** + * Get a tokenized header as a Collection + * + * @param string $header Header to retrieve + * @param string $token Token separator + * + * @return Collection|null + */ + public function getTokenizedHeader($header, $token = ';') + { + if (!$this->hasHeader($header)) { + return null; + } + + $data = new Collection(); + + foreach ($this->getHeader($header) as $singleValue) { + foreach (explode($token, $singleValue) as $kvp) { + $parts = explode('=', $kvp, 2); + if (!isset($parts[1])) { + $data[count($data)] = trim($parts[0]); + } else { + $data->add(trim($parts[0]), trim($parts[1])); + } + } + } + + foreach ($data as $key => $value) { + if (is_array($value)) { + $data->set($key, array_unique($value)); + } + } + + return $data; + } + + /** + * Set a tokenized header on the request that implodes a Collection of data + * into a string separated by a token + * + * @param string $header Header to set + * @param array|Collection $data Header data + * @param string $token Token delimiter + * + * @return AbstractMessage + * @throws InvalidArgumentException if data is not an array or Collection + */ + public function setTokenizedHeader($header, $data, $token = ';') + { + if (!($data instanceof Collection) && !is_array($data)) { + throw new InvalidArgumentException('Data must be a Collection or array'); + } + + $values = array(); + foreach ($data as $key => $value) { + foreach ((array) $value as $v) { + $values[] = is_int($key) ? $v : $key . '=' . $v; + } + } + + return $this->setHeader($header, implode($token, $values)); + } + + /** + * Get a Cache-Control directive from the message + * + * @param string $directive Directive to retrieve + * + * @return null|string + */ + public function getCacheControlDirective($directive) + { + return isset($this->cacheControl[$directive]) ? $this->cacheControl[$directive] : null; + } + + /** + * Check if the message has a Cache-Control directive + * + * @param string $directive Directive to check + * + * @return bool + */ + public function hasCacheControlDirective($directive) + { + return isset($this->cacheControl[$directive]); + } + + /** + * Add a Cache-Control directive on the message + * + * @param string $directive Directive to set + * @param bool|string $value Value to set + * + * @return AbstractMessage + */ + public function addCacheControlDirective($directive, $value = true) + { + $this->cacheControl[$directive] = $value; + $this->rebuildCacheControlDirective(); + + return $this; + } + + /** + * Remove a Cache-Control directive from the message + * + * @param string $directive Directive to remove + * + * @return AbstractMessage + */ + public function removeCacheControlDirective($directive) + { + if (array_key_exists($directive, $this->cacheControl)) { + unset($this->cacheControl[$directive]); + $this->rebuildCacheControlDirective(); + } + + return $this; + } + + /** + * Check to see if the modified headers need to reset any of the managed + * headers like cache-control + * + * @param string $action One of set or remove + * @param string $header Header that changed + */ + protected function changedHeader($action, $header) + { + if ($header == 'cache-control') { + $this->parseCacheControlDirective(); + } + } + + /** + * Parse the Cache-Control HTTP header into an array + */ + private function parseCacheControlDirective() + { + $this->cacheControl = array(); + $tokenized = $this->getTokenizedHeader('Cache-Control', ',') ?: array(); + foreach ($tokenized as $key => $value) { + if (is_numeric($key)) { + $this->cacheControl[$value] = true; + } else { + $this->cacheControl[$key] = $value; + } + } + } + + /** + * Rebuild the Cache-Control HTTP header using the user-specified values + */ + private function rebuildCacheControlDirective() + { + $cacheControl = array(); + foreach ($this->cacheControl as $key => $value) { + $cacheControl[] = ($value === true) ? $key : ($key . '=' . $value); + } + + $this->headers['cache-control'] = new Header('Cache-Control', implode(', ', $cacheControl)); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php new file mode 100644 index 0000000..df40bb0 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequest.php @@ -0,0 +1,293 @@ +postFields = new QueryString(); + $this->postFields->setPrefix(''); + parent::__construct($method, $url, $headers); + } + + /** + * Get the HTTP request as a string + * + * @return string + */ + public function __toString() + { + // Only attempt to include the POST data if it's only fields + if (count($this->postFields) && empty($this->postFiles)) { + return parent::__toString() . (string) $this->postFields; + } + + return parent::__toString() . $this->body; + } + + /** + * Set the body of the request + * + * @param string|resource|EntityBody $body Body to use in the entity body of the request + * @param string $contentType Content-Type to set. Leave null to use an existing + * Content-Type or to guess the Content-Type + * @param bool $tryChunkedTransfer Set to TRUE to try to use Transfer-Encoding chunked + * + * @return EntityEnclosingRequest + * @throws RequestException if the protocol is < 1.1 and Content-Length can not be determined + */ + public function setBody($body, $contentType = null, $tryChunkedTransfer = false) + { + $this->body = EntityBody::factory($body); + $this->removeHeader('Content-Length'); + $this->setHeader('Expect', '100-Continue'); + + if ($contentType) { + $this->setHeader('Content-Type', (string) $contentType); + } + + if ($tryChunkedTransfer) { + $this->setHeader('Transfer-Encoding', 'chunked'); + } else { + $this->removeHeader('Transfer-Encoding'); + // Set the Content-Length header if it can be determined + $size = $this->body->getContentLength(); + if ($size !== null && $size !== false) { + $this->setHeader('Content-Length', $size); + } elseif ('1.1' == $this->protocolVersion) { + $this->setHeader('Transfer-Encoding', 'chunked'); + } else { + throw new RequestException('Cannot determine entity body ' + . 'size and cannot use chunked Transfer-Encoding when ' + . 'using HTTP/' . $this->protocolVersion + ); + } + } + + return $this; + } + + /** + * Get the body of the request if set + * + * @return EntityBody|null + */ + public function getBody() + { + return $this->body; + } + + /** + * Get a POST field from the request + * + * @param string $field Field to retrieve + * + * @return mixed|null + */ + public function getPostField($field) + { + return $this->postFields->get($field); + } + + /** + * Get the post fields that will be used in the request + * + * @return QueryString + */ + public function getPostFields() + { + return $this->postFields; + } + + /** + * Set a POST field value + * + * @param string $key Key to set + * @param string $value Value to set + * + * @return EntityEnclosingRequest + */ + public function setPostField($key, $value) + { + $this->postFields->set($key, $value); + $this->processPostFields(); + + return $this; + } + + /** + * Add POST fields to use in the request + * + * @param QueryString|array $fields POST fields + * + * @return EntityEnclosingRequest + */ + public function addPostFields($fields) + { + $this->postFields->merge($fields); + $this->processPostFields(); + + return $this; + } + + /** + * Remove a POST field or file by name + * + * @param string $field Name of the POST field or file to remove + * + * @return EntityEnclosingRequest + */ + public function removePostField($field) + { + $this->postFields->remove($field); + $this->processPostFields(); + + return $this; + } + + /** + * Returns an associative array of POST field names to an array of PostFileInterface objects + * + * @return array + */ + public function getPostFiles() + { + return $this->postFiles; + } + + /** + * Get a POST file from the request + * + * @param string $fieldName POST fields to retrieve + * + * @return PostFileInterface|null Returns an array wrapping PostFileInterface objects + */ + public function getPostFile($fieldName) + { + return isset($this->postFiles[$fieldName]) ? $this->postFiles[$fieldName] : null; + } + + /** + * Remove a POST file from the request + * + * @param string $fieldName POST file field name to remove + * + * @return EntityEnclosingRequest + */ + public function removePostFile($fieldName) + { + unset($this->postFiles[$fieldName]); + $this->processPostFields(); + + return $this; + } + + /** + * Add a POST file to the upload + * + * @param string|PostFileUpload $field POST field to use (e.g. file) or PostFileInterface object. + * @param string $filename Full path to the file. Do not include the @ symbol. + * @param string $contentType Optional Content-Type to add to the Content-Disposition. + * Default behavior is to guess. Set to false to not specify. + * @param bool $process Set to false to not process POST fields immediately. + * + * @return EntityEnclosingRequest + * @throws RequestException if the file cannot be read + */ + public function addPostFile($field, $filename = null, $contentType = null, $process = true) + { + $data = null; + + if ($field instanceof PostFileInterface) { + $data = $field; + } elseif (!is_string($filename)) { + throw new RequestException('The path to a file must be a string'); + } elseif (!empty($filename)) { + // Adding an empty file will cause cURL to error out + $data = new PostFile($field, $filename, $contentType); + } + + if ($data) { + if (!isset($this->postFiles[$data->getFieldName()])) { + $this->postFiles[$data->getFieldName()] = array($data); + } else { + $this->postFiles[$data->getFieldName()][] = $data; + } + if ($process) { + $this->processPostFields(); + } + } + + return $this; + } + + /** + * Add POST files to use in the upload + * + * @param array $files An array of POST fields => filenames where filename can be a string or PostFileInterfaces + * + * @return EntityEnclosingRequest + * @throws RequestException if the file cannot be read + */ + public function addPostFiles(array $files) + { + foreach ($files as $key => $file) { + if ($file instanceof PostFileInterface) { + $this->addPostFile($file, null, null, false); + } elseif (is_string($file)) { + // Convert non-associative array keys into 'file' + if (is_numeric($key)) { + $key = 'file'; + } + $this->addPostFile($key, $file, null, false); + } else { + throw new RequestException('File must be a string or instance of PostFileInterface'); + } + } + + $this->processPostFields(); + + return $this; + } + + /** + * Determine what type of request should be sent based on post fields + */ + protected function processPostFields() + { + if (empty($this->postFiles)) { + $this->setHeader('Content-Type', 'application/x-www-form-urlencoded'); + $this->removeHeader('Expect'); + } else { + $this->setHeader('Expect', '100-Continue') + ->setHeader('Content-Type', 'multipart/form-data'); + $this->postFields->setEncodeFields(false)->setEncodeValues(false); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php new file mode 100644 index 0000000..e821e24 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/EntityEnclosingRequestInterface.php @@ -0,0 +1,123 @@ + filenames where filename can be a string or PostFileInterface + * + * @return EntityEnclosingRequestInterface + */ + public function addPostFiles(array $files); +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php new file mode 100644 index 0000000..456a225 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/Header.php @@ -0,0 +1,253 @@ +header = $header; + $this->glue = $glue; + + if (null !== $values) { + foreach ((array) $values as $key => $value) { + if (is_numeric($key)) { + $key = $header; + } + if ($value === null) { + $this->add($value, $key); + } else { + foreach ((array) $value as $v) { + $this->add($v, $key); + } + } + } + } + } + + /** + * Convert the header to a string + * + * @return string + */ + public function __toString() + { + if (!$this->stringCache) { + $this->stringCache = implode($this->glue, $this->toArray()); + } + + return $this->stringCache; + } + + /** + * Add a value to the list of header values + * + * @param string $value Value to add + * @param string $header The exact header casing to add with. + * Defaults to the name of the header. + * + * @return Header + */ + public function add($value, $header = null) + { + if (!$header) { + $header = $this->getName(); + } + + if (!array_key_exists($header, $this->values)) { + $this->values[$header] = array($value); + } else { + $this->values[$header][] = $value; + } + + $this->clearCache(); + + return $this; + } + + /** + * Get the name of the header + * + * @return string + */ + public function getName() + { + return $this->header; + } + + /** + * Change the glue used to implode the values + * + * @param string $glue Glue used to implode multiple values + * + * @return Header + */ + public function setGlue($glue) + { + $this->glue = $glue; + $this->stringCache = null; + + return $this; + } + + /** + * Get the glue used to implode multiple values into a string + * + * @return string + */ + public function getGlue() + { + return $this->glue; + } + + /** + * Normalize the header into a single standard header with an array of values + * + * @return Header + */ + public function normalize() + { + $this->clearCache(); + $this->values = array( + $this->getName() => $this->toArray() + ); + + return $this; + } + + /** + * Check if a particular case variation is present in the header + * Example: A header exists on a message for 'Foo', and 'foo'. The Header + * object will contain all of the values of 'Foo' and all of the values of + * 'foo'. You can use this method to check to see if a header was set + * using 'foo' (true), 'Foo' (true), 'FOO' (false), etc. + * + * @param string $header Exact header to check for + * + * @return bool + */ + public function hasExactHeader($header) + { + return array_key_exists($header, $this->values); + } + + /** + * Check if the collection of headers has a particular value + * + * @param string $searchValue Value to search for + * @param bool $caseInsensitive Set to TRUE to use a case + * insensitive search + * + * @return bool + */ + public function hasValue($searchValue, $caseInsensitive = false) + { + foreach ($this->toArray() as $value) { + if ($value == $searchValue) { + return true; + } elseif ($caseInsensitive && !strcasecmp($value, $searchValue)) { + return true; + } + } + + return false; + } + + /** + * Remove a specific value from the header + * + * @param string $value Value to remove + * + * @return self + */ + public function removeValue($searchValue) + { + foreach ($this->values as $key => $values) { + foreach ($values as $index => $value) { + if ($value == $searchValue) { + unset($this->values[$key][$index]); + $this->clearCache(); + break 2; + } + } + } + + return $this; + } + + /** + * Get all of the header values as a flat array + * + * @return array + */ + public function toArray() + { + if (!$this->arrayCache) { + $this->arrayCache = array(); + foreach ($this->values as $values) { + foreach ($values as $value) { + $this->arrayCache[] = $value; + } + } + } + + return $this->arrayCache; + } + + /** + * Get the raw data array of the headers. This array is represented as an + * associative array of the various cases that might be stored in the + * header and an array of values associated with each case variation. + * + * @return array + */ + public function raw() + { + return $this->values; + } + + /** + * Returns the total number of header values + * + * @return int + */ + public function count() + { + return count($this->toArray()); + } + + /** + * Get an iterator that can be used to easily iterate over each header value + * + * @return ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->toArray()); + } + + /** + * Clear the internal header cache + */ + private function clearCache() + { + $this->arrayCache = null; + $this->stringCache = null; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/HeaderComparison.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/HeaderComparison.php new file mode 100644 index 0000000..6759d78 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/HeaderComparison.php @@ -0,0 +1,129 @@ +getAll(); + } + + foreach ($filteredHeaders as $k => $v) { + if ($k[0] == '_') { + // This header should be ignored + $ignore[] = str_replace('_', '', $k); + } elseif ($k[0] == '!') { + // This header must not be present + $absent[] = str_replace('!', '', $k); + } else { + $expected[$k] = $v; + } + } + + return $this->compareArray($expected, $actualHeaders, $ignore, $absent); + } + + /** + * Check if an array of HTTP headers matches another array of HTTP headers + * while taking * into account as a wildcard for header values + * + * @param array $expected Expected HTTP headers (allows wildcard values) + * @param array|Collection $actual Actual HTTP header array + * @param array $ignore Headers to ignore from the comparison + * @param array $absent Array of headers that must not be present + * + * @return array|false Returns an array of the differences or FALSE if none + */ + public function compareArray(array $expected, $actual, array $ignore = array(), array $absent = array()) + { + $differences = array(); + + // Add information about headers that were present but weren't supposed to be + foreach ($absent as $header) { + if ($this->hasKey($header, $actual)) { + $differences["++ {$header}"] = $actual[$header]; + unset($actual[$header]); + } + } + + // Check if expected headers are missing + foreach ($expected as $header => $value) { + if (!$this->hasKey($header, $actual)) { + $differences["- {$header}"] = $value; + } + } + + // Flip the ignore array so it works with the case insensitive helper + $ignore = array_flip($ignore); + // Allow case-insensitive comparisons in wildcards + $expected = array_change_key_case($expected); + + // Compare the expected and actual HTTP headers in no particular order + foreach ($actual as $key => $value) { + + // If this is to be ignored, the skip it + if ($this->hasKey($key, $ignore)) { + continue; + } + + // If the header was not expected + if (!$this->hasKey($key, $expected)) { + $differences["+ {$key}"] = $value; + continue; + } + + // Check values and take wildcards into account + $lkey = strtolower($key); + $pos = is_string($expected[$lkey]) ? strpos($expected[$lkey], '*') : false; + + foreach ((array) $actual[$key] as $v) { + if (($pos === false && $v != $expected[$lkey]) || $pos > 0 && substr($v, 0, $pos) != substr($expected[$lkey], 0, $pos)) { + $differences[$key] = "{$value} != {$expected[$lkey]}"; + } + } + } + + return empty($differences) ? false : $differences; + } + + /** + * Case insensitive check if an array have a key + * + * @param string $key Key to check + * @param array $array Array to check + * + * @return bool + */ + protected function hasKey($key, $array) + { + foreach (array_keys($array) as $k) { + if (!strcasecmp($k, $key)) { + return true; + } + } + + return false; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php new file mode 100644 index 0000000..f9ee2ef --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/MessageInterface.php @@ -0,0 +1,170 @@ +fieldName = $fieldName; + $this->setFilename($filename); + $this->contentType = $contentType ?: $this->guessContentType(); + } + + /** + * {@inheritdoc} + */ + public function setFieldName($name) + { + $this->fieldName = $name; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFieldName() + { + return $this->fieldName; + } + + /** + * {@inheritdoc} + */ + public function setFilename($filename) + { + // Remove leading @ symbol + if (strpos($filename, '@') === 0) { + $filename = substr($filename, 1); + } + + if (!is_readable($filename)) { + throw new InvalidArgumentException("Unable to open {$filename} for reading"); + } + + $this->filename = $filename; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFilename() + { + return $this->filename; + } + + /** + * {@inheritdoc} + */ + public function setContentType($type) + { + $this->contentType = $type; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getContentType() + { + return $this->contentType; + } + + /** + * {@inheritdoc} + */ + public function getCurlString() + { + return $this->contentType + ? '@' . $this->filename . ';type=' . $this->contentType + : '@' . $this->filename; + } + + /** + * Determine the Content-Type of the file + */ + protected function guessContentType() + { + // @codeCoverageIgnoreStart + if (!class_exists('finfo', false)) { + return 'application/octet-stream'; + } + // @codeCoverageIgnoreEnd + + $finfo = new \finfo(FILEINFO_MIME_TYPE); + + return $finfo->file($this->filename); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php new file mode 100644 index 0000000..a590741 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/PostFileInterface.php @@ -0,0 +1,65 @@ +method = strtoupper($method); + $this->curlOptions = new Collection(); + $this->params = new Collection(); + $this->setUrl($url); + + if ($headers) { + // Special handling for multi-value headers + foreach ($headers as $key => $value) { + $lkey = strtolower($key); + // Deal with collisions with Host and Authorization + if ($lkey == 'host') { + $this->setHeader($key, $value); + } elseif ($lkey == 'authorization') { + $parts = explode(' ', $value); + if ($parts[0] == 'Basic' && isset($parts[1])) { + list($user, $pass) = explode(':', base64_decode($parts[1])); + $this->setAuth($user, $pass); + } else { + $this->setHeader($key, $value); + } + } else { + foreach ((array) $value as $v) { + $this->addHeader($key, $v); + } + } + } + } + + if (!$this->hasHeader('User-Agent', true)) { + $this->setHeader('User-Agent', Utils::getDefaultUserAgent()); + } + + $this->setState(self::STATE_NEW); + } + + /** + * Clone the request object, leaving off any response that was received + */ + public function __clone() + { + if ($this->eventDispatcher) { + $this->eventDispatcher = clone $this->eventDispatcher; + } + $this->curlOptions = clone $this->curlOptions; + $this->params = clone $this->params; + $this->url = clone $this->url; + + // Get a clone of the headers + $headers = array(); + foreach ($this->headers as $k => $v) { + $headers[$k] = clone $v; + } + $this->headers = $headers; + + $this->response = $this->responseBody = null; + $this->params->remove('curl_handle') + ->remove('queued_response') + ->remove('curl_multi'); + $this->setState(RequestInterface::STATE_NEW); + } + + /** + * Get the HTTP request as a string + * + * @return string + */ + public function __toString() + { + return $this->getRawHeaders() . "\r\n\r\n"; + } + + /** + * Default method that will throw exceptions if an unsuccessful response + * is received. + * + * @param Event $event Received + * @throws BadResponseException if the response is not successful + */ + public static function onRequestError(Event $event) + { + $e = BadResponseException::factory($event['request'], $event['response']); + $event['request']->dispatch('request.exception', array( + 'request' => $event['request'], + 'response' => $event['response'], + 'exception' => $e + )); + + throw $e; + } + + /** + * Set the client used to transport the request + * + * @param ClientInterface $client + * + * @return Request + */ + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + /** + * Get the client used to transport the request + * + * @return ClientInterface $client + */ + public function getClient() + { + return $this->client; + } + + /** + * Get the raw message headers as a string + * + * @return string + */ + public function getRawHeaders() + { + $protocolVersion = $this->protocolVersion ?: '1.1'; + + return trim($this->method . ' ' . $this->getResource()) . ' ' + . strtoupper(str_replace('https', 'http', $this->url->getScheme())) + . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines()); + } + + /** + * Set the URL of the request + * + * Warning: Calling this method will modify headers, rewrite the query + * string object, and set other data associated with the request. + * + * @param string|Url $url Full URL to set including query string + * + * @return Request + */ + public function setUrl($url) + { + if ($url instanceof Url) { + $this->url = $url; + } else { + $this->url = Url::factory($url); + } + + // Update the port and host header + $this->setPort($this->url->getPort()); + + if ($this->url->getUsername() || $this->url->getPassword()) { + $this->setAuth($this->url->getUsername(), $this->url->getPassword()); + // Remove the auth info from the URL + $this->url->setUsername(null); + $this->url->setPassword(null); + } + + return $this; + } + + /** + * Send the request + * + * @return Response + * @throws RuntimeException if a client is not associated with the request + */ + public function send() + { + if (!$this->client) { + throw new RuntimeException('A client must be set on the request'); + } + + return $this->client->send($this); + } + + /** + * Get the previously received {@see Response} or NULL if the request has + * not been sent + * + * @return Response|null + */ + public function getResponse() + { + return $this->response; + } + + /** + * Get the collection of key value pairs that will be used as the query + * string in the request + * + * @param bool $asString Set to TRUE to get the query as string + * + * @return QueryString|string + */ + public function getQuery($asString = false) + { + return $asString + ? (string) $this->url->getQuery() + : $this->url->getQuery(); + } + + /** + * Get the HTTP method of the request + * + * @return string + */ + public function getMethod() + { + return $this->method; + } + + /** + * Get the URI scheme of the request (http, https, ftp, etc) + * + * @return string + */ + public function getScheme() + { + return $this->url->getScheme(); + } + + /** + * Set the URI scheme of the request (http, https, ftp, etc) + * + * @param string $scheme Scheme to set + * + * @return Request + */ + public function setScheme($scheme) + { + $this->url->setScheme($scheme); + + return $this; + } + + /** + * Get the host of the request + * + * @return string + */ + public function getHost() + { + return $this->url->getHost(); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, www.yahoo.com) + * + * @return Request + */ + public function setHost($host) + { + $this->url->setHost($host); + $this->setPort($this->url->getPort()); + + return $this; + } + + /** + * Get the HTTP protocol version of the request + * + * @return string + */ + public function getProtocolVersion() + { + return $this->protocolVersion; + } + + /** + * Set the HTTP protocol version of the request (e.g. 1.1 or 1.0) + * + * @param string $protocol HTTP protocol version to use with the request + * + * @return Request + */ + public function setProtocolVersion($protocol) + { + $this->protocolVersion = $protocol; + + return $this; + } + + /** + * Get the path of the request (e.g. '/', '/index.html') + * + * @return string + */ + public function getPath() + { + return $this->url->getPath(); + } + + /** + * Set the path of the request (e.g. '/', '/index.html') + * + * @param string|array $path Path to set or array of segments to implode + * + * @return Request + */ + public function setPath($path) + { + $this->url->setPath($path); + + return $this; + } + + /** + * Get the port that the request will be sent on if it has been set + * + * @return int|null + */ + public function getPort() + { + return $this->url->getPort(); + } + + /** + * Set the port that the request will be sent on + * + * @param int $port Port number to set + * + * @return Request + */ + public function setPort($port) + { + $this->url->setPort($port); + + // Include the port in the Host header if it is not the default port + // for the scheme of the URL + $scheme = $this->url->getScheme(); + if (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443)) { + $this->headers['host'] = new Header('Host', $this->url->getHost() . ':' . $port); + } else { + $this->headers['host'] = new Header('Host', $this->url->getHost()); + } + + return $this; + } + + /** + * Get the username to pass in the URL if set + * + * @return string|null + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the password to pass in the URL if set + * + * @return string|null + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set HTTP authorization parameters + * + * @param string|false $user User name or false disable authentication + * @param string $password Password + * @param string $scheme Curl authentication scheme to use + * + * @return Request + * + * @see http://www.ietf.org/rfc/rfc2617.txt + */ + public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC) + { + // If we got false or null, disable authentication + if (!$user || !$password) { + $this->password = $this->username = null; + $this->removeHeader('Authorization'); + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + } else { + $this->username = $user; + $this->password = $password; + // Bypass CURL when using basic auth to promote connection reuse + if ($scheme == CURLAUTH_BASIC) { + $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH); + $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password)); + } else { + $this->getCurlOptions()->set(CURLOPT_HTTPAUTH, $scheme) + ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password); + } + } + + return $this; + } + + /** + * Get the resource part of the the request, including the path, query + * string, and fragment + * + * @return string + */ + public function getResource() + { + return $this->url->getPath() . (string) $this->url->getQuery(); + } + + /** + * Get the full URL of the request (e.g. 'http://www.guzzle-project.com/') + * scheme://username:password@domain:port/path?query_string#fragment + * + * @param bool $asObject Set to TRUE to retrieve the URL as + * a clone of the URL object owned by the request + * + * @return string|Url + */ + public function getUrl($asObject = false) + { + return $asObject ? clone $this->url : (string) $this->url; + } + + /** + * Get the state of the request. One of 'complete', 'sending', 'new' + * + * @return string + */ + public function getState() + { + return $this->state; + } + + /** + * Set the state of the request + * + * @param string $state State of the request (complete, sending, or new) + * + * @return Request + */ + public function setState($state) + { + $this->state = $state; + if ($this->state == self::STATE_NEW) { + $this->responseBody = $this->response = null; + } elseif ($this->state == self::STATE_COMPLETE) { + $this->processResponse(); + } + + return $this; + } + + /** + * Get the cURL options that will be applied when the cURL handle is created + * + * @return Collection + */ + public function getCurlOptions() + { + return $this->curlOptions; + } + + /** + * Method to receive HTTP response headers as they are retrieved + * + * @param string $data Header data. + * + * @return integer Returns the size of the data. + */ + public function receiveResponseHeader($data) + { + static $normalize = array("\r", "\n"); + $this->state = self::STATE_TRANSFER; + $length = strlen($data); + $data = str_replace($normalize, '', $data); + + if (strpos($data, 'HTTP/') === 0) { + + list($dummy, $code, $status) = explode(' ', $data, 3); + + // Only download the body of the response to the specified response + // body when a successful response is received. + if ($code >= 200 && $code < 300) { + $body = $this->getResponseBody(); + } else { + $body = EntityBody::factory(); + } + + $previousResponse = $this->response; + $this->response = new Response($code, null, $body); + $this->response->setStatus($code, $status)->setRequest($this); + $this->dispatch('request.receive.status_line', array( + 'line' => $data, + 'status_code' => $code, + 'reason_phrase' => $status, + 'previous_response' => $previousResponse + )); + + } elseif (strpos($data, ':') !== false) { + + list($header, $value) = explode(':', $data, 2); + $this->response->addHeader(trim($header), trim($value)); + } + + return $length; + } + + /** + * Manually set a response for the request. + * + * This method is useful for specifying a mock response for the request or + * setting the response using a cache. Manually setting a response will + * bypass the actual sending of a request. + * + * @param Response $response Response object to set + * @param bool $queued Set to TRUE to keep the request in a stat + * of not having been sent, but queue the response for send() + * + * @return Request Returns a reference to the object. + */ + public function setResponse(Response $response, $queued = false) + { + $response->setRequest($this); + + if ($queued) { + $this->getParams()->set('queued_response', $response); + } else { + $this->getParams()->remove('queued_response'); + $this->response = $response; + $this->responseBody = $response->getBody(); + $this->processResponse(); + } + + $this->dispatch('request.set_response', $this->getEventArray()); + + return $this; + } + + /** + * Set the EntityBody that will hold the response message's entity body. + * + * This method should be invoked when you need to send the response's + * entity body somewhere other than the normal php://temp buffer. For + * example, you can send the entity body to a socket, file, or some other + * custom stream. + * + * @param EntityBody $body Response body object + * + * @return Request + */ + public function setResponseBody(EntityBody $body) + { + $this->responseBody = $body; + + return $this; + } + + /** + * Determine if the response body is repeatable (readable + seekable) + * + * @return bool + */ + public function isResponseBodyRepeatable() + { + return !$this->responseBody ? true : $this->responseBody->isSeekable() && $this->responseBody->isReadable(); + } + + /** + * Get an array of cookies + * + * @return array + */ + public function getCookies() + { + $cookieData = new Collection(); + if ($cookies = $this->getHeader('Cookie')) { + foreach ($cookies as $cookie) { + $parts = explode('=', $cookie, 2); + $cookieData->add($parts[0], isset($parts[1]) ? $parts[1] : ''); + } + } + + return $cookieData->getAll(); + } + + /** + * Get a cookie value by name + * + * @param string $name Cookie to retrieve + * + * @return null|string|array + */ + public function getCookie($name) + { + $cookies = $this->getCookies(); + + return isset($cookies[$name]) ? $cookies[$name] : null; + } + + /** + * Add a Cookie value by name to the Cookie header + * + * @param string $name Name of the cookie to add + * @param string $value Value to set + * + * @return Request + */ + public function addCookie($name, $value) + { + if (!$this->hasHeader('Cookie')) { + $this->setHeader('Cookie', "{$name}={$value}"); + } else { + $this->getHeader('Cookie')->add("{$name}={$value}"); + } + + return $this; + } + + /** + * Remove a specific cookie value by name + * + * @param string $name Cookie to remove by name + * + * @return Request + */ + public function removeCookie($name) + { + if ($cookie = $this->getHeader('Cookie')) { + foreach ($cookie as $cookieValue) { + if (strpos($cookieValue, $name . '=') === 0) { + $cookie->removeValue($cookieValue); + } + } + } + + return $this; + } + + + /** + * Returns whether or not the request can be cached + * + * @return bool + */ + public function canCache() + { + // Only GET and HEAD requests can be cached + if ($this->method != RequestInterface::GET && $this->method != RequestInterface::HEAD) { + return false; + } + + // Never cache requests when using no-store + if ($this->hasCacheControlDirective('no-store')) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher) + { + $this->eventDispatcher = $eventDispatcher; + $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getEventDispatcher() + { + if (!$this->eventDispatcher) { + $this->setEventDispatcher(new EventDispatcher()); + } + + return $this->eventDispatcher; + } + + /** + * {@inheritdoc} + */ + public function dispatch($eventName, array $context = array()) + { + $context['request'] = $this; + $this->getEventDispatcher()->dispatch($eventName, new Event($context)); + } + + /** + * {@inheritdoc} + * @codeCoverageIgnore + */ + public function addSubscriber(EventSubscriberInterface $subscriber) + { + $this->getEventDispatcher()->addSubscriber($subscriber); + + return $this; + } + + /** + * {@inheritdoc} + */ + protected function changedHeader($action, $header) + { + parent::changedHeader($action, $header); + + if ($header === 'host') { + // If the Host header was changed, be sure to update the internal URL + $this->setHost((string) $this->getHeader('Host')); + } + } + + /** + * Get the EntityBody that will store the received response entity body + * + * @return EntityBody + */ + protected function getResponseBody() + { + if ($this->responseBody === null) { + $this->responseBody = EntityBody::factory(); + } + + return $this->responseBody; + } + + /** + * Get an array containing the request and response for event notifications + * + * @return array + */ + protected function getEventArray() + { + return array( + 'request' => $this, + 'response' => $this->response + ); + } + + /** + * Process a received response + * + * @throws BadResponseException on unsuccessful responses + */ + protected function processResponse() + { + // Use the queued response if one is set + if ($this->getParams()->get('queued_response')) { + $this->response = $this->getParams()->get('queued_response'); + $this->responseBody = $this->response->getBody(); + $this->getParams()->remove('queued_response'); + } elseif (!$this->response) { + // If no response, then processResponse shouldn't have been called + $e = new RequestException('Error completing request'); + $e->setRequest($this); + throw $e; + } + + $this->state = self::STATE_COMPLETE; + + // A request was sent, but we don't know if we'll send more or if the + // final response will be a successful response + $this->dispatch('request.sent', $this->getEventArray()); + + // Some response processors will remove the response or reset the state + // (example: ExponentialBackoffPlugin) + if ($this->state == RequestInterface::STATE_COMPLETE) { + + // The request completed, so the HTTP transaction is complete + $this->dispatch('request.complete', $this->getEventArray()); + + // If the response is bad, allow listeners to modify it or throw + // exceptions. You can change the response by modifying the Event + // object in your listeners or calling setResponse() on the request + if ($this->response->isError()) { + $event = new Event($this->getEventArray()); + $this->getEventDispatcher()->dispatch('request.error', $event); + // Allow events of request.error to quietly change the response + if ($event['response'] !== $this->response) { + $this->response = $event['response']; + } + } + + // If a successful response was received, dispatch an event + if ($this->response->isSuccessful()) { + $this->dispatch('request.success', $this->getEventArray()); + } + } + + return $this; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php new file mode 100644 index 0000000..83d9b9b --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactory.php @@ -0,0 +1,138 @@ +parseRequest($message); + + if (!$parsed) { + return false; + } + + $request = $this->fromParts($parsed['method'], $parsed['request_url'], + $parsed['headers'], $parsed['body'], $parsed['protocol'], + $parsed['version']); + + // EntityEnclosingRequest adds an "Expect: 100-Continue" header when + // using a raw request body for PUT or POST requests. This factory + // method should accurately reflect the message, so here we are + // removing the Expect header if one was not supplied in the message. + if (!isset($parsed['headers']['Expect']) && !isset($parsed['headers']['expect'])) { + $request->removeHeader('Expect'); + } + + return $request; + } + + /** + * {@inheritdoc} + */ + public function fromParts($method, array $urlParts, $headers = null, $body = null, $protocol = 'HTTP', $protocolVersion = '1.1') + { + return $this->create($method, Url::buildUrl($urlParts, true), $headers, $body) + ->setProtocolVersion($protocolVersion); + } + + /** + * {@inheritdoc} + */ + public function create($method, $url, $headers = null, $body = null) + { + if ($method != 'POST' && $method != 'PUT' && $method != 'PATCH') { + $c = $this->requestClass; + $request = new $c($method, $url, $headers); + if ($body) { + $request->setResponseBody(EntityBody::factory($body)); + } + + return $request; + } + + $c = $this->entityEnclosingRequestClass; + $request = new $c($method, $url, $headers); + + if ($body) { + + $isChunked = (string) $request->getHeader('Transfer-Encoding') == 'chunked'; + + if ($method == 'POST' && (is_array($body) || $body instanceof Collection)) { + + // Normalize PHP style cURL uploads with a leading '@' symbol + $files = array(); + foreach ($body as $key => $value) { + if (is_string($value) && strpos($value, '@') === 0) { + $files[$key] = $value; + unset($body[$key]); + } + } + + // Add the fields if they are still present and not all files + if (count($body) > 0) { + $request->addPostFields($body); + } + // Add any files that were prefixed with '@' + if (!empty($files)) { + $request->addPostFiles($files); + } + + if ($isChunked) { + $request->setHeader('Transfer-Encoding', 'chunked'); + } + + } elseif (is_resource($body) || $body instanceof EntityBody) { + $request->setBody($body, (string) $request->getHeader('Content-Type'), $isChunked); + } else { + $request->setBody((string) $body, (string) $request->getHeader('Content-Type'), $isChunked); + } + } + + return $request; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php new file mode 100644 index 0000000..1c2fba1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Message/RequestFactoryInterface.php @@ -0,0 +1,59 @@ + 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + ); + + /** + * @var EntityBody The response body + */ + protected $body; + + /** + * @var string The reason phrase of the response (human readable code) + */ + protected $reasonPhrase; + + /** + * @var string The status code of the response + */ + protected $statusCode; + + /** + * @var string Response protocol + */ + protected $protocol = 'HTTP'; + + /** + * @var array Information about the request + */ + protected $info = array(); + + /** + * @var RequestInterface Request object that may or may not be set + */ + protected $request = null; + + /** + * @var array Cacheable response codes (see RFC 2616:13.4) + */ + protected $cacheResponseCodes = array(200, 203, 206, 300, 301, 410); + + /** + * Create a new Response based on a raw response message + * + * @param string $message Response message + * + * @return Response|bool Returns false on error + */ + public static function fromMessage($message) + { + $data = ParserRegistry::get('message')->parseResponse($message); + if (!$data) { + return false; + } + + // Always set the appropriate Content-Length + unset($data['headers']['Content-Length']); + unset($data['headers']['content-length']); + $data['headers']['Content-Length'] = strlen($data['body']); + + $response = new static($data['code'], $data['headers'], $data['body']); + $response->setProtocol($data['protocol'], $data['version']) + ->setStatus($data['code'], $data['reason_phrase']); + + return $response; + } + + /** + * Construct the response + * + * @param string $statusCode The response status code (e.g. 200, 404, etc) + * @param Collection|array $headers The response headers + * @param string|resource|EntityBody $body The body of the response + * + * @throws BadResponseException if an invalid response code is given + */ + public function __construct($statusCode, $headers = null, $body = null) + { + $this->setStatus($statusCode); + $this->params = new Collection(); + $this->body = EntityBody::factory($body ?: ''); + + if ($headers) { + if (!is_array($headers) && !($headers instanceof Collection)) { + throw new BadResponseException('Invalid headers argument received'); + } + foreach ($headers as $key => $value) { + $this->addHeaders(array($key => $value)); + } + } + } + + /** + * Convert the response object to a string + * + * @return string + */ + public function __toString() + { + return $this->getMessage(); + } + + /** + * Get the response entity body + * + * @param bool $asString Set to TRUE to return a string of the body rather + * than a full body object + * + * @return EntityBody|string + */ + public function getBody($asString = false) + { + return $asString ? (string) $this->body : $this->body; + } + + /** + * Set the protocol and protocol version of the response + * + * @param string $protocol Response protocol + * @param string $version Protocol version + * + * @return Response + */ + public function setProtocol($protocol, $version) + { + $this->protocol = $protocol; + $this->protocolVersion = $version; + + return $this; + } + + /** + * Get the protocol used for the response (e.g. HTTP) + * + * @return string + */ + public function getProtocol() + { + return $this->protocol ?: 'HTTP'; + } + + /** + * Get the HTTP protocol version + * + * @return string + */ + public function getProtocolVersion() + { + return $this->protocolVersion ?: '1.1'; + } + + /** + * Get a cURL transfer information + * + * @param string $key A single statistic to check + * + * @return array|string|null Returns all stats if no key is set, a single + * stat if a key is set, or null if a key is set and not found + * @link http://www.php.net/manual/en/function.curl-getinfo.php + */ + public function getInfo($key = null) + { + if ($key === null) { + return $this->info; + } elseif (array_key_exists($key, $this->info)) { + return $this->info[$key]; + } else { + return null; + } + } + + /** + * Set the transfer information + * + * @param array $info Array of cURL transfer stats + * + * @return Response + */ + public function setInfo(array $info) + { + $this->info = $info; + + return $this; + } + + /** + * Set the response status + * + * @param int $statusCode Response status code to set + * @param string $reasonPhrase Response reason phrase + * + * @return Response + * @throws BadResponseException when an invalid response code is received + */ + public function setStatus($statusCode, $reasonPhrase = '') + { + $this->statusCode = (int) $statusCode; + + if (!$reasonPhrase && array_key_exists($this->statusCode, self::$statusTexts)) { + $this->reasonPhrase = self::$statusTexts[$this->statusCode]; + } else { + $this->reasonPhrase = $reasonPhrase; + } + + return $this; + } + + /** + * Get the response status code + * + * @return integer + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Get the entire response as a string + * + * @return string + */ + public function getMessage() + { + $message = $this->getRawHeaders(); + + // Only include the body in the message if the size is < 2MB + $size = $this->body->getSize(); + if ($size < 2097152) { + $message .= (string) $this->body; + } + + return $message; + } + + /** + * Get the the raw message headers as a string + * + * @return string + */ + public function getRawHeaders() + { + $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n"; + $lines = $this->getHeaderLines(); + if (!empty($lines)) { + $headers .= implode("\r\n", $lines) . "\r\n"; + } + + return $headers . "\r\n"; + } + + /** + * Get the request object that is associated with this response + * + * @return null|Request\Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * Get the response reason phrase- a human readable version of the numeric + * status code + * + * @return string + */ + public function getReasonPhrase() + { + return $this->reasonPhrase; + } + + /** + * Get the Accept-Ranges HTTP header + * + * @return string Returns what partial content range types this + * server supports. + */ + public function getAcceptRanges() + { + return $this->getHeader('Accept-Ranges', true); + } + + /** + * Get the Age HTTP header + * + * @param bool $headerOnly Set to TRUE to only retrieve the + * Age header rather than calculating the age + * + * @return integer|null Returns the age the object has been in a proxy cache + * in seconds. + */ + public function getAge($headerOnly = false) + { + $age = $this->getHeader('Age', true); + + if (!$headerOnly && $age === null && $this->getDate()) { + $age = time() - strtotime($this->getDate()); + } + + return $age; + } + + /** + * Get the Allow HTTP header + * + * @return string|null Returns valid actions for a specified resource. To + * be used for a 405 Method not allowed. + */ + public function getAllow() + { + return $this->getHeader('Allow', true); + } + + /** + * Check if an HTTP method is allowed by checking the Allow response header + * + * @param string $method Method to check + * + * @return bool + */ + public function isMethodAllowed($method) + { + $allow = $this->getHeader('Allow'); + if ($allow) { + foreach (explode(',', $allow) as $allowable) { + if (!strcasecmp(trim($allowable), $method)) { + return true; + } + } + } + + return false; + } + + /** + * Get the Cache-Control HTTP header + * + * @return Header|null Returns a Header object that tells all caching + * mechanisms from server to client whether they may cache this object. + */ + public function getCacheControl() + { + return $this->getHeader('Cache-Control'); + } + + /** + * Get the Connection HTTP header + * + * @return string + */ + public function getConnection() + { + return $this->getHeader('Connection', true); + } + + /** + * Get the Content-Encoding HTTP header + * + * @return string|null Returns the type of encoding used on the data. One + * of compress, deflate, gzip, identity. + */ + public function getContentEncoding() + { + return $this->getHeader('Content-Encoding', true); + } + + /** + * Get the Content-Language HTTP header + * + * @return string|null Returns the language the content is in. + */ + public function getContentLanguage() + { + return $this->getHeader('Content-Language', true); + } + + /** + * Get the Content-Length HTTP header + * + * @return integer Returns the length of the response body in bytes + */ + public function getContentLength() + { + return (int) $this->getHeader('Content-Length', true); + } + + /** + * Get the Content-Location HTTP header + * + * @return string|null Returns an alternate location for the returned data + * (e.g /index.htm) + */ + public function getContentLocation() + { + return $this->getHeader('Content-Location', true); + } + + /** + * Get the Content-Disposition HTTP header + * + * @return string|null Returns the Content-Disposition header + */ + public function getContentDisposition() + { + return (string) $this->getHeader('Content-Disposition')->setGlue(';'); + } + + /** + * Get the Content-MD5 HTTP header + * + * @return string|null Returns a Base64-encoded binary MD5 sum of the + * content of the response. + */ + public function getContentMd5() + { + return $this->getHeader('Content-MD5', true); + } + + /** + * Get the Content-Range HTTP header + * + * @return string Returns where in a full body message this partial message + * belongs (e.g. bytes 21010-47021/47022). + */ + public function getContentRange() + { + return $this->getHeader('Content-Range', true); + } + + /** + * Get the Content-Type HTTP header + * + * @return string Returns the mime type of this content. + */ + public function getContentType() + { + return $this->getHeader('Content-Type', true); + } + + /** + * Checks if the Content-Type is of a certain type. This is useful if the + * Content-Type header contains charset information and you need to know if + * the Content-Type matches a particular type. + * + * @param string $type Content type to check against + * + * @return bool + */ + public function isContentType($type) + { + return stripos($this->getContentType(), $type) !== false; + } + + /** + * Get the Date HTTP header + * + * @return string|null Returns the date and time that the message was sent. + */ + public function getDate() + { + return $this->getHeader('Date', true); + } + + /** + * Get the ETag HTTP header + * + * @return string|null Returns an identifier for a specific version of a + * resource, often a Message digest. + */ + public function getEtag() + { + $etag = $this->getHeader('ETag', true); + + return $etag ? str_replace('"', '', $etag) : null; + } + + /** + * Get the Expires HTTP header + * + * @return string|null Returns the date/time after which the response is + * considered stale. + */ + public function getExpires() + { + return $this->getHeader('Expires', true); + } + + /** + * Get the Last-Modified HTTP header + * + * @return string|null Returns the last modified date for the requested + * object, in RFC 2822 format (e.g. Tue, 15 Nov 1994 12:45:26 GMT) + */ + public function getLastModified() + { + return $this->getHeader('Last-Modified', true); + } + + /** + * Get the Location HTTP header + * + * @return string|null Used in redirection, or when a new resource has been + * created. (e.g. http://www.w3.org/pub/WWW/People.html) + */ + public function getLocation() + { + return $this->getHeader('Location', true); + } + + /** + * Get the Pragma HTTP header + * + * @return Header|null Returns the implementation-specific headers that may + * have various effects anywhere along the request-response chain. + */ + public function getPragma() + { + return $this->getHeader('Pragma'); + } + + /** + * Get the Proxy-Authenticate HTTP header + * + * @return string|null Authentication to access the proxy (e.g. Basic) + */ + public function getProxyAuthenticate() + { + return $this->getHeader('Proxy-Authenticate', true); + } + + /** + * Get the Retry-After HTTP header + * + * @return int|null If an entity is temporarily unavailable, this + * instructs the client to try again after a specified period of time. + */ + public function getRetryAfter() + { + $time = $this->getHeader('Retry-After', true); + if ($time === null) { + return null; + } + + if (!is_numeric($time)) { + $time = strtotime($time) - time(); + } + + return (int) $time; + } + + /** + * Get the Server HTTP header + * + * @return string|null A name for the server + */ + public function getServer() + { + return $this->getHeader('Server', true); + } + + /** + * Get the Set-Cookie HTTP header + * + * @return Header|null An HTTP cookie. + */ + public function getSetCookie() + { + return $this->getHeader('Set-Cookie'); + } + + /** + * Get the Trailer HTTP header + * + * @return string|null The Trailer general field value indicates that the + * given set of header fields is present in the trailer of a message + * encoded with chunked transfer-coding. + */ + public function getTrailer() + { + return $this->getHeader('Trailer', true); + } + + /** + * Get the Transfer-Encoding HTTP header + * + * @return string|null The form of encoding used to safely transfer the + * entity to the user. Currently defined methods are: chunked + */ + public function getTransferEncoding() + { + return $this->getHeader('Transfer-Encoding', true); + } + + /** + * Get the Vary HTTP header + * + * @return string|null Tells downstream proxies how to match future request + * headers to decide whether the cached response can be used rather + * than requesting a fresh one from the origin server. + */ + public function getVary() + { + return $this->getHeader('Vary', true); + } + + /** + * Get the Via HTTP header + * + * @return string|null Informs the client of proxies through which the + * response was sent. (e.g. 1.0 fred, 1.1 nowhere.com (Apache/1.1)) + */ + public function getVia() + { + return $this->getHeader('Via', true); + } + + /** + * Get the Warning HTTP header + * + * @return string|null A general warning about possible problems with the + * entity body. (e.g. 199 Miscellaneous warning) + */ + public function getWarning() + { + return $this->getHeader('Warning', true); + } + + /** + * Get the WWW-Authenticate HTTP header + * + * @return string|null Indicates the authentication scheme that should be + * used to access the requested entity (e.g. Basic) + */ + public function getWwwAuthenticate() + { + return $this->getHeader('WWW-Authenticate', true); + } + + /** + * Checks if HTTP Status code is a Client Error (4xx) + * + * @return bool + */ + public function isClientError() + { + return substr(strval($this->statusCode), 0, 1) == '4'; + } + + /** + * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx) + * + * @return boolean + */ + public function isError() + { + return $this->isClientError() || $this->isServerError(); + } + + /** + * Checks if HTTP Status code is Information (1xx) + * + * @return bool + */ + public function isInformational() + { + return substr(strval($this->statusCode), 0, 1) == '1'; + } + + /** + * Checks if HTTP Status code is a Redirect (3xx) + * + * @return bool + */ + public function isRedirect() + { + return substr(strval($this->statusCode), 0, 1) == '3'; + } + + /** + * Checks if HTTP Status code is Server Error (5xx) + * + * @return bool + */ + public function isServerError() + { + return substr(strval($this->statusCode), 0, 1) == '5'; + } + + /** + * Checks if HTTP Status code is Successful (2xx | 304) + * + * @return bool + */ + public function isSuccessful() + { + return substr(strval($this->statusCode), 0, 1) == '2' || $this->statusCode == '304'; + } + + /** + * Set the request object associated with the response + * + * @param RequestInterface The request object used to generate the response + * + * @return Response + */ + public function setRequest(RequestInterface $request) + { + $this->request = $request; + + return $this; + } + + /** + * Check if the response can be cached + * + * @return bool Returns TRUE if the response can be cached or false if not + */ + public function canCache() + { + // Check if the response is cacheable based on the code + if (!in_array((int) $this->getStatusCode(), $this->cacheResponseCodes)) { + return false; + } + + // Make sure a valid body was returned and can be cached + if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable()) + && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) { + return false; + } + + // Never cache no-store resources (this is a private cache, so private + // can be cached) + if ($this->hasCacheControlDirective('no-store')) { + return false; + } + + return $this->isFresh() || $this->getFreshness() === null || $this->canValidate(); + } + + /** + * Gets the number of seconds from the current time in which this response + * is still considered fresh as specified in RFC 2616-13 + * + * @return int|null Returns the number of seconds + */ + public function getMaxAge() + { + // s-max-age, then max-age, then Expires + if ($age = $this->getCacheControlDirective('s-maxage')) { + return $age; + } + + if ($age = $this->getCacheControlDirective('max-age')) { + return $age; + } + + if ($this->getHeader('Expires')) { + return strtotime($this->getExpires()) - time(); + } + + return null; + } + + /** + * Check if the response is considered fresh. + * + * A response is considered fresh when its age is less than the freshness + * lifetime (maximum age) of the response. + * + * @return bool|null + */ + public function isFresh() + { + $fresh = $this->getFreshness(); + + return $fresh === null ? null : $this->getFreshness() > 0; + } + + /** + * Check if the response can be validated against the origin server using + * a conditional GET request. + * + * @return bool + */ + public function canValidate() + { + return $this->getEtag() || $this->getLastModified(); + } + + /** + * Get the freshness of the response by returning the difference of the + * maximum lifetime of the response and the age of the response + * (max-age - age). + * + * Freshness values less than 0 mean that the response is no longer fresh + * and is ABS(freshness) seconds expired. Freshness values of greater than + * zer0 is the number of seconds until the response is no longer fresh. + * A NULL result means that no freshness information is available. + * + * @return int + */ + public function getFreshness() + { + $maxAge = $this->getMaxAge(); + $age = $this->getAge(); + + return $maxAge && $age ? ($maxAge - $age) : null; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/AsyncPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/AsyncPlugin.php new file mode 100644 index 0000000..fc7e41c --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/AsyncPlugin.php @@ -0,0 +1,92 @@ + 'onBeforeSend', + 'request.exception' => 'onRequestTimeout', + 'request.sent' => 'onRequestSent', + 'curl.callback.progress' => 'onCurlProgress' + ); + } + + /** + * Event emitted before a request is sent. Ensure that progress callback + * are emitted from the curl handle's request mediator. + * + * @param Event $event + */ + public function onBeforeSend(Event $event) + { + // Ensure that progress callbacks are dispatched + $event['request']->getCurlOptions()->set('progress', true); + } + + /** + * Event emitted when a curl progress function is called. When the amount + * of data uploaded == the amount of data to upload OR any bytes have been + * downloaded, then time the request out after 1ms because we're done with + * transmitting the request, and tell curl not download a body. + * + * @param Event $event + */ + public function onCurlProgress(Event $event) + { + if (!$event['handle']) { + return; + } + + if ($event['downloaded'] || ($event['uploaded'] || $event['upload_size'] === $event['uploaded'])) { + $event['handle']->getOptions() + ->set(CURLOPT_TIMEOUT_MS, 1) + ->set(CURLOPT_NOBODY, true); + // Timeout after 1ms + curl_setopt($event['handle']->getHandle(), CURLOPT_TIMEOUT_MS, 1); + // Even if the response is quick, tell curl not to download the body + curl_setopt($event['handle']->getHandle(), CURLOPT_NOBODY, true); + } + } + + /** + * Event emitted when a curl exception occurs. Ignore the exception and + * set a mock response. + * + * @param Event $event + */ + public function onRequestTimeout(Event $event) + { + if ($event['exception'] instanceof CurlException) { + $event['request']->setResponse(new Response(200, array( + 'X-Guzzle-Async' => 'Did not wait for the response' + ))); + } + } + + /** + * Event emitted when a request completes because it took less than 1ms. + * Add an X-Guzzle-Async header to notify the caller that there is no + * body in the message. + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + // Let the caller know this was meant to be async + $event['request']->getResponse()->setHeader('X-Guzzle-Async', 'Did not wait for the response'); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CachePlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CachePlugin.php new file mode 100644 index 0000000..90b855c --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CachePlugin.php @@ -0,0 +1,365 @@ +adapter = $adapter; + $this->defaultLifetime = (int) $defaultLifetime; + $this->cached = new \SplObjectStorage(); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -255), + 'request.sent' => array('onRequestSent', 255) + ); + } + + /** + * Calculate the hash key of a request object + * + * @param RequestInterface $request Request to hash + * @param string $raw Set to TRUE to retrieve the un-encoded string for debugging + * + * @return string + */ + public function getCacheKey(RequestInterface $request, $raw = false) + { + // See if the key has already been calculated + $key = $request->getParams()->get('cache.key'); + + // Always recalculate when using the raw option + if (!$key || $raw) { + + // Generate the start of the key + $key = $request->getMethod() . '_' . $request->getScheme() . '_' . $request->getHost() . $request->getPath(); + $filterHeaders = array('Cache-Control'); + $filterQuery = array(); + + // Check to see how and if the key should be filtered + foreach (explode(';', $request->getParams()->get('cache.key_filter')) as $part) { + $pieces = array_map('trim', explode('=', $part)); + if (isset($pieces[1])) { + $remove = array_map('trim', explode(',', $pieces[1])); + if ($pieces[0] == 'header') { + $filterHeaders = array_merge($filterHeaders, $remove); + } elseif ($pieces[0] == 'query') { + $filterQuery = array_merge($filterQuery, $remove); + } + } + } + + // Use the filtered query string + $queryString = (string) $request->getQuery()->filter(function($key, $value) use ($filterQuery) { + return !in_array($key, $filterQuery); + }); + + // Use the filtered headers + $headerString = http_build_query($request->getHeaders()->map(function($key, $value) { + return count($value) == 1 ? $value[0] : $value; + })->filter(function($key, $value) use ($filterHeaders) { + return !in_array($key, $filterHeaders); + })->getAll()); + + if ($raw) { + $key = strtolower('gz_' . $key . $queryString . '_' . $headerString); + } else { + $key = strtolower('gz_' . md5($key . $queryString . '_' . $headerString)); + $request->getParams()->set('cache.key', $key); + } + } + + return $key; + } + + /** + * Check if a response in cache will satisfy the request before sending + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + + // Only cache cacheable requests + if ($cacheFilter = $request->getParams()->get('cache.filter_strategy')) { + if (!call_user_func($cacheFilter, $request)) { + return; + } + } elseif (!$request->canCache()) { + return; + } + + $hashKey = $this->getCacheKey($request); + $this->cached[$request] = $hashKey; + $cachedData = $this->adapter->fetch($hashKey); + + // If the cached data was found, then make the request into a + // manually set request + if ($cachedData) { + unset($this->cached[$request]); + $response = new Response($cachedData[0], $cachedData[1], $cachedData[2]); + $response->setHeader('Age', time() - strtotime($response->getDate() ?: 'now')); + if (!$response->hasHeader('X-Guzzle-Cache')) { + $response->setHeader('X-Guzzle-Cache', "key={$hashKey}"); + } + + // Validate that the response satisfies the request + if ($this->canResponseSatisfyRequest($request, $response)) { + $request->setResponse($response); + } + } + } + + /** + * If possible, store a response in cache after sending + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + + if ($response->canCache() && isset($this->cached[$request])) { + + $cacheKey = $this->cached[$request]; + unset($this->cached[$request]); + + if ($response->isSuccessful()) { + if ($request->getParams()->get('cache.override_ttl')) { + $lifetime = $request->getParams()->get('cache.override_ttl'); + $response->setHeader('X-Guzzle-Cache', "key={$cacheKey}, ttl={$lifetime}"); + } else { + $lifetime = $response->getMaxAge(); + } + $this->saveCache($cacheKey, $response, $lifetime); + } + } + } + + /** + * Revalidate a cached response + * + * @param RequestInterface $request Request to revalidate + * @param Response $response Response to revalidate + * + * @return bool + */ + public function revalidate(RequestInterface $request, Response $response) + { + static $replaceHeaders = array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'); + + $revalidate = clone $request; + $revalidate->getEventDispatcher()->removeSubscriber($this); + $revalidate->removeHeader('Pragma') + ->removeHeader('Cache-Control') + ->setHeader('If-Modified-Since', $response->getDate()); + + if ($response->getEtag()) { + $revalidate->setHeader('If-None-Match', '"' . $response->getEtag() . '"'); + } + + try { + + $validateResponse = $revalidate->send(); + if ($validateResponse->getStatusCode() == 200) { + // The server does not support validation, so use this response + $request->setResponse($validateResponse); + // Store this response in cache if possible + if ($validateResponse->canCache()) { + $this->saveCache($this->getCacheKey($request), $validateResponse, $validateResponse->getMaxAge()); + } + + return false; + } + + if ($validateResponse->getStatusCode() == 304) { + // Make sure that this response has the same ETage + if ($validateResponse->getEtag() != $response->getEtag()) { + return false; + } + // Replace cached headers with any of these headers from the + // origin server that might be more up to date + $modified = false; + foreach ($replaceHeaders as $name) { + if ($validateResponse->hasHeader($name)) { + $modified = true; + $response->setHeader($name, $validateResponse->getHeader($name)); + } + } + // Store the updated response in cache + if ($modified && $response->canCache()) { + $this->saveCache($this->getCacheKey($request), $response, $response->getMaxAge()); + } + + return true; + } + + } catch (BadResponseException $e) { + + // 404 errors mean the resource no longer exists, so remove from + // cache, and prevent an additional request by throwing the exception + if ($e->getResponse()->getStatusCode() == 404) { + $this->adapter->delete($this->getCacheKey($request)); + throw $e; + } + } + + // Other exceptions encountered in the revalidation request are ignored + // in hopes that sending a request to the origin server will fix it + return false; + } + + /** + * Check if a cache response satisfies a request's caching constraints + * + * @param RequestInterface $request Request to validate + * @param Response $response Response to validate + * + * @return bool + */ + public function canResponseSatisfyRequest(RequestInterface $request, Response $response) + { + $responseAge = $response->getAge(); + + // Check the request's max-age header against the age of the response + if ($request->hasCacheControlDirective('max-age') && + $responseAge > $request->getCacheControlDirective('max-age')) { + return false; + } + + // Check the response's max-age header + if ($response->isFresh() === false) { + $maxStale = $request->getCacheControlDirective('max-stale'); + if (null !== $maxStale) { + if ($maxStale !== true && $response->getFreshness() < (-1 * $maxStale)) { + return false; + } + } elseif ($responseAge > $response->getCacheControlDirective('max-age')) { + return false; + } + } + + // Only revalidate GET requests + if ($request->getMethod() == RequestInterface::GET) { + // Check if the response must be validated against the origin server + if ($request->getHeader('Pragma') == 'no-cache' || + $request->hasCacheControlDirective('no-cache') || + $request->hasCacheControlDirective('must-revalidate') || + $response->hasCacheControlDirective('must-revalidate') || + $response->hasCacheControlDirective('no-cache')) { + // no-cache: When no parameters are present, always revalidate + // When parameters are present in no-cache and the request includes + // those same parameters, then the response must re-validate + // I'll need an example of what fields look like in order to + // implement a smarter version of no-cache + + // Requests can decline to revalidate against the origin server + // by setting the cache.revalidate param to one of: + // never - To never revalidate and always contact the origin server + // skip - To skip revalidation and just use what is in cache + switch ($request->getParams()->get('cache.revalidate')) { + case 'never': + return false; + case 'skip': + return true; + default: + return $this->revalidate($request, $response); + } + } + } + + return true; + } + + /** + * Save data to the cache adapter + * + * @param string $key The cache key + * @param Response $response The response to cache + * @param int $lifetime Amount of seconds to cache + * + * @return int Returns the lifetime of the cached data + */ + protected function saveCache($key, Response $response, $lifetime = null) + { + // If the data is cacheable, then save it to the cache adapter + if ($lifetime = $lifetime ?: $this->defaultLifetime) { + // Remove excluded headers from the response (see RFC 2616:13.5.1) + foreach ($this->excludeResponseHeaders as $header) { + $response->removeHeader($header); + } + // Add a Date header to the response if none is set (for validation) + if (!$response->getDate()) { + $response->setHeader('Date', Utils::getHttpDate('now')); + } + $this->adapter->save( + $key, + array($response->getStatusCode(), $response->getHeaders(), $response->getBody(true)), + $lifetime + ); + } + + return $lifetime; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CookiePlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CookiePlugin.php new file mode 100644 index 0000000..dc5d6ef --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CookiePlugin.php @@ -0,0 +1,87 @@ +cookieJar = $cookieJar; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', 100), + 'request.sent' => array('onRequestSent', 100), + 'request.receive.status_line' => 'onRequestReceiveStatusLine' + ); + } + + /** + * Get the cookie cookieJar + * + * @return CookieJarInterface + */ + public function getCookieJar() + { + return $this->cookieJar; + } + + /** + * Add cookies before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + if (!$request->getParams()->get('cookies.disable')) { + $request->removeHeader('Cookie'); + // Find cookies that match this request + foreach ($this->cookieJar->getMatchingCookies($request) as $cookie) { + $request->addCookie($cookie->getName(), $cookie->getValue()); + } + } + } + + /** + * Extract cookies from a sent request + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $this->cookieJar->addCookiesFromResponse($event['response']); + } + + /** + * Extract cookies from a redirect response + * + * @param Event $event + */ + public function onRequestReceiveStatusLine(Event $event) + { + if ($event['previous_response']) { + $this->cookieJar->addCookiesFromResponse($event['previous_response']); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CurlAuthPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CurlAuthPlugin.php new file mode 100644 index 0000000..903e729 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/CurlAuthPlugin.php @@ -0,0 +1,49 @@ +username = $username; + $this->password = $password; + $this->scheme = $scheme; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array('client.create_request' => array('onRequestCreate', 255)); + } + + /** + * Add basic auth + * + * @param Event $event + */ + public function onRequestCreate(Event $event) + { + $event['request']->setAuth($this->username, $this->password, $this->scheme); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/ExponentialBackoffPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/ExponentialBackoffPlugin.php new file mode 100644 index 0000000..004172a --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/ExponentialBackoffPlugin.php @@ -0,0 +1,270 @@ +setMaxRetries($maxRetries); + $this->setFailureCodes($failureCodes); + + if (!$delayFunction) { + $this->delayClosure = array($this, 'calculateWait'); + } else { + $this->delayClosure = $delayFunction; + } + + // @codeCoverageIgnoreStart + if (!self::$defaultFailureCodesHash) { + self::$defaultFailureCodesHash = array_fill_keys(self::$defaultFailureCodes, 1); + } + // @codeCoverageIgnoreEnd + } + + /** + * Get a default array of codes and cURL errors to retry + * + * @return array + */ + public static function getDefaultFailureCodes() + { + return self::$defaultFailureCodes; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + 'request.sent' => 'onRequestSent', + 'request.exception' => 'onRequestSent', + CurlMultiInterface::POLLING_REQUEST => 'onRequestPoll' + ); + } + + /** + * Set the maximum number of retries the plugin should use before failing + * the request + * + * @param integer $maxRetries The maximum number of retries. + * + * @return ExponentialBackoffPlugin + */ + public function setMaxRetries($maxRetries) + { + $this->maxRetries = max(0, (int) $maxRetries); + + return $this; + } + + /** + * Get the maximum number of retries the plugin will attempt + * + * @return integer + */ + public function getMaxRetries() + { + return $this->maxRetries; + } + + /** + * Get the HTTP response codes that should be retried using truncated + * exponential backoff + * + * @return array + */ + public function getFailureCodes() + { + return array_keys($this->failureCodes); + } + + /** + * Set the HTTP response codes that should be retried using truncated + * exponential backoff + * + * @param mixed $codes Array of HTTP response codes or PHP callable + * + * @return ExponentialBackoffPlugin + */ + public function setFailureCodes($codes = null) + { + // Use the default failure codes if no value was provided + $this->failureCodes = $codes ?: static::getDefaultFailureCodes(); + // Determine if the passed failure codes are a PHP callable + $this->callableFailureCodes = is_callable($this->failureCodes); + + // Use a hash of codes so that it is faster to lookup errors + if (!$this->callableFailureCodes) { + $this->failureCodes = array_fill_keys($this->failureCodes, 1); + } + + return $this; + } + + /** + * Determine how long to wait using truncated exponential backoff + * + * @param int $retries Number of retries so far + * + * @return int + */ + public function calculateWait($retries) + { + return (int) pow(2, $retries); + } + + /** + * Called when a request has been sent and isn't finished processing + * + * @param Event $event + */ + public function onRequestSent(Event $event) + { + $request = $event['request']; + $response = $event['response']; + $exception = $event['exception']; + $retry = null; + $failureCodes = $this->failureCodes; + + if ($this->callableFailureCodes) { + // Use a callback to determine if the request should be retried + $retry = call_user_func($this->failureCodes, $request, $response, $exception); + // If null is returned, then use the default check + if ($retry === null) { + $failureCodes = self::$defaultFailureCodesHash; + } + } + + // If a retry method hasn't decided what to do yet, then use the default check + if ($retry === null) { + if ($exception && $exception instanceof CurlException) { + // Handle cURL exceptions + $retry = isset($failureCodes[$exception->getErrorNo()]); + } elseif ($response) { + $retry = isset($failureCodes[$response->getStatusCode()]) || + isset($failureCodes[$response->getReasonPhrase()]); + } + } + + if ($retry) { + $this->retryRequest($request); + } + } + + /** + * Called when a request is polling in the curl multi object + * + * @param Event $event + */ + public function onRequestPoll(Event $event) + { + $request = $event['request']; + $delay = $request->getParams()->get(self::DELAY_PARAM); + + // If the duration of the delay has passed, retry the request using the pool + if (null !== $delay && microtime(true) >= $delay) { + // Remove the request from the pool and then add it back again. + // This is required for cURL to know that we want to retry sending + // the easy handle. + $request->getParams()->remove(self::DELAY_PARAM); + // Rewind the request body if possible + if ($request instanceof EntityEnclosingRequestInterface) { + $request->getBody()->seek(0); + } + $multi = $event['curl_multi']; + $multi->remove($request); + $multi->add($request, true); + } + } + + /** + * Trigger a request to retry + * + * @param RequestInterface $request Request to retry + */ + protected function retryRequest(RequestInterface $request) + { + $params = $request->getParams(); + $retries = ((int) $params->get(self::RETRY_PARAM)) + 1; + $params->set(self::RETRY_PARAM, $retries); + + // If this request has been retried too many times, then throw an exception + if ($retries <= $this->maxRetries) { + // Calculate how long to wait until the request should be retried + $delay = call_user_func($this->delayClosure, $retries, $request); + $delayTime = microtime(true) + $delay; + // Send the request again + $request->setState(RequestInterface::STATE_TRANSFER); + $params->set(self::DELAY_PARAM, $delay); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/HistoryPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/HistoryPlugin.php new file mode 100644 index 0000000..b5a0746 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/HistoryPlugin.php @@ -0,0 +1,135 @@ + 'onRequestComplete'); + } + + /** + * Add a request to the history + * + * @param RequestInterface $request Request to add + * + * @return HistoryPlugin + */ + public function add(RequestInterface $request) + { + if ($request->getResponse()) { + $this->requests[] = $request; + if (count($this->requests) > $this->getlimit()) { + array_shift($this->requests); + } + } + + return $this; + } + + /** + * Set the max number of requests to store + * + * @param int $limit Limit + * + * @return HistoryPlugin + */ + public function setLimit($limit) + { + $this->limit = (int) $limit; + + return $this; + } + + /** + * Get the request limit + * + * @return int + */ + public function getLimit() + { + return $this->limit; + } + + /** + * Get the requests in the history + * + * @return ArrayIterator + */ + public function getIterator() + { + return new \ArrayIterator($this->requests); + } + + /** + * Get the number of requests in the history + * + * @return int + */ + public function count() + { + return count($this->requests); + } + + /** + * Get the last request sent + * + * @return RequestInterface + */ + public function getLastRequest() + { + return end($this->requests); + } + + /** + * Get the last response in the history + * + * @return Response + */ + public function getLastResponse() + { + return $this->getLastRequest()->getResponse(); + } + + /** + * Clears the history + * + * @return HistoryPlugin + */ + public function clear() + { + $this->requests = array(); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function onRequestComplete(Event $event) + { + $this->add($event['request']); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/LogPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/LogPlugin.php new file mode 100644 index 0000000..a7a3292 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/LogPlugin.php @@ -0,0 +1,273 @@ +logAdapter = $logAdapter; + $this->hostname = gethostname(); + $this->setSettings($settings); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + 'curl.callback.write' => array('onCurlWrite', 255), + 'curl.callback.read' => array('onCurlRead', 255), + 'request.before_send' => array('onRequestBeforeSend', 255), + 'request.complete' => array('onRequestComplete', 255) + ); + } + + /** + * Change the log settings of the plugin + * + * @param int $settings Bitwise settings to control what's logged + * + * @return LogPlugin + */ + public function setSettings($settings) + { + $this->settings = $settings; + + return $this; + } + + /** + * Get the log adapter object + * + * @return LogAdapterInterface + */ + public function getLogAdapter() + { + return $this->logAdapter; + } + + /** + * Event triggered when curl data is read from a request + * + * @param Event $event + */ + public function onCurlRead(Event $event) + { + // Stream the request body to the log if the body is not repeatable + $request = $event['request']; + if ($request->getParams()->get('request_wire')) { + $request->getParams()->get('request_wire')->write($event['read']); + } + } + + /** + * Event triggered when curl data is written to a response + * + * @param Event $event + */ + public function onCurlWrite(Event $event) + { + // Stream the response body to the log if the body is not repeatable + $request = $event['request']; + if ($request->getParams()->get('response_wire')) { + $request->getParams()->get('response_wire')->write($event['write']); + } + } + + /** + * Called before a request is sent + * + * @param Event $event + */ + public function onRequestBeforeSend(Event $event) + { + $request = $event['request']; + // Ensure that curl IO events are emitted + $request->getParams()->set('curl.emit_io', true); + $request->getCurlOptions()->set('debug', true); + // We need to make special handling for content wiring and + // non-repeatable streams. + if ($this->settings & self::LOG_BODY) { + if ($request instanceof EntityEnclosingRequestInterface) { + if ($request->getBody() && (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())) { + // The body of the request cannot be recalled so + // logging the content of the request will need to + // be streamed using updates + $request->getParams()->set('request_wire', EntityBody::factory()); + } + } + if (!$request->isResponseBodyRepeatable()) { + // The body of the response cannot be recalled so + // logging the content of the response will need to + // be streamed using updates + $request->getParams()->set('response_wire', EntityBody::factory()); + } + } + } + + /** + * Triggers the actual log write when a request completes + * + * @param Event $event + */ + public function onRequestComplete(Event $event) + { + $this->log($event['request'], $event['response']); + } + + /** + * Log a message based on a request and response + * + * @param RequestInterface $request Request to log + * @param Response $response Response to log + */ + private function log(RequestInterface $request, Response $response = null) + { + $message = ''; + + if ($this->settings & self::LOG_CONTEXT) { + // Log common contextual information + $message = $request->getHost() . ' - "' . $request->getMethod() + . ' ' . $request->getResource() . ' ' + . strtoupper($request->getScheme()) . '/' + . $request->getProtocolVersion() . '"'; + + // If a response is set, then log additional contextual information + if ($response) { + $message .= sprintf(' - %s %s - %s %s %s', + $response->getStatusCode(), + $response->getContentLength() ?: 0, + $response->getInfo('total_time'), + $response->getInfo('speed_upload'), + $response->getInfo('speed_download') + ); + } + } + + // Check if we are logging anything that will come from cURL + if ($request->getParams()->get('curl_handle') && ($this->settings & self::LOG_DEBUG || $this->settings & self::LOG_HEADERS || $this->settings & self::LOG_BODY)) { + + // If context logging too, then add a new line for cleaner messages + if ($this->settings & self::LOG_CONTEXT) { + $message .= "\n"; + } + + // Filter cURL's verbose output based on config settings + $message .= $this->parseCurlLog($request); + + // Log the response body if the response is available + if ($this->settings & self::LOG_BODY && $response) { + if ($request->getParams()->get('response_wire')) { + $message .= (string) $request->getParams()->get('response_wire'); + } else { + $message .= $response->getBody(true); + } + } + } + + // Send the log message to the adapter, adding a category and host + $priority = $response && !$response->isSuccessful() ? LOG_ERR : LOG_DEBUG; + $this->logAdapter->log(trim($message), $priority, array( + 'category' => 'guzzle.request', + 'host' => $this->hostname + )); + } + + /** + * Parse cURL log messages + * + * @param RequestInterface $request Request that has a curl handle + * + * @return string + */ + protected function parseCurlLog(RequestInterface $request) + { + $message = ''; + $handle = $request->getParams()->get('curl_handle'); + $stderr = $handle->getStderr(true); + if ($stderr) { + rewind($stderr); + $addedBody = false; + while ($line = fgets($stderr)) { + // * - Debug | < - Downstream | > - Upstream + if ($line[0] == '*') { + if ($this->settings & self::LOG_DEBUG) { + $message .= $line; + } + } elseif ($this->settings & self::LOG_HEADERS) { + $message .= $line; + } + // Add the request body if needed + if ($this->settings & self::LOG_BODY) { + if (trim($line) == '' && $request instanceof EntityEnclosingRequestInterface) { + if ($request->getParams()->get('request_wire')) { + $message .= (string) $request->getParams()->get('request_wire') . "\r\n"; + } else { + $message .= (string) $request->getBody() . "\r\n"; + } + $addedBody = true; + } + } + } + } + + return $message; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/Md5ValidatorPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/Md5ValidatorPlugin.php new file mode 100644 index 0000000..778cf01 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/Md5ValidatorPlugin.php @@ -0,0 +1,102 @@ +contentLengthCutoff = $contentLengthCutoff; + $this->contentEncoded = $contentEncoded; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array('request.complete' => array('onRequestComplete', 255)); + } + + /** + * {@inheritdoc} + * @throws UnexpectedValueException + */ + public function onRequestComplete(Event $event) + { + $response = $event['response']; + + $contentMd5 = $response->getContentMd5(); + if (!$contentMd5) { + return; + } + + $contentEncoding = $response->getContentEncoding(); + if ($contentEncoding && !$this->contentEncoded) { + return false; + } + + // Make sure that the size of the request is under the cutoff size + $size = $response->getContentLength() ?: $response->getBody()->getSize(); + if (!$size || $size > $this->contentLengthCutoff) { + return; + } + + if (!$contentEncoding) { + $hash = $response->getBody()->getContentMd5(); + } elseif ($contentEncoding == 'gzip') { + $response->getBody()->compress('zlib.deflate'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } elseif ($contentEncoding == 'compress') { + $response->getBody()->compress('bzip2.compress'); + $hash = $response->getBody()->getContentMd5(); + $response->getBody()->uncompress(); + } else { + return; + } + + if ($contentMd5 !== $hash) { + throw new UnexpectedValueException(sprintf( + 'The response entity body may have been ' + . 'modified over the wire. The Content-MD5 ' + . 'received (%s) did not match the calculated ' + . 'MD5 hash (%s).', + $contentMd5, $hash + )); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/MockPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/MockPlugin.php new file mode 100644 index 0000000..9f34927 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/MockPlugin.php @@ -0,0 +1,199 @@ +temporary = $temporary; + if ($responses) { + foreach ($responses as $response) { + $this->addResponse($response); + } + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array('client.create_request' => 'onRequestCreate'); + } + + /** + * {@inheritdoc} + */ + public static function getAllEvents() + { + return array('mock.request'); + } + + /** + * Get a mock response from a file + * + * @param string $file File to retrieve a mock response from + * + * @return Response + * @throws InvalidArgumentException if the file is not found + */ + public static function getMockFile($path) + { + if (!file_exists($path)) { + throw new InvalidArgumentException('Unable to open mock file: ' . $path); + } + + return Response::fromMessage(file_get_contents($path)); + } + + /** + * Returns the number of remaining mock responses + * + * @return int + */ + public function count() + { + return count($this->queue); + } + + /** + * Add a response to the end of the queue + * + * @param string|Response $response Response object or path to response file + * + * @return MockPlugin + * @throws InvalidArgumentException if a string or Response is not passed + */ + public function addResponse($response) + { + if (!($response instanceof Response)) { + if (!is_string($response)) { + throw new InvalidArgumentException('Invalid response'); + } + $response = self::getMockFile($response); + } + + $this->queue[] = $response; + + return $this; + } + + /** + * Clear the queue + * + * @return MockPlugin + */ + public function clearQueue() + { + $this->queue = array(); + + return $this; + } + + /** + * Returns an array of mock responses remaining in the queue + * + * @return array + */ + public function getQueue() + { + return $this->queue; + } + + /** + * Check if this is a temporary plugin + * + * @return bool + */ + public function isTemporary() + { + return $this->temporary; + } + + /** + * Get a response from the front of the list and add it to a request + * + * @param RequestInterface $request Request to mock + * + * @return MockPlugin + */ + public function dequeue(RequestInterface $request) + { + $this->dispatch('mock.request', array( + 'plugin' => $this, + 'request' => $request + )); + $request->setResponse(array_shift($this->queue), true); + + return $this; + } + + /** + * Clear the array of received requests + */ + public function flush() + { + $this->received = array(); + } + + /** + * Get an array of requests that were mocked by this plugin + * + * @return array + */ + public function getReceivedRequests() + { + return $this->received; + } + + /** + * Called when a request completes + * + * @param Event $event + */ + public function onRequestCreate(Event $event) + { + if (!empty($this->queue)) { + $request = $event['request']; + $this->dequeue($request); + $this->received[] = $request; + // Detach the filter from the client so it's a one-time use + if ($this->temporary && empty($this->queue) && $request->getClient()) { + $request->getClient()->getEventDispatcher()->removeSubscriber($this); + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/OauthPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/OauthPlugin.php new file mode 100644 index 0000000..acc6591 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Plugin/OauthPlugin.php @@ -0,0 +1,170 @@ +config = Inspector::prepareConfig($config, array( + 'version' => '1.0', + 'consumer_key' => 'anonymous', + 'consumer_secret' => 'anonymous', + 'signature_method' => 'HMAC-SHA1', + 'signature_callback' => function($stringToSign, $key) { + return hash_hmac('sha1', $stringToSign, $key, true); + } + ), array( + 'signature_method', 'signature_callback', 'version', + 'consumer_key', 'consumer_secret', 'token', 'token_secret' + )); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + 'request.before_send' => array('onRequestBeforeSend', -1000) + ); + } + + /** + * Request before-send event handler + * + * @param Event $event Event received + */ + public function onRequestBeforeSend(Event $event) + { + $timestamp = $event['timestamp'] ?: time(); + + // Build Authorization header + $authString = 'OAuth '; + foreach(array( + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_nonce' => $this->generateNonce($event['request'], $timestamp), + 'oauth_signature' => $this->getSignature($event['request'], $timestamp), + 'oauth_signature_method' => $this->config['signature_method'], + 'oauth_timestamp' => $timestamp, + 'oauth_token' => $this->config['token'], + 'oauth_version' => $this->config['version'], + ) as $key => $val) { + $authString .= $key . '="' . urlencode($val) . '", '; + } + + // Add Authorization header + $event['request']->setHeader('Authorization', substr($authString, 0, -2)); + } + + /** + * Calculate signature for request + * + * @param RequestInterface $request Request to generate a signature for + * @param int $timestamp Timestamp to use for nonce + * + * @return string + */ + public function getSignature(RequestInterface $request, $timestamp) + { + $string = $this->getStringToSign($request, $timestamp); + $key = urlencode($this->config['consumer_secret']) . '&' . urlencode($this->config['token_secret']); + + return base64_encode(call_user_func($this->config['signature_callback'], $string, $key)); + } + + /** + * Calculate string to sign + * + * @param RequestInterface $request Request to generate a signature for + * @param int $timestamp Timestamp to use for nonce + * + * @return string + */ + public function getStringToSign(RequestInterface $request, $timestamp) + { + $params = new Collection(array( + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_nonce' => $this->generateNonce($request, $timestamp), + 'oauth_signature_method' => $this->config['signature_method'], + 'oauth_timestamp' => $timestamp, + 'oauth_token' => $this->config['token'], + 'oauth_version' => $this->config['version'] + )); + + // Add query string parameters + $params->merge($request->getQuery()); + // Add POST fields to signing string + if ($request instanceof EntityEnclosingRequestInterface && $request->getHeader('Content-Type') == 'application/x-www-form-urlencoded') { + $params->merge($request->getPostFields()); + } + + // Sort params + $params = $params->getAll(); + ksort($params); + + // Build signing string from combined params + $parameterString = array(); + foreach ($params as $key => $values) { + $key = rawurlencode($key); + $values = (array) $values; + sort($values); + foreach ($values as $value) { + if (is_bool($value)) { + $value = $value ? 'true' : 'false'; + } + $parameterString[] = $key . '=' . rawurlencode($value); + } + } + + $url = Url::factory($request->getUrl())->setQuery('')->setFragment(''); + + return strtoupper($request->getMethod()) . '&' + . rawurlencode($url) . '&' + . rawurlencode(implode('&', $parameterString)); + } + + /** + * Returns a Nonce Based on the Timestamp and URL. This will allow for + * multiple requests in parallel with the same exact timestamp to use + * separate nonce's. + * + * @param RequestInterface $request Request to generate a nonce for + * @param int $timestamp Timestamp to use for nonce + * + * @return string + */ + protected function generateNonce(RequestInterface $request, $timestamp) + { + return sha1($timestamp . $request->getUrl()); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php new file mode 100644 index 0000000..f598cf9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php @@ -0,0 +1,369 @@ +add($key, $value); + } + + return $q; + } + + /** + * Convert the query string parameters to a query string string + * + * @return string + */ + public function __toString() + { + if (empty($this->data)) { + return ''; + } + + $queryString = $this->prefix; + $firstValue = true; + + foreach ($this->encodeData($this->data, $this->encodeFields, $this->encodeValues) as $name => $value) { + $value = $value !== null ? (array) $value : array(false); + foreach ($value as $v) { + if (!$firstValue) { + $queryString .= $this->fieldSeparator; + } + $queryString .= $name; + if (is_string($v)) { + $queryString .= $this->valueSeparator . $v; + } + $firstValue = false; + } + } + + return $queryString; + } + + /** + * Aggregate multi-valued parameters using PHP style syntax + * + * @param string $key The name of the query string parameter + * @param array $value The values of the parameter + * @param bool $encodeFields Set to TRUE to encode field names + * @param bool $encodeValues Set to TRUE to encode values + * + * @return array Returns an array of the combined values + */ + public function aggregateUsingPhp($key, array $value, $encodeFields = false, $encodeValues = false) + { + $ret = array(); + + foreach ($value as $k => $v) { + + $k = $key . '[' . $k . ']'; + + if (is_array($v)) { + $ret = array_merge($ret, $this->aggregateUsingPhp($k, $v, $encodeFields, $encodeValues)); + } else { + if ($encodeFields) { + $k = rawurlencode($k); + } + $v = $encodeValues ? rawurlencode($v) : $v; + $ret[$k] = $v; + } + } + + return $ret; + } + + /** + * Aggregate multi-valued parameters by joining the values using a comma + * + * + * $q = new \Guzzle\Http\QueryString(array( + * 'value' => array(1, 2, 3) + * )); + * $q->setAggregateFunction(array($q, 'aggregateUsingComma')); + * echo $q; // outputs: ?value=1,2,3 + * + * + * @param string $key The name of the query string parameter + * @param array $value The values of the parameter + * @param bool $encodeFields Set to TRUE to encode field names + * @param bool $encodeValues Set to TRUE to encode values + * + * @return array Returns an array of the combined values + */ + public function aggregateUsingComma($key, array $value, $encodeFields = false, $encodeValues = false) + { + return array( + $encodeFields ? rawurlencode($key) : $key => $encodeValues + ? implode(',', array_map('rawurlencode', $value)) + : implode(',', $value) + ); + } + + /** + * Aggregate multi-valued parameters using duplicate values in a query string + * + * Example: http://test.com?q=1&q=2 + * + * @param string $key The name of the query string parameter + * @param array $value The values of the parameter + * @param bool $encodeFields Set to TRUE to encode field names + * @param bool $encodeValues Set to TRUE to encode values + * + * @return array Returns an array of the combined values + */ + public function aggregateUsingDuplicates($key, array $value, $encodeFields = false, $encodeValues = false) + { + return array( + $encodeFields ? rawurlencode($key) : $key => $encodeValues + ? array_map('rawurlencode', $value) + : $value + ); + } + + /** + * Get the query string field separator + * + * @return string + */ + public function getFieldSeparator() + { + return $this->fieldSeparator; + } + + /** + * Get the query string prefix + * + * @return string + */ + public function getPrefix() + { + return $this->prefix; + } + + /** + * Get the query string value separator + * + * @return string + */ + public function getValueSeparator() + { + return $this->valueSeparator; + } + + /** + * Returns whether or not field names are being urlencoded when converting + * the query string object to a string + * + * @return bool + */ + public function isEncodingFields() + { + return $this->encodeFields; + } + + /** + * Returns whether or not values are being urlencoded when converting + * the query string object to a string + * + * @return bool + */ + public function isEncodingValues() + { + return $this->encodeValues; + } + + /** + * Provide a function for combining multi-valued query string parameters + * into a single or multiple fields + * + * @param callback|null $callback A function or callback array that accepts + * a $key, $value, $encodeFields, and $encodeValues as arguments and + * returns an associative array containing the combined values. Set + * to null to remove any custom aggregator. + * + * @return QueryString + * + * @see \Guzzle\Http\QueryString::aggregateUsingComma() + */ + public function setAggregateFunction($callback) + { + $this->aggregator = $callback; + } + + /** + * Set whether or not field names should be urlencoded when converting + * the query string object to a string + * + * @param bool $encode Set to TRUE to encode field names, FALSE to not encode field names + * + * @return QueryString + */ + public function setEncodeFields($encode) + { + $this->encodeFields = $encode; + + return $this; + } + + /** + * Set whether or not field values should be urlencoded when converting + * the query string object to a string + * + * @param bool $encode Set to TRUE to encode field values, FALSE to not encode field values + * + * @return QueryString + */ + public function setEncodeValues($encode) + { + $this->encodeValues = $encode; + + return $this; + } + + /** + * Set the query string separator + * + * @param string $separator The query string separator that will separate fields + * + * @return QueryString + */ + public function setFieldSeparator($separator) + { + $this->fieldSeparator = $separator; + + return $this; + } + + /** + * Set the query string prefix + * + * @param string $prefix Prefix to use with the query string (e.g. '?') + * + * @return QueryString + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + + return $this; + } + + /** + * Set the query string value separator + * + * @param string $separator The query string separator that will separate values from fields + * + * @return QueryString + */ + public function setValueSeparator($separator) + { + $this->valueSeparator = $separator; + + return $this; + } + + /** + * Returns an array of url encoded field names and values + * + * @return array + */ + public function urlEncode() + { + return $this->encodeData($this->data, $this->encodeFields, $this->encodeValues); + } + + /** + * Url encode parameter data. + * + * If a parameter value is an array and no aggregator has been set, the + * values of the array will be converted into a PHP compatible form array. + * If an aggregator is set, the values will be converted using the + * aggregator function + * + * @param array $data The data to encode + * @param bool $encodeFields Toggle URL encoding of fields + * @param bool $encodeValues Toggle URL encoding of values + * + * @return array Returns an array of encoded values and keys + */ + protected function encodeData(array $data, $encodeFields = true, $encodeValues = true) + { + if (!$this->aggregator) { + $this->aggregator = array($this, 'aggregateUsingPhp'); + } + + $temp = array(); + foreach ($data as $key => &$value) { + if (is_array($value)) { + $encoded = $this->encodeData($value, $encodeFields, $encodeValues); + $temp = array_merge($temp, call_user_func_array($this->aggregator, array($key, $value, $encodeFields, $encodeValues))); + } else { + if ($encodeValues && is_string($value) || is_numeric($value)) { + $value = rawurlencode($value); + } + if ($encodeFields) { + $temp[rawurlencode($key)] = $value; + } else { + $temp[$key] = $value; + } + } + } + + return $temp; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php new file mode 100644 index 0000000..2bbb20b --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Url.php @@ -0,0 +1,546 @@ +parseUrl($url); + + // Convert the query string into a QueryString object + if ($parts['query']) { + $parts['query'] = QueryString::fromString($parts['query']); + } + + return new self($parts['scheme'], $parts['host'], $parts['user'], + $parts['pass'], $parts['port'], $parts['path'], $parts['query'], + $parts['fragment']); + } + + /** + * Build a URL from parse_url parts. The generated URL will be a relative + * URL if a scheme or host are not provided. + * + * @param array $parts Array of parse_url parts + * + * @return string + */ + public static function buildUrl(array $parts) + { + $url = $scheme = ''; + + if (isset($parts['scheme'])) { + $scheme = $parts['scheme']; + $url .= $scheme . '://'; + } + + if (isset($parts['host'])) { + + if (isset($parts['user'])) { + $url .= $parts['user']; + if (isset($parts['pass'])) { + $url .= ':' . $parts['pass']; + } + $url .= '@'; + } + + $url .= $parts['host']; + + // Only include the port if it is not the default port of the scheme + if (isset($parts['port']) + && !(($scheme == 'http' && $parts['port'] == 80) + || ($scheme == 'https' && $parts['port'] == 443))) { + $url .= ':' . $parts['port']; + } + } + + if (empty($parts['path'])) { + $url .= '/'; + } else { + if ($parts['path'][0] != '/') { + $url .= '/'; + } + $url .= $parts['path']; + } + + // Add the query string if present + if (!empty($parts['query'])) { + if ($parts['query'][0] != '?') { + $url .= array_key_exists('query_prefix', $parts) + ? $parts['query_prefix'] : '?'; + } + $url .= $parts['query']; + } + + // Ensure that # is only added to the url if fragment contains anything. + if (isset($parts['fragment']) && !empty($parts['fragment'])) { + $url .= '#' . $parts['fragment']; + } + + return $url; + } + + /** + * Create a new URL from URL parts + * + * @param string $scheme Scheme of the URL + * @param string $host Host of the URL + * @param string $username Username of the URL + * @param string $password Password of the URL + * @param int $port Port of the URL + * @param string $path Path of the URL + * @param QueryString|array|string $query Query string of the URL + * @param string $fragment Fragment of the URL + */ + public function __construct($scheme, $host, $username = null, $password = null, $port = null, $path = null, QueryString $query = null, $fragment = null) + { + $this->scheme = $scheme; + $this->host = $host; + $this->port = $port; + $this->username = $username; + $this->password = $password; + $this->fragment = $fragment; + $this->setQuery($query ?: new QueryString()); + + if ($path) { + $this->setPath($path); + } + } + + /** + * Clone the URL + */ + public function __clone() + { + $this->query = clone $this->query; + } + + /** + * Returns the URL as a URL string + * + * @return string + */ + public function __toString() + { + return self::buildUrl($this->getParts()); + } + + /** + * Get the parts of the URL as an array + * + * @return array + */ + public function getParts() + { + return array( + 'scheme' => $this->scheme, + 'user' => $this->username, + 'pass' => $this->password, + 'host' => $this->host, + 'port' => $this->port, + 'path' => $this->getPath(), + 'query' => (string) $this->query, + 'fragment' => $this->fragment, + 'query_prefix' => $this->query->getPrefix() + ); + } + + /** + * Set the host of the request. + * + * @param string $host Host to set (e.g. www.yahoo.com, yahoo.com) + * + * @return Url + */ + public function setHost($host) + { + if (strpos($host, ':') === false) { + $this->host = $host; + } else { + list($host, $port) = explode(':', $host); + $this->host = $host; + $this->setPort($port); + } + + return $this; + } + + /** + * Get the host part of the URL + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set the scheme part of the URL (http, https, ftp, etc) + * + * @param string $scheme Scheme to set + * + * @return Url + */ + public function setScheme($scheme) + { + $this->scheme = $scheme; + + return $this; + } + + /** + * Get the scheme part of the URL + * + * @return string + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Set the port part of the URL + * + * @param int $port Port to set + * + * @return Url + */ + public function setPort($port) + { + $this->port = $port; + + return $this; + } + + /** + * Get the port part of the URl. Will return the default port for a given + * scheme if a port has not explicitly been set. + * + * @return int|null + */ + public function getPort() + { + if ($this->port) { + return $this->port; + } elseif ($this->scheme == 'http') { + return 80; + } elseif ($this->scheme == 'https') { + return 443; + } + + return null; + } + + /** + * Set the path part of the URL + * + * @param array|string $path Path string or array of path segments + * + * @return Url + */ + public function setPath($path) + { + if (is_array($path)) { + $this->path = '/' . implode('/', $path); + } else { + $this->path = $path; + if ($this->path != '*' && substr($this->path, 0, 1) != '/') { + $this->path = '/' . $path; + } + } + + return $this; + } + + /** + * Normalize the URL so that double slashes and relative paths are removed + * + * @return Url + */ + public function normalizePath() + { + if ($this->path == '*') { + return $this; + } + + if ($this->path && $this->path != '/') { + + // Replace // and /./ with / + $this->path = str_replace(array('/./', '//'), '/', $this->path); + + // Remove trailing relative paths if possible + $segments = $this->getPathSegments(); + $totalSegments = count($segments); + $last = end($segments); + $trailingSlash = false; + if ($last === '') { + array_pop($segments); + $trailingSlash = true; + } + + while ($last == '..' || $last == '.') { + if ($last == '..') { + array_pop($segments); + $last = array_pop($segments); + } + if ($last == '.' || $last == '') { + $last = array_pop($segments); + } + } + + $this->path = implode('/', $segments); + if ($trailingSlash) { + $this->path .= '/'; + } + } + + // Must always start with a slash + if (substr($this->path, 0, 1) != '/') { + $this->path = '/' . $this->path; + } + + return $this; + } + + /** + * Add a relative path to the currently set path + * + * @param string $relativePath Relative path to add + * + * @return Url + */ + public function addPath($relativePath) + { + if (!$relativePath || $relativePath == '/') { + return $this; + } + + // Add a leading slash if needed + if ($relativePath[0] != '/') { + $relativePath = '/' . $relativePath; + } + + return $this->setPath(str_replace('//', '/', $this->getPath() . $relativePath)); + } + + /** + * Get the path part of the URL + * + * @return string + */ + public function getPath() + { + return $this->path ?: '/'; + } + + /** + * Get the path segments of the URL as an array + * + * @return array + */ + public function getPathSegments() + { + return array_slice(explode('/', $this->getPath()), 1); + } + + /** + * Set the password part of the URL + * + * @param string $password Password to set + * + * @return Url + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get the password part of the URL + * + * @return null|string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set the username part of the URL + * + * @param string $username Username to set + * + * @return Url + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get the username part of the URl + * + * @return null|string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get the query part of the URL as a QueryString object + * + * @return QueryString + */ + public function getQuery() + { + return $this->query; + } + + /** + * Set the query part of the URL + * + * @param QueryString|string|array $query Query to set + * + * @return Url + */ + public function setQuery($query) + { + if (is_string($query)) { + $output = null; + parse_str($query, $output); + $this->query = new QueryString($output); + } elseif (is_array($query)) { + $this->query = new QueryString($query); + } elseif ($query instanceof QueryString) { + $this->query = $query; + } + + return $this; + } + + /** + * Get the fragment part of the URL + * + * @return null|string + */ + public function getFragment() + { + return $this->fragment; + } + + /** + * Set the fragment part of the URL + * + * @param string $fragment Fragment to set + * + * @return Url + */ + public function setFragment($fragment) + { + $this->fragment = $fragment; + + return $this; + } + + /** + * Check if this is an absolute URL + * + * @return bool + */ + public function isAbsolute() + { + return $this->scheme && $this->host; + } + + /** + * Combine the URL with another URL. Every part of the second URL supersede + * the current URL if that part is specified. + * + * @param string $url Relative URL to combine with + * + * @return Url + * @throws InvalidArgumentException + */ + public function combine($url) + { + $absolutePath = $url[0] == '/'; + $url = self::factory($url); + + if ($url->getScheme()) { + $this->scheme = $url->getScheme(); + } + + if ($url->getHost()) { + $this->host = $url->getHost(); + } + + if ($url->getPort()) { + $this->port = $url->getPort(); + } + + if ($url->getUsername()) { + $this->username = $url->getUsername(); + } + + if ($url->getPassword()) { + $this->password = $url->getPassword(); + } + + if ($url->getFragment()) { + $this->fragment = $url->getFragment(); + } + + if ($absolutePath) { + // Replace the current URL and query if set + if ($url->getPath()) { + $this->path = $url->getPath(); + } + if (count($url->getQuery())) { + $this->query = clone $url->getQuery(); + } + } else { + // Append to the current path and query string + if ($url->getPath()) { + $this->addPath($url->getPath()); + } + if ($url->getQuery()) { + $this->query->merge($url->getQuery()); + } + } + + return $this; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/Utils.php b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Utils.php new file mode 100644 index 0000000..0b11464 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/Utils.php @@ -0,0 +1,51 @@ +get('version'), PHP_VERSION, + $curl->get('ssl_version') + ); + } + + return self::$userAgent; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json b/core/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json new file mode 100644 index 0000000..13b114f --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Http/composer.json @@ -0,0 +1,17 @@ +{ + "name": "guzzle/http", + "homepage": "http://guzzlephp.org/", + "description": "HTTP libraries used by Guzzle", + "keywords": ["http client", "http", "client", "Guzzle", "curl"], + "license": "MIT", + "require": { + "php": ">=5.3.2", + "guzzle/common": "*", + "guzzle/parser": "*", + "symfony/event-dispatcher": "2.*" + }, + "autoload": { + "psr-0": { "Guzzle\\Http": "" } + }, + "target-dir": "Guzzle/Http" +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php new file mode 100644 index 0000000..ad525e8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParser.php @@ -0,0 +1,93 @@ + 'Domain', + 'path' => 'Path', + 'max_age' => 'Max-Age', + 'expires' => 'Expires', + 'version' => 'Version', + 'secure' => 'Secure', + 'port' => 'Port', + 'discard' => 'Discard', + 'comment' => 'Comment', + 'comment_url' => 'Comment-Url', + 'http_only' => 'HttpOnly' + ); + + /** + * {@inheritdoc} + */ + public function parseCookie($cookie, $host = null, $path = null, $decode = true) + { + // Explode the cookie string using a series of semicolons + $pieces = array_filter(array_map('trim', explode(';', $cookie))); + + // The name of the cookie (first kvp) must include an equal sign. + if (empty($pieces) || !strpos($pieces[0], '=')) { + return false; + } + + // Create the default return array + $data = array_merge(array_fill_keys(array_keys(self::$cookieParts), null), array( + 'cookies' => array(), + 'data' => array(), + 'path' => $path ?: '/', + 'http_only' => false, + 'discard' => false, + 'domain' => $host + )); + $foundNonCookies = 0; + + // Add the cookie pieces into the parsed data array + foreach ($pieces as $part) { + + $cookieParts = explode('=', $part, 2); + $key = trim($cookieParts[0]); + + if (count($cookieParts) == 1) { + // Can be a single value (e.g. secure, httpOnly) + $value = true; + } else { + // Be sure to strip wrapping quotes + $value = trim($cookieParts[1], " \n\r\t\0\x0B\""); + if ($decode) { + $value = urldecode($value); + } + } + + // Only check for non-cookies when cookies have been found + if (!empty($data['cookies'])) { + foreach (self::$cookieParts as $mapValue => $search) { + if (!strcasecmp($search, $key)) { + $data[$mapValue] = $mapValue == 'port' ? array_map('trim', explode(',', $value)) : $value; + $foundNonCookies++; + continue 2; + } + } + } + + // If cookies have not yet been retrieved, or this value was + // not found in the cookie pieces array, treat as a cookie + // IF non-cookies have been parsed, then this isn't a cookie, + // but it's cookie data. Cookies must be first, followed by data. + $data[$foundNonCookies ? 'data' : 'cookies'][$key] = $value; + } + + // Calculate the expires date + if (!$data['expires'] && $data['max_age']) { + $data['expires'] = time() + (int) $data['max_age']; + } + + return $data; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php new file mode 100644 index 0000000..6337ce1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Cookie/CookieParserInterface.php @@ -0,0 +1,35 @@ + $requestUrl, + 'scheme' => 'http' + ); + + // Check for the Host header + if (isset($parts['headers']['Host'])) { + $urlParts['host'] = $parts['headers']['Host']; + } elseif (isset($parts['headers']['host'])) { + $urlParts['host'] = $parts['headers']['host']; + } else { + $urlParts['host'] = ''; + } + + if (false === strpos($urlParts['host'], ':')) { + $urlParts['port'] = ''; + } else { + $hostParts = explode(':', $urlParts['host']); + $urlParts['host'] = trim($hostParts[0]); + $urlParts['port'] = (int) trim($hostParts[1]); + if ($urlParts['port'] == 443) { + $urlParts['scheme'] = 'https'; + } + } + + // Check if a query is present + $path = $urlParts['path']; + $qpos = strpos($path, '?'); + if ($qpos) { + $urlParts['query'] = substr($path, $qpos + 1); + $urlParts['path'] = substr($path, 0, $qpos); + } else { + $urlParts['query'] = ''; + } + + return $urlParts; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php new file mode 100644 index 0000000..9452fca --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParser.php @@ -0,0 +1,113 @@ +parseMessage($message); + + // Parse the protocol and protocol version + list($protocol, $version) = explode('/', $parts['start_line'][2]); + + $parsed = array( + 'method' => strtoupper($parts['start_line'][0]), + 'protocol' => strtoupper($protocol), + 'version' => $version, + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts['start_line'][1], $parsed); + + return $parsed; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = $this->parseMessage($message); + list($protocol, $version) = explode('/', trim($parts['start_line'][0])); + + return array( + 'protocol' => $protocol, + 'version' => $version, + 'code' => $parts['start_line'][1], + 'reason_phrase' => $parts['start_line'][2], + 'headers' => $parts['headers'], + 'body' => $parts['body'] + ); + } + + /** + * Parse a message into parts + * + * @param string $message Message to parse + * + * @return array + */ + protected function parseMessage($message) + { + $startLine = null; + $headers = array(); + $body = ''; + + // Iterate over each line in the message, accounting for line endings + $lines = preg_split('/(\\r?\\n)/', $message, -1, PREG_SPLIT_DELIM_CAPTURE); + for ($i = 0, $totalLines = count($lines); $i < $totalLines; $i += 2) { + + $line = $lines[$i]; + + // If two line breaks were encountered, then this is the end of body + if (empty($line)) { + if ($i < $totalLines - 1) { + $body = implode('', array_slice($lines, $i + 2)); + } + break; + } + + // Parse message headers + if (!$startLine) { + + $startLine = explode(' ', $line, 3); + + } elseif (strpos($line, ':')) { + + $parts = explode(':', $line, 2); + $key = trim($parts[0]); + $value = isset($parts[1]) ? trim($parts[1]) : ''; + + if (!isset($headers[$key])) { + $headers[$key] = $value; + } elseif (!is_array($headers[$key])) { + $headers[$key] = array($headers[$key], $value); + } else { + $headers[$key][] = $value; + } + } + } + + return array( + 'start_line' => $startLine, + 'headers' => $headers, + 'body' => $body + ); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php new file mode 100644 index 0000000..0c11973 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Message/MessageParserInterface.php @@ -0,0 +1,27 @@ + $parts->requestMethod, + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'headers' => $parts->headers, + 'body' => $parts->body + ); + + $parsed['request_url'] = $this->getUrlPartsFromMessage($parts->requestUrl, $parsed); + + return $parsed; + } + + /** + * {@inheritdoc} + */ + public function parseResponse($message) + { + if (!$message) { + return false; + } + + $parts = http_parse_message($message); + + return array( + 'protocol' => 'HTTP', + 'version' => number_format($parts->httpVersion, 1), + 'code' => $parts->responseCode, + 'reason_phrase' => $parts->responseStatus, + 'headers' => $parts->headers, + 'body' => $parts->body + ); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php new file mode 100644 index 0000000..a524048 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/ParserRegistry.php @@ -0,0 +1,56 @@ + 'Guzzle\\Parser\\Message\\MessageParser', + 'cookie' => 'Guzzle\\Parser\\Cookie\\CookieParser', + 'url' => 'Guzzle\\Parser\\Url\\UrlParser', + 'uri_template' => 'Guzzle\\Parser\\UriTemplate\\UriTemplate', + ); + + /** + * Get a specific parser by handle name + * + * @param string $name Name of the parser to retrieve + * + * @return mixed|null Returns null if the parser is not found or cannot + * be instantiated + */ + public static function get($name) + { + if (!isset(self::$instances[$name])) { + if (!isset(self::$mapping[$name])) { + return null; + } + $class = self::$mapping[$name]; + self::$instances[$name] = new $class(); + } + + return self::$instances[$name]; + } + + /** + * Register a custom parser by name with the register + * + * @param string $name Name or handle of the parser to register + * @param mixed $parser Instantiated parser to register + */ + public static function set($name, $parser) + { + self::$instances[$name] = $parser; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php new file mode 100644 index 0000000..521b98a --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplate.php @@ -0,0 +1,249 @@ + true, '#' => true, '.' => true, '/' => true, ';' => true, + '?' => true, '&' => true + ); + + /** + * @var array Delimiters + */ + private static $delims = array(':', '/', '?', '#', '[', ']', '@', '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '='); + + /** + * @var array Percent encoded delimiters + */ + private static $delimsPct = array('%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40', '%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', '%3B', '%3D'); + + /** + * Expand the URI template using the supplied variables + * + * @param string $template URI Template to expand + * @param array $variables Variables to use with the expansion + * + * @return string Returns the expanded template + */ + public function expand($template, array $variables) + { + $this->template = $template; + $this->variables = $variables; + + // Check to ensure that the preg_* function is needed + if (false === strpos($this->template, '{')) { + return $this->template; + } + + return preg_replace_callback(self::$regex, array($this, 'expandMatch'), $this->template); + } + + /** + * Parse an expression into parts + * + * @param string $expression Expression to parse + * + * @return array Returns an associative array of parts + */ + private function parseExpression($expression) + { + // Check for URI operators + $operator = ''; + + if (isset(self::$operatorHash[$expression[0]])) { + $operator = $expression[0]; + $expression = substr($expression, 1); + } + + $values = explode(',', $expression); + foreach ($values as &$value) { + $value = trim($value); + $varspec = array(); + $substrPos = strpos($value, ':'); + if ($substrPos) { + $varspec['value'] = substr($value, 0, $substrPos); + $varspec['modifier'] = ':'; + $varspec['position'] = (int) substr($value, $substrPos + 1); + } elseif (substr($value, -1) == '*') { + $varspec['modifier'] = '*'; + $varspec['value'] = substr($value, 0, -1); + } else { + $varspec['value'] = (string) $value; + $varspec['modifier'] = ''; + } + $value = $varspec; + } + + return array( + 'operator' => $operator, + 'values' => $values + ); + } + + /** + * Process an expansion + * + * @param array $matches Matches met in the preg_replace_callback + * + * @return string Returns the replacement string + */ + private function expandMatch(array $matches) + { + static $rfc1738to3986 = array( + '+' => '%20', + '%7e' => '~' + ); + + $parsed = self::parseExpression($matches[1]); + $replacements = array(); + + $prefix = $parsed['operator']; + $joiner = $parsed['operator']; + $useQueryString = false; + if ($parsed['operator'] == '?') { + $joiner = '&'; + $useQueryString = true; + } elseif ($parsed['operator'] == '&') { + $useQueryString = true; + } elseif ($parsed['operator'] == '#') { + $joiner = ','; + } elseif ($parsed['operator'] == ';') { + $useQueryString = true; + } elseif ($parsed['operator'] == '' || $parsed['operator'] == '+') { + $joiner = ','; + $prefix = ''; + } + + foreach ($parsed['values'] as $value) { + + if (!array_key_exists($value['value'], $this->variables)) { + continue; + } + + $variable = $this->variables[$value['value']]; + $actuallyUseQueryString = $useQueryString; + $expanded = ''; + + if (is_array($variable)) { + + $isAssoc = $this->isAssoc($variable); + $kvp = array(); + foreach ($variable as $key => $var) { + + if ($isAssoc) { + $key = rawurlencode($key); + $isNestedArray = is_array($var); + } else { + $isNestedArray = false; + } + + if (!$isNestedArray) { + $var = rawurlencode($var); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $var = $this->decodeReserved($var); + } + } + + if ($value['modifier'] == '*') { + if ($isAssoc) { + if ($isNestedArray) { + // Nested arrays must allow for deeply nested structures + $var = strtr(http_build_query(array($key => $var)), $rfc1738to3986); + } else { + $var = $key . '=' . $var; + } + } elseif ($key > 0 && $actuallyUseQueryString) { + $var = $value['value'] . '=' . $var; + } + } + + $kvp[$key] = $var; + } + + if ($value['modifier'] == '*') { + $expanded = implode($joiner, $kvp); + if ($isAssoc) { + // Don't prepend the value name when using the explode + // modifier with an associative array + $actuallyUseQueryString = false; + } + } else { + if ($isAssoc) { + // When an associative array is encountered and the + // explode modifier is not set, then the result must + // be a comma separated list of keys followed by their + // respective values. + foreach ($kvp as $k => &$v) { + $v = $k . ',' . $v; + } + } + $expanded = implode(',', $kvp); + } + + } else { + if ($value['modifier'] == ':') { + $variable = substr($variable, 0, $value['position']); + } + $expanded = rawurlencode($variable); + if ($parsed['operator'] == '+' || $parsed['operator'] == '#') { + $expanded = $this->decodeReserved($expanded); + } + } + + if ($actuallyUseQueryString) { + if (!$expanded && $joiner != '&') { + $expanded = $value['value']; + } else { + $expanded = $value['value'] . '=' . $expanded; + } + } + + $replacements[] = $expanded; + } + + $ret = implode($joiner, $replacements); + if ($ret && $prefix) { + return $prefix . $ret; + } + + return $ret; + } + + /** + * Determines if an array is associative + * + * @param array $array Array to check + * + * @return bool + */ + private function isAssoc(array $array) + { + return (bool) count(array_filter(array_keys($array), 'is_string')); + } + + /** + * Removes percent encoding on reserved characters (used with + and # modifiers) + * + * @param string $string String to fix + * + * @return string + */ + private function decodeReserved($string) + { + return str_replace(self::$delimsPct, self::$delims, $string); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php new file mode 100644 index 0000000..d3ccc7d --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/UriTemplate/UriTemplateInterface.php @@ -0,0 +1,20 @@ +utf8 = $utf8; + } + + /** + * {@inheritdoc} + */ + public function parseUrl($url) + { + $parts = parse_url($url); + + // Need to handle query parsing specially for UTF-8 requirements + if ($this->utf8 && isset($parts['query'])) { + $queryPos = strpos($url, '?'); + if (isset($parts['fragment'])) { + $parts['query'] = substr($url, $queryPos + 1, strpos($url, '#') - $queryPos - 1); + } else { + $parts['query'] = substr($url, $queryPos + 1); + } + } + + $parts['scheme'] = isset($parts['scheme']) ? $parts['scheme'] : null; + $parts['host'] = isset($parts['host']) ? $parts['host'] : null; + $parts['path'] = isset($parts['path']) ? $parts['path'] : null; + $parts['port'] = isset($parts['port']) ? $parts['port'] : null; + $parts['query'] = isset($parts['query']) ? $parts['query'] : null; + $parts['user'] = isset($parts['user']) ? $parts['user'] : null; + $parts['pass'] = isset($parts['pass']) ? $parts['pass'] : null; + $parts['fragment'] = isset($parts['fragment']) ? $parts['fragment'] : null; + + return $parts; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php new file mode 100644 index 0000000..83b9e18 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Parser/Url/UrlParserInterface.php @@ -0,0 +1,21 @@ +=5.3.2" + }, + "autoload": { + "psr-0": { "Guzzle\\Parser": "" } + }, + "target-dir": "Guzzle/Parser" +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractFactory.php new file mode 100644 index 0000000..af47491 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/AbstractFactory.php @@ -0,0 +1,93 @@ +getCacheTtlKey($config); + + // Check if a cache was provided + if (isset($options['cache.adapter']) && is_string($config)) { + + $adapter = $options['cache.adapter']; + $ttl = isset($options[$cacheTtlKey]) ? $options[$cacheTtlKey] : 3600; + $cacheKey = 'guzzle' . crc32($config); + + // Check if the instantiated data is in the cache + $cached = $adapter->fetch($cacheKey); + if ($cached) { + return $cached; + } + } + + // Get the name of the class to instantiate for the type of data + $class = $this->getClassName($config); + if ($class) { + $result = $this->getFactory($class)->build($config, $options); + if ($adapter) { + $adapter->save($cacheKey, $result, $ttl); + } + return $result; + } + + $this->throwException(); + } + + /** + * Get the string used to hold cache TTL specific information for the factory + * + * @param mixed $config Config data being loaded + * + * @return string + */ + abstract protected function getCacheTtlKey($config); + + /** + * Throw an exception when the abstract factory cannot instantiate anything + * + * @param string $message Message for the exception + * + * @return string + * @throws Exception + */ + abstract protected function throwException($message = ''); + + /** + * Get the name of a class to instantiate for the type of data provided + * + * @param mixed $config Data to use to determine a class name + * + * @return mixed + */ + abstract protected function getClassName($config); + + /** + * Get a factory by object name, or retrieve previously a created factory + * + * @param string $class Name of the factory class to retrieve + * + * @return mixed + */ + protected function getFactory($class) + { + if (!isset($this->factories[$class])) { + $this->factories[$class] = new $class(); + } + + return $this->factories[$class]; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ArrayServiceBuilderFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ArrayServiceBuilderFactory.php new file mode 100644 index 0000000..643bcc1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ArrayServiceBuilderFactory.php @@ -0,0 +1,55 @@ + &$service) { + + $service['params'] = isset($service['params']) ? $service['params'] : array(); + + // Check if this client builder extends another client + if (!empty($service['extends'])) { + + // Make sure that the service it's extending has been defined + if (!isset($services[$service['extends']])) { + throw new ServiceNotFoundException($name . ' is trying to extend a non-existent service: ' . $service['extends']); + } + + $service['class'] = empty($service['class']) + ? $services[$service['extends']]['class'] : $service['class']; + + $extendsParams = isset($services[$service['extends']]['params']) ? $services[$service['extends']]['params'] : false; + if ($extendsParams) { + $service['params'] = array_merge($extendsParams, $service['params']); + } + } + + // Overwrite default values with global parameter values + if (!empty($options)) { + $service['params'] = array_merge($service['params'], $options); + } + + $service['class'] = !isset($service['class']) ? '' : str_replace('.', '\\', $service['class']); + } + + return new $class($services); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/JsonServiceBuilderFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/JsonServiceBuilderFactory.php new file mode 100644 index 0000000..f89a36d --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/JsonServiceBuilderFactory.php @@ -0,0 +1,28 @@ +loader) { + $this->loader = new JsonLoader(); + } + + return ServiceBuilder::factory($this->loader->parseJsonFile($config), $options); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php new file mode 100644 index 0000000..eeba63b --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilder.php @@ -0,0 +1,199 @@ +build($config, $globalParameters); + } + + /** + * Construct a new service builder + * + * @param array $serviceBuilderConfig Service configuration settings: + * - name: Name of the service + * - class: Client class to instantiate using a factory method + * - params: array of key value pair configuration settings for the builder + */ + public function __construct(array $serviceBuilderConfig) + { + $this->builderConfig = $serviceBuilderConfig; + } + + /** + * {@inheritdoc} + */ + public static function getAllEvents() + { + return array('service_builder.create_client'); + } + + /** + * Restores the service builder from JSON + * + * @param string $serialized JSON data to restore from + */ + public function unserialize($serialized) + { + $this->builderConfig = json_decode($serialized, true); + } + + /** + * Represents the service builder as a string + * + * @return array + */ + public function serialize() + { + return json_encode($this->builderConfig); + } + + /** + * Get a client using a registered builder + * + * @param string $name Name of the registered client to retrieve + * @param bool $throwAway Set to TRUE to not store the client for later retrieval from the ServiceBuilder + * + * @return ClientInterface + * @throws ServiceNotFoundException when a client cannot be found by name + */ + public function get($name, $throwAway = false) + { + if (!isset($this->builderConfig[$name])) { + throw new ServiceNotFoundException('No service is registered as ' . $name); + } + + if (!$throwAway && isset($this->clients[$name])) { + return $this->clients[$name]; + } + + // Convert references to the actual client + foreach ($this->builderConfig[$name]['params'] as $k => &$v) { + if (0 === strpos($v, '{') && strlen($v) - 1 == strrpos($v, '}')) { + $v = $this->get(trim(str_replace(array('{', '}'), '', $v))); + } + } + + $client = call_user_func( + array($this->builderConfig[$name]['class'], 'factory'), + $this->builderConfig[$name]['params'] + ); + + if (!$throwAway) { + $this->clients[$name] = $client; + } + + // Dispatch an event letting listeners know a client was created + $this->dispatch('service_builder.create_client', array( + 'client' => $client + )); + + return $client; + } + + /** + * Register a client by name with the service builder + * + * @param string $name Name of the client to register + * @param mixed $value Service to register + * + * @return ServiceBuilderInterface + */ + public function set($key, $service) + { + $this->builderConfig[$key] = $service; + + return $this; + } + + /** + * Register a client by name with the service builder + * + * @param string $offset Name of the client to register + * @param ClientInterface $value Client to register + */ + public function offsetSet($offset, $value) + { + $this->set($offset, $value); + } + + /** + * Remove a registered client by name + * + * @param string $offset Client to remove by name + */ + public function offsetUnset($offset) + { + unset($this->builderConfig[$offset]); + } + + /** + * Check if a client is registered with the service builder by name + * + * @param string $offset Name to check to see if a client exists + * + * @return bool + */ + public function offsetExists($offset) + { + return isset($this->builderConfig[$offset]); + } + + /** + * Get a registered client by name + * + * @param string $offset Registered client name to retrieve + * + * @return ClientInterface + */ + public function offsetGet($offset) + { + return $this->get($offset); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderAbstractFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderAbstractFactory.php new file mode 100644 index 0000000..32399f5 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderAbstractFactory.php @@ -0,0 +1,60 @@ +throwException( + "Unable to determine which factory to use based on the file extension of {$config}." + . " Valid file extensions are: .js, .json, .xml" + ); + } + } elseif ($config instanceof \SimpleXMLElement) { + $class = self::XML_FACTORY; + } else { + $this->throwException('Must pass a file name, array, or SimpleXMLElement'); + } + + return __NAMESPACE__ . '\\' . $class; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderFactoryInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderFactoryInterface.php new file mode 100644 index 0000000..704a6f0 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Builder/ServiceBuilderFactoryInterface.php @@ -0,0 +1,19 @@ +parseXmlFile($config), $options); + } + + /** + * Parse an XML document into an array + * + * @param string $filename Path to the file + * + * @return array + */ + protected function parseXmlFile($filename) + { + if ($filename instanceof \SimpleXMLElement) { + $xml = $filename; + } else { + $xml = new \SimpleXMLElement($filename, null, true); + $result = array( + 'services' => array() + ); + } + + // Account for old style service builder config files + if (isset($xml->services)) { + $services = $xml->services->service; + } else { + $services = $xml->clients->client; + } + + foreach ($services as $service) { + $row = array(); + foreach ($service->param as $param) { + $row[(string) $param->attributes()->name] = (string) $param->attributes()->value; + } + $result['services'][(string) $service->attributes()->name] = array( + 'class' => (string) $service->attributes()->class, + 'extends' => (string) $service->attributes()->extends, + 'params' => $row + ); + } + + // Include any XML files under the includes elements + if (isset($xml->includes->include)) { + + // You can only extend other services when using a file + if ($filename instanceof \SimpleXMLElement) { + throw new ServiceBuilderException('You can not extend other SimpleXMLElement services'); + } + + foreach ($xml->includes->include as $include) { + $path = (string) $include->attributes()->path; + if ($path[0] != DIRECTORY_SEPARATOR) { + $path = dirname($filename) . DIRECTORY_SEPARATOR . $path; + } + $result = array_merge_recursive($this->parseXmlFile($path), $result); + } + } + + // Grab the class name if it's set + if (isset($xml->class)) { + $result['class'] = str_replace('.', '\\', (string) $xml->class); + } + + return $result; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php new file mode 100644 index 0000000..45ae3b1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Client.php @@ -0,0 +1,335 @@ +magicMethodBehavior == self::MAGIC_CALL_DISABLED) { + throw new BadMethodCallException( + "Missing method {$method}. Enable magic calls to use magic methods with command names." + ); + } + + $command = $this->getCommand($method, isset($args[0]) ? $args[0] : array()); + + return $this->magicMethodBehavior == self::MAGIC_CALL_RETURN ? $command : $this->execute($command); + } + + /** + * Set the behavior for missing methods + * + * @param int $behavior Behavior to use when a missing method is called. + * - Client::MAGIC_CALL_DISABLED: Disable magic method calls + * - Client::MAGIC_CALL_EXECUTE: Execute commands and return the result + * - Client::MAGIC_CALL_RETURN: Instantiate and return the command + * + * @return Client + */ + public function setMagicCallBehavior($behavior) + { + $this->magicMethodBehavior = (int) $behavior; + + return $this; + } + + /** + * Get a command by name. First, the client will see if it has a service + * description and if the service description defines a command by the + * supplied name. If no dynamic command is found, the client will look for + * a concrete command class exists matching the name supplied. If neither + * are found, an InvalidArgumentException is thrown. + * + * @param string $name Name of the command to retrieve + * @param array $args Arguments to pass to the command + * + * @return CommandInterface + * @throws InvalidArgumentException if no command can be found by name + */ + public function getCommand($name, array $args = array()) + { + $command = $this->getCommandFactory()->factory($name, $args); + if (!$command) { + throw new InvalidArgumentException("Command was not found matching {$name}"); + } + $command->setClient($this); + $this->dispatch('client.command.create', array( + 'client' => $this, + 'command' => $command + )); + + return $command; + } + + /** + * Set the command factory used to create commands by name + * + * @param CommandFactoryInterface $factory Command factory + * + * @return Client + */ + public function setCommandFactory(CommandFactoryInterface $factory) + { + $this->commandFactory = $factory; + + return $this; + } + + /** + * Set the resource iterator factory associated with the client + * + * @param ResourceIteratorFactoryInterface $factory Resource iterator factory + * + * @return Client + */ + public function setResourceIteratorFactory(ResourceIteratorFactoryInterface $factory) + { + $this->resourceIteratorFactory = $factory; + + return $this; + } + + /** + * Get a resource iterator from the client. + * + * @param string|CommandInterface $command Command class or command name. + * @param array $commandOptions Command options used when creating commands. + * @param array $iteratorOptions Iterator options passed to the iterator when it is instantiated. + * + * @return ResourceIteratorInterface + */ + public function getIterator($command, array $commandOptions = null, array $iteratorOptions = array()) + { + if (!($command instanceof CommandInterface)) { + $command = $this->getCommand($command, $commandOptions ?: array()); + } + + return $this->getResourceIteratorFactory()->build($command, $iteratorOptions); + } + + /** + * Execute one or more commands + * + * @param CommandInterface|array $command Command or array of commands to execute + * + * @return mixed Returns the result of the executed command or an array of + * commands if an array of commands was passed. + * @throws InvalidArgumentException if an invalid command is passed + */ + public function execute($command) + { + if ($command instanceof CommandInterface) { + $command = array($command); + $singleCommand = true; + } elseif (is_array($command)) { + $singleCommand = false; + } else { + throw new InvalidArgumentException('Command must be a command or array of commands'); + } + + $requests = array(); + + foreach ($command as $c) { + $event = array('command' => $c->setClient($this)); + $this->dispatch('command.before_prepare', $event); + // Set the state to new if the command was previously executed + $requests[] = $c->prepare()->setState(RequestInterface::STATE_NEW); + $this->dispatch('command.before_send', $event); + } + + if ($singleCommand) { + $this->send($requests[0]); + } else { + $this->send($requests); + } + + foreach ($command as $c) { + $this->dispatch('command.after_send', array('command' => $c)); + } + + return $singleCommand ? end($command)->getResult() : $command; + } + + /** + * Set the service description of the client + * + * @param ServiceDescription $service Service description + * @param bool $updateFactory Set to FALSE to not update the service description based + * command factory if it is not already on the client. + * + * @return Client + */ + public function setDescription(ServiceDescription $service, $updateFactory = true) + { + $this->serviceDescription = $service; + + // Add the service description factory to the factory chain if it is not set + if ($updateFactory) { + // Convert non chain factories to a chain factory + if (!($this->getCommandFactory() instanceof CompositeFactory)) { + $this->commandFactory = new CompositeFactory(array($this->commandFactory)); + } + // Add a service description factory if one does not already exist + if (!$this->commandFactory->has('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory')) { + // Add the service description factory before the concrete factory + $this->commandFactory->add(new ServiceDescriptionFactory($service), 'Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory'); + } else { + // Update an existing service description factory + $factory = $this->commandFactory->find('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory'); + $factory->setServiceDescription($service); + } + } + + return $this; + } + + /** + * Get the service description of the client + * + * @return ServiceDescription|null + */ + public function getDescription() + { + return $this->serviceDescription; + } + + /** + * Set the inflector used with the client + * + * @param InflectorInterface $inflector Inflection object + * + * @return Client + */ + public function setInflector(InflectorInterface $inflector) + { + $this->inflector = $inflector; + + return $this; + } + + /** + * Get the inflector used with the client + * + * @return InflectorInterface + */ + public function getInflector() + { + if (!$this->inflector) { + $this->inflector = Inflector::getDefault(); + } + + return $this->inflector; + } + + /** + * Get the resource iterator factory associated with the client + * + * @return ResourceIteratorFactoryInterface + */ + protected function getResourceIteratorFactory() + { + if (!$this->resourceIteratorFactory) { + // Build the default resource iterator factory if one is not set + $clientClass = get_class($this); + $namespace = substr($clientClass, 0, strrpos($clientClass, '\\')) . '\\Model'; + $this->resourceIteratorFactory = new ResourceIteratorClassFactory($namespace); + } + + return $this->resourceIteratorFactory; + } + + /** + * Get the command factory associated with the client + * + * @return CommandFactoryInterface + */ + protected function getCommandFactory() + { + if (!$this->commandFactory) { + $this->commandFactory = CompositeFactory::getDefaultChain($this); + } + + return $this->commandFactory; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php new file mode 100644 index 0000000..ce906da --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/ClientInterface.php @@ -0,0 +1,111 @@ +apiCommand = $apiCommand ?: ApiCommand::fromCommand(get_class($this)); + $this->initConfig(); + + $headers = $this->get('headers'); + if (!$headers instanceof Collection) { + $this->set('headers', new Collection((array) $headers)); + } + + // You can set a command.on_complete option in your parameters as a + // convenience method for setting an onComplete function + $onComplete = $this->get('command.on_complete'); + if ($onComplete) { + $this->remove('command.on_complete'); + $this->setOnComplete($onComplete); + } + + $this->init(); + } + + /** + * Custom clone behavior + */ + public function __clone() + { + $this->request = null; + $this->result = null; + } + + /** + * Get the short form name of the command + * + * @return string + */ + public function getName() + { + return $this->apiCommand->getName(); + } + + /** + * Get the API command information about the command + * + * @return ApiCommand + */ + public function getApiCommand() + { + return $this->apiCommand; + } + + /** + * Specify a callable to execute when the command completes + * + * @param mixed $callable Callable to execute when the command completes. + * The callable must accept a {@see CommandInterface} object as the + * only argument. + * + * @return Command + * @throws InvalidArgumentException + */ + public function setOnComplete($callable) + { + if (!is_callable($callable)) { + throw new InvalidArgumentException('The onComplete function must be callable'); + } + + $this->onComplete = $callable; + + return $this; + } + + /** + * Execute the command and return the result + * + * @return mixed Returns the result of {@see AbstractCommand::execute} + * @throws CommandException if a client has not been associated with the command + */ + public function execute() + { + if (!$this->client) { + throw new CommandException('A Client object must be associated with the command before it can be executed from the context of the command.'); + } + + return $this->client->execute($this); + } + + /** + * Get the client object that will execute the command + * + * @return ClientInterface|null + */ + public function getClient() + { + return $this->client; + } + + /** + * Set the client object that will execute the command + * + * @param ClientInterface $client The client object that will execute the command + * + * @return Command + */ + public function setClient(ClientInterface $client) + { + $this->client = $client; + + return $this; + } + + /** + * Get the request object associated with the command + * + * @return RequestInterface + * @throws CommandException if the command has not been executed + */ + public function getRequest() + { + if (!$this->request) { + throw new CommandException('The command must be prepared before retrieving the request'); + } + + return $this->request; + } + + /** + * Get the response object associated with the command + * + * @return Response + * @throws CommandException if the command has not been executed + */ + public function getResponse() + { + if (!$this->isExecuted()) { + throw new CommandException('The command must be executed before retrieving the response'); + } + + return $this->request->getResponse(); + } + + /** + * Get the result of the command + * + * @return Response By default, commands return a Response + * object unless overridden in a subclass + * @throws CommandException if the command has not been executed + */ + public function getResult() + { + if (!$this->isExecuted()) { + throw new CommandException('The command must be executed before retrieving the result'); + } + + if (null === $this->result) { + $this->process(); + // Call the onComplete method if one is set + if ($this->onComplete) { + call_user_func($this->onComplete, $this); + } + } + + return $this->result; + } + + /** + * Set the result of the command + * + * @param mixed $result Result to set + * + * @return self + */ + public function setResult($result) + { + $this->result = $result; + + return $this; + } + + /** + * Returns TRUE if the command has been prepared for executing + * + * @return bool + */ + public function isPrepared() + { + return $this->request !== null; + } + + /** + * Returns TRUE if the command has been executed + * + * @return bool + */ + public function isExecuted() + { + return $this->request !== null && $this->request->getState() == 'complete'; + } + + /** + * Prepare the command for executing and create a request object. + * + * @return RequestInterface Returns the generated request + * @throws CommandException if a client object has not been set previously + * or in the prepare() + */ + public function prepare() + { + if (!$this->isPrepared()) { + if (!$this->client) { + throw new CommandException('A Client object must be associated with the command before it can be prepared.'); + } + + // Fail on missing required arguments, and change parameters via filters + $this->apiCommand->validate($this, $this->getInspector()); + $this->build(); + + // Add custom request headers set on the command + $headers = $this->get('headers'); + if ($headers && $headers instanceof Collection) { + foreach ($headers as $key => $value) { + $this->request->setHeader($key, $value); + } + } + } + + return $this->getRequest(); + } + + /** + * Get the object that manages the request headers that will be set on any + * outbound requests from the command + * + * @return Collection + */ + public function getRequestHeaders() + { + return $this->get('headers', new Collection()); + } + + /** + * Set the Inspector to use with the command + * + * @param Inspector $inspector Inspector to use for config validation + * + * @return AbstractCommand + */ + public function setInspector(Inspector $inspector) + { + $this->inspector = $inspector; + + return $this; + } + + /** + * Get the Inspector used with the Command + * + * @return Inspector + */ + protected function getInspector() + { + if (!$this->inspector) { + $this->inspector = Inspector::getInstance(); + } + + return $this->inspector; + } + + /** + * Initialize the command (hook to be implemented in subclasses) + */ + protected function init() {} + + /** + * Create the request object that will carry out the command + */ + abstract protected function build(); + + /** + * Create the result of the command after the request has been completed. + * + * Sets the result as the response by default. If the response is an XML + * document, this will set the result as a SimpleXMLElement. If the XML + * response is invalid, the result will remain the Response, not XML. + * If an application/json response is received, the result will automat- + * ically become an array. + */ + protected function process() + { + // Uses the response object by default + $this->result = $this->getRequest()->getResponse(); + + $contentType = $this->result->getContentType(); + + // Is the body an JSON document? If so, set the result to be an array + if (stripos($contentType, 'json') !== false) { + $body = trim($this->result->getBody(true)); + if ($body) { + $decoded = json_decode($body, true); + if (JSON_ERROR_NONE !== json_last_error()) { + throw new JsonException('The response body can not be decoded to JSON', json_last_error()); + } + + $this->result = $decoded; + } + } if (stripos($contentType, 'xml') !== false) { + // Is the body an XML document? If so, set the result to be a SimpleXMLElement + // If the body is available, then parse the XML + $body = trim($this->result->getBody(true)); + if ($body) { + // Silently allow parsing the XML to fail + try { + $xml = new \SimpleXMLElement($body); + $this->result = $xml; + } catch (\Exception $e) {} + } + } + } + + /** + * Prepare the default and static settings of the command + */ + protected function initConfig() + { + foreach ($this->apiCommand->getParams() as $name => $arg) { + $currentValue = $this->get($name); + $configValue = $arg->getValue($currentValue); + // If default or static values are set, then this should always be + // updated on the config object + if ($currentValue !== $configValue) { + $this->set($name, $configValue); + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/BatchCommandTransfer.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/BatchCommandTransfer.php new file mode 100644 index 0000000..ab81743 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/BatchCommandTransfer.php @@ -0,0 +1,82 @@ +batchSize = $batchSize; + } + + /** + * Creates batches by grouping commands by their associated client + * + * {@inheritdoc} + */ + public function createBatches(\SplQueue $queue) + { + $groups = new \SplObjectStorage(); + foreach ($queue as $item) { + if (!$item instanceof CommandInterface) { + throw new InvalidArgumentException('All items must implement Guzzle\Service\Command\CommandInterface'); + } + $client = $item->getClient(); + if (!$groups->contains($client)) { + $groups->attach($client, new \ArrayObject(array($item))); + } else { + $groups[$client]->append($item); + } + } + + $batches = array(); + foreach ($groups as $batch) { + $batches = array_merge($batches, array_chunk($groups[$batch]->getArrayCopy(), $this->batchSize)); + } + + return $batches; + } + + /** + * {@inheritdoc} + */ + public function transfer(array $batch) + { + if (empty($batch)) { + return; + } + + // Get the client of the first found command + $client = reset($batch)->getClient(); + + // Keep a list of all commands with invalid clients + $invalid = array_filter($batch, function ($command) use ($client) { + return $command->getClient() !== $client; + }); + + if (!empty($invalid)) { + throw new InconsistentClientTransferException($invalid); + } + + $client->execute($batch); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php new file mode 100644 index 0000000..941de5e --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/ClosureCommand.php @@ -0,0 +1,43 @@ +get('closure')) { + throw new InvalidArgumentException('A closure must be passed in the parameters array'); + } + } + + /** + * {@inheritdoc} + * + * @throws UnexpectedValueException If the closure does not return a request + */ + protected function build() + { + $closure = $this->get('closure'); + $this->request = $closure($this, $this->apiCommand); + + if (!$this->request || !$this->request instanceof RequestInterface) { + throw new UnexpectedValueException('Closure command did not return a RequestInterface object'); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php new file mode 100644 index 0000000..67bcba9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/CommandInterface.php @@ -0,0 +1,142 @@ +apiCommand->getUri()) { + $url = $this->getClient()->getBaseUrl(); + } else { + + // Get the path values and use the client config settings + $variables = $this->getClient()->getConfig()->getAll(); + foreach ($this->apiCommand->getParams() as $name => $arg) { + $configValue = $this->get($name); + if (is_scalar($configValue)) { + $variables[$name] = $arg->getPrepend() . $configValue . $arg->getAppend(); + } + } + + // Expand the URI template using the URI values + $uri = ParserRegistry::get('uri_template')->expand($this->apiCommand->getUri(), $variables); + + // Merge the client's base URL with the URI template + $url = Url::factory($this->getClient()->getBaseUrl()); + $url->combine($uri); + $url = (string) $url; + } + + // Inject path and base_url values into the URL + $this->request = $this->getClient()->createRequest($this->apiCommand->getMethod(), $url); + + // Add arguments to the request using the location attribute + foreach ($this->apiCommand->getParams() as $name => $arg) { + + $configValue = $this->get($name); + $location = $arg->getLocation(); + + if (!$configValue || !$location) { + continue; + } + + // Create the value based on prepend and append settings + if ($arg->getPrepend() || $arg->getAppend()) { + $value = $arg->getPrepend() . $configValue . $arg->getAppend(); + } else { + $value = $configValue; + } + + // If a location key mapping is set, then use it + $key = $arg->getLocationKey() ?: $name; + + // Add the parameter to the request + switch ($location) { + case 'body': + $this->request->setBody(EntityBody::factory($value)); + break; + case 'header': + $this->request->setHeader($key, $value); + break; + case 'query': + $this->request->getQuery()->set($key, $value); + break; + case 'post_field': + $this->request->setPostField($key, $value); + break; + case 'post_file': + if ($value instanceof PostFileInterface) { + $this->request->addPostFile($value); + } else { + $this->request->addPostFile($key, $value); + } + break; + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php new file mode 100644 index 0000000..9671f15 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/AliasFactory.php @@ -0,0 +1,46 @@ +client = $client; + $this->aliases = $aliases; + } + + /** + * {@inheritdoc} + */ + public function factory($name, array $args = array()) + { + if (isset($this->aliases[$name])) { + try { + return $this->client->getCommand($this->aliases[$name], $args); + } catch (InvalidArgumentException $e) { + return null; + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php new file mode 100644 index 0000000..9897b67 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/CompositeFactory.php @@ -0,0 +1,166 @@ +getDescription(); + if ($description instanceof ServiceDescription) { + $chain->add(new ServiceDescriptionFactory($description)); + } + + $chain->add(new ConcreteClassFactory($client)); + + return $chain; + } + + /** + * @param array $factories Array of command factories + */ + public function __construct(array $factories = array()) + { + $this->factories = $factories; + } + + /** + * Add a command factory to the chain + * + * @param FactoryInterface $factory Factory to add + * @param string|FactoryInterface $before Insert the new command factory before a command + * factory class or object matching a class name. + * + * @return CompositeFactory + */ + public function add(FactoryInterface $factory, $before = null) + { + $pos = null; + + if ($before) { + foreach ($this->factories as $i => $f) { + if ($before instanceof FactoryInterface) { + if ($f === $before) { + $pos = $i; + break; + } + } elseif (is_string($before)) { + if ($f instanceof $before) { + $pos = $i; + break; + } + } + } + } + + if ($pos === null) { + $this->factories[] = $factory; + } else { + array_splice($this->factories, $i, 0, array($factory)); + } + + return $this; + } + + /** + * Check if the chain contains a specific command factory + * + * @param FactoryInterface|string $factory Factory to check + * + * @return bool + */ + public function has($factory) + { + return (bool) $this->find($factory); + } + + /** + * Remove a specific command factory from the chain + * + * @param string|FactoryInterface $factory Factory to remove by name or instance + * + * @return CompositeFactory + */ + public function remove($factory = null) + { + if (!($factory instanceof FactoryInterface)) { + $factory = $this->find($factory); + } + + $this->factories = array_values(array_filter($this->factories, function($f) use ($factory) { + return $f !== $factory; + })); + + return $this; + } + + /** + * Get a command factory by class name + * + * @param string|FactoryInterface $factory Command factory class or instance + * + * @return null|FactoryInterface + */ + public function find($factory) + { + foreach ($this->factories as $f) { + if ($factory === $f || (is_string($factory) && $f instanceof $factory)) { + return $f; + } + } + } + + /** + * Create a command using the associated command factories + * + * @param string $name Name of the command + * @param array $args Command arguments + * + * @return CommandInterface + */ + public function factory($name, array $args = array()) + { + foreach ($this->factories as $factory) { + $command = $factory->factory($name, $args); + if ($command) { + return $command; + } + } + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->factories); + } + + /** + * {@inheritdoc} + */ + public function getIterator() + { + return new \ArrayIterator($this->factories); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php new file mode 100644 index 0000000..198b04c --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ConcreteClassFactory.php @@ -0,0 +1,55 @@ +client = $client; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + /** + * {@inheritdoc} + */ + public function factory($name, array $args = array()) + { + // Determine the class to instantiate based on the namespace of the + // current client and the default location of commands + $prefix = $this->client->getConfig('command.prefix'); + if (!$prefix) { + // The prefix can be specified in a factory method and is cached + $prefix = implode('\\', array_slice(explode('\\', get_class($this->client)), 0, -1)) . '\\Command\\'; + $this->client->getConfig()->set('command.prefix', $prefix); + } + + $class = $prefix . str_replace(' ', '\\', ucwords(str_replace('.', ' ', $this->inflector->camel($name)))); + + // Create the concrete command if it exists + if (class_exists($class)) { + return new $class($args); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php new file mode 100644 index 0000000..d9c6cf4 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/FactoryInterface.php @@ -0,0 +1,21 @@ +map = $map; + } + + /** + * {@inheritdoc} + */ + public function factory($name, array $args = array()) + { + if (isset($this->map[$name])) { + $class = $this->map[$name]; + + return new $class($args); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php new file mode 100644 index 0000000..462bc35 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Command/Factory/ServiceDescriptionFactory.php @@ -0,0 +1,75 @@ +setServiceDescription($description); + $this->inflector = $inflector; + } + + /** + * Change the service description used with the factory + * + * @param ServiceDescription $description Service description to use + * + * @return ServiceDescriptionFactory + */ + public function setServiceDescription(ServiceDescription $description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the service description + * + * @return ServiceDescription + */ + public function getServiceDescription() + { + return $this->description; + } + + /** + * {@inheritdoc} + */ + public function factory($name, array $args = array()) + { + $command = $this->description->getCommand($name); + + // If an inflector was passed, then attempt to get the command using + // snake_case inflection + if (!$command && $this->inflector) { + $command = $this->description->getCommand($this->inflector->snake($name)); + } + + if ($command) { + $class = $command->getConcreteClass(); + return new $class($args, $command); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ApiCommand.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ApiCommand.php new file mode 100644 index 0000000..d5b1526 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ApiCommand.php @@ -0,0 +1,397 @@ +name = isset($config['name']) ? trim($config['name']) : null; + $this->doc = isset($config['doc']) ? trim($config['doc']) : null; + $this->docUrl = isset($config['doc_url']) ? $config['doc_url'] : null; + $this->method = isset($config['method']) ? $config['method'] : null; + $this->uri = isset($config['uri']) ? $config['uri'] : ''; + $this->class = isset($config['class']) ? trim($config['class']) : self::DEFAULT_COMMAND_CLASS; + $this->resultType = isset($config['result_type']) ? $config['result_type'] : null; + $this->resultDoc = isset($config['result_doc']) ? $config['result_doc'] : null; + $this->deprecated = isset($config['deprecated']) && ($config['deprecated'] === 'true' || $config['deprecated'] === true); + + if (!empty($config['params'])) { + foreach ($config['params'] as $name => $param) { + if ($param instanceof ApiParam) { + $this->params[$name] = $param; + } elseif (is_array($param)) { + $param['name'] = $name; + $this->params[$name] = new ApiParam($param); + } + } + } + } + + /** + * Create an ApiCommand object from a class and its docblock + * + * The following is the format for @guzzle arguments: + * @guzzle argument_name [default="default value"] [required="true|false"] [type="registered constraint name"] [type_args=""] [doc="Description of argument"] + * Example: @guzzle my_argument default="hello" required="true" doc="Set the argument to control the widget..." + * + * @param string $className Name of the class + * + * @return ApiCommand + */ + public static function fromCommand($className) + { + if (!isset(self::$apiCommandCache[$className])) { + + $reflection = new \ReflectionClass($className); + + // Get all of the @guzzle annotations from the class + $matches = array(); + $params = array(); + preg_match_all('/' . self::GUZZLE_ANNOTATION . '\s+([A-Za-z0-9_\-\.]+)\s*([A-Za-z0-9]+=".+")*/', $reflection->getDocComment(), $matches); + + // Parse the docblock annotations + if (!empty($matches[1])) { + foreach ($matches[1] as $index => $match) { + // Add the matched argument to the array keys + $params[$match] = array(); + if (isset($matches[2])) { + // Break up the argument attributes by closing quote + foreach (explode('" ', $matches[2][$index]) as $part) { + $attrs = array(); + // Find the attribute and attribute value + preg_match('/([A-Za-z0-9]+)="(.+)"*/', $part, $attrs); + if (isset($attrs[1]) && isset($attrs[0])) { + // Sanitize the strings + if ($attrs[2][strlen($attrs[2]) - 1] == '"') { + $attrs[2] = substr($attrs[2], 0, strlen($attrs[2]) - 1); + } + $params[$match][$attrs[1]] = $attrs[2]; + } + } + } + $params[$match] = new ApiParam($params[$match]); + } + } + + + self::$apiCommandCache[$className] = new ApiCommand(array( + 'name' => str_replace('\\_', '.', Inflector::getDefault()->snake(substr($className, strpos($className, 'Command') + 8))), + 'class' => $className, + 'params' => $params + )); + } + + return self::$apiCommandCache[$className]; + } + + /** + * Get as an array + * + * @return true + */ + public function toArray() + { + return array( + 'name' => $this->name, + 'doc' => $this->doc, + 'doc_url' => $this->docUrl, + 'method' => $this->method, + 'uri' => $this->uri, + 'class' => $this->class, + 'params' => $this->params, + 'result_type' => $this->resultType, + 'result_doc' => $this->resultDoc, + 'deprecated' => $this->deprecated + ); + } + + /** + * Get the params of the command + * + * @return array + */ + public function getParams() + { + return $this->params; + } + + /** + * Get a single parameter of the command + * + * @param string $param Parameter to retrieve by name + * + * @return ApiParam|null + */ + public function getParam($param) + { + return isset($this->params[$param]) ? $this->params[$param] : null; + } + + /** + * Get the HTTP method of the command + * + * @return string|null + */ + public function getMethod() + { + return $this->method; + } + + /** + * Get the concrete command class that implements this command + * + * @return string + */ + public function getConcreteClass() + { + return $this->class; + } + + /** + * Get the name of the command + * + * @return string|null + */ + public function getName() + { + return $this->name; + } + + /** + * Get the documentation for the command + * + * @return string|null + */ + public function getDoc() + { + return $this->doc; + } + + /** + * Get the documentation URL of the command + * + * @return string|null + */ + public function getDocUrl() + { + return $this->docUrl; + } + + /** + * Get the type of data stored in the result of the command + * + * @return string|null + */ + public function getResultType() + { + return $this->resultType; + } + + /** + * Get the documentation specific to the result of the command + * + * @return string|null + */ + public function getResultDoc() + { + return $this->resultDoc; + } + + /** + * Get whether or not the command is deprecated + * + * @return bool + */ + public function isDeprecated() + { + return $this->deprecated; + } + + /** + * Get the URI that will be merged into the generated request + * + * @return string + */ + public function getUri() + { + return $this->uri; + } + + /** + * Validates that all required args are included in a config object, + * and if not, throws an InvalidArgumentException with a helpful error message. Adds + * default args to the passed config object if the parameter was not + * set in the config object. + * + * @param Collection $config Configuration settings + * + * @throws ValidationException when validation errors occur + */ + public function validate(Collection $config, Inspector $inspector = null) + { + $inspector = $inspector ?: Inspector::getInstance(); + $typeValidation = $inspector->getTypeValidation(); + $errors = array(); + + foreach ($this->params as $name => $arg) { + + $currentValue = $config->get($name); + $configValue = $arg->getValue($currentValue); + + // Inject configuration information into the config value + if ($configValue && is_string($configValue)) { + $configValue = $config->inject($configValue); + } + + // Ensure that required arguments are set + if ($arg->getRequired() && ($configValue === null || $configValue === '')) { + $errors[] = 'Requires that the ' . $name . ' argument be supplied.' . ($arg->getDoc() ? ' (' . $arg->getDoc() . ').' : ''); + continue; + } + + // Ensure that the correct data type is being used + if ($typeValidation && $configValue !== null && $argType = $arg->getType()) { + $validation = $inspector->validateConstraint($argType, $configValue, $arg->getTypeArgs()); + if ($validation !== true) { + $errors[] = $name . ': ' . $validation; + $config->set($name, $configValue); + continue; + } + } + + $configValue = $arg->filter($configValue); + + // Update the config value if it changed + if (!$configValue !== $currentValue) { + $config->set($name, $configValue); + } + + // Check the length values if validating data + $argMinLength = $arg->getMinLength(); + if ($argMinLength && strlen($configValue) < $argMinLength) { + $errors[] = 'Requires that the ' . $name . ' argument be >= ' . $arg->getMinLength() . ' characters.'; + } + + $argMaxLength = $arg->getMaxLength(); + if ($argMaxLength && strlen($configValue) > $argMaxLength) { + $errors[] = 'Requires that the ' . $name . ' argument be <= ' . $arg->getMaxLength() . ' characters.'; + } + } + + if (!empty($errors)) { + $e = new ValidationException('Validation errors: ' . implode("\n", $errors)); + $e->setErrors($errors); + throw $e; + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ApiParam.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ApiParam.php new file mode 100644 index 0000000..1e427cc --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ApiParam.php @@ -0,0 +1,278 @@ + $value) { + if ($key == 'min_length') { + $this->minLength = $value; + } elseif ($key == 'max_length') { + $this->maxLength = $value; + } elseif ($key == 'location' && strpos($value, ':')) { + list($this->location, $this->locationKey) = explode(':', $value, 2); + } elseif ($key == 'location_key') { + $this->locationKey = $value; + } elseif ($key == 'type' && strpos($value, ':')) { + list($this->type, $this->typeArgs) = explode(':', $value, 2); + } elseif ($key == 'type_args') { + $this->typeArgs = $value; + } else { + $this->{$key} = $value; + } + } + + if ($this->filters) { + $this->filters = array_map('trim', explode(',', $this->filters)); + } else { + $this->filters = array(); + } + + if ($this->required === 'false') { + $this->required = false; + } elseif ($this->required === 'true') { + $this->required = true; + } + + // Parse CSV type value data into an array + if ($this->typeArgs && is_string($this->typeArgs)) { + if (strpos($this->typeArgs, ',') !== false) { + $this->typeArgs = str_getcsv($this->typeArgs, ',', "'"); + } else { + $this->typeArgs = array($this->typeArgs); + } + } + } + + /** + * Convert the object to an array + * + * @return array + */ + public function toArray() + { + return array( + 'name' => $this->name, + 'type' => $this->type, + 'type_args' => $this->typeArgs, + 'required' => $this->required, + 'default' => $this->default, + 'doc' => $this->doc, + 'min_length' => $this->minLength, + 'max_length' => $this->maxLength, + 'location' => $this->location, + 'location_key' => $this->locationKey, + 'static' => $this->static, + 'prepend' => $this->prepend, + 'append' => $this->append, + 'filters' => implode(',', $this->filters) + ); + } + + /** + * Get the default or static value of the command based on a value + * + * @param string $value Value that is currently set + * + * @return mixed Returns the value, a static value if one is present, or a default value + */ + public function getValue($value) + { + if ($this->static || ($this->default && !$value)) { + $check = $this->static ?: $this->default; + if ($check === 'true') { + return true; + } elseif ($check === 'false') { + return false; + } else { + return $check; + } + } + + return $value; + } + + /** + * Filter a value + * + * @param mixed $value Value to filter + * + * @return mixed Returns the filtered value + */ + public function filter($value) + { + if ($this->filters) { + foreach ($this->filters as $filter) { + $value = call_user_func($filter, $value); + } + } + + return $value; + } + + /** + * Get the name of the parameter + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get the type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Get the arguments to pass to type constraint + * + * @return array|null + */ + public function getTypeArgs() + { + return $this->typeArgs; + } + + /** + * Get if the parameter is required + * + * @return bool + */ + public function getRequired() + { + return $this->required; + } + + /** + * Get the default value of the parameter + * + * @return string|null + */ + public function getDefault() + { + return $this->default; + } + + /** + * Get the docs for the parameter + * + * @return string|null + */ + public function getDoc() + { + return $this->doc; + } + + /** + * Get the minimum allowed length of the parameter + * + * @return int|null + */ + public function getMinLength() + { + return $this->minLength; + } + + /** + * Get the maximum allowed length of the parameter + * + * @return int|null + */ + public function getMaxLength() + { + return $this->maxLength; + } + + /** + * Get the location of the parameter + * + * @return string|null + */ + public function getLocation() + { + return $this->location; + } + + /** + * Get the location key mapping of the parameter + * + * @return string|null + */ + public function getLocationKey() + { + return $this->locationKey; + } + + /** + * Get the static value of the parameter + * + * @return int|null + */ + public function getStatic() + { + return $this->static; + } + + /** + * Get the string to prepend to values + * + * @return string + */ + public function getPrepend() + { + return $this->prepend; + } + + /** + * Get the string to append to values + * + * @return string + */ + public function getAppend() + { + return $this->append; + } + + /** + * Get an array of filters used by the parameter + * + * @return array + */ + public function getFilters() + { + return $this->filters; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ArrayDescriptionBuilder.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ArrayDescriptionBuilder.php new file mode 100644 index 0000000..9affe2c --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ArrayDescriptionBuilder.php @@ -0,0 +1,54 @@ + $type) { + $default = array(); + if (!isset($type['class'])) { + throw new DescriptionBuilderException('Custom types require a class attribute'); + } + foreach ($type as $key => $value) { + if ($key != 'name' && $key != 'class') { + $default[$key] = $value; + } + } + Inspector::getInstance()->registerConstraint($name, $type['class'], $default); + } + } + + $commands = array(); + if (!empty($config['commands'])) { + foreach ($config['commands'] as $name => $command) { + $name = $command['name'] = isset($command['name']) ? $command['name'] : $name; + // Extend other commands + if (!empty($command['extends'])) { + if (empty($commands[$command['extends']])) { + throw new DescriptionBuilderException($name . ' extends missing command ' . $command['extends']); + } + $params = array_merge($commands[$command['extends']]->getParams(), !empty($command['params']) ? $command['params'] : array()); + $command = array_merge($commands[$command['extends']]->toArray(), $command); + $command['params'] = $params; + } + // Use the default class + $command['class'] = isset($command['class']) ? str_replace('.', '\\', $command['class']) : ServiceDescription::DEFAULT_COMMAND_CLASS; + $commands[$name] = new ApiCommand($command); + } + } + + return new ServiceDescription($commands); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/DescriptionBuilderInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/DescriptionBuilderInterface.php new file mode 100644 index 0000000..125f9c9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/DescriptionBuilderInterface.php @@ -0,0 +1,19 @@ +loader) { + $this->loader = new JsonLoader(); + } + + return ServiceDescription::factory($this->loader->parseJsonFile($config)); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php new file mode 100644 index 0000000..5fc31d8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescription.php @@ -0,0 +1,115 @@ +build($config); + } + + /** + * Create a new ServiceDescription + * + * @param array $commands Array of {@see ApiCommand} objects + */ + public function __construct(array $commands = array()) + { + $this->commands = $commands; + } + + /** + * Serialize the service description + * + * @return string + */ + public function serialize() + { + return json_encode(array_map(function($command) { + // Convert ApiCommands into arrays + $data = $command->toArray(); + // Convert ApiParams into arrays + $data['params'] = array_map(function($param) { + return $param->toArray(); + }, $data['params']); + + return $data; + }, $this->commands)); + } + + /** + * Unserialize the service description + * + * @param string|array $json JSON data + */ + public function unserialize($json) + { + $this->commands = array_map(function($data) { + // Convert params to ApiParam objects + $data['params'] = array_map(function($param) { + return new ApiParam($param); + }, $data['params']); + // Convert commands into ApiCommands + return new ApiCommand($data); + }, json_decode($json, true)); + } + + /** + * Get the API commands of the service + * + * @return array Returns an array of ApiCommand objects + */ + public function getCommands() + { + return $this->commands; + } + + /** + * Check if the service has a command by name + * + * @param string $name Name of the command to check + * + * @return bool + */ + public function hasCommand($name) + { + return array_key_exists($name, $this->commands); + } + + /** + * Get an API command by name + * + * @param string $name Name of the command + * + * @return ApiCommand|null + */ + public function getCommand($name) + { + return $this->hasCommand($name) ? $this->commands[$name] : null; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionAbstractFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionAbstractFactory.php new file mode 100644 index 0000000..5d56b20 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Description/ServiceDescriptionAbstractFactory.php @@ -0,0 +1,49 @@ +parseXmlFile($config)); + } + + /** + * Convert an XML file to an array of service data + * + * @param string $file XML filename to parse + * + * @return array + * @throws InvalidArgumentException + */ + protected function parseXmlFile($file) + { + if (!file_exists($file)) { + throw new DescriptionBuilderException('Unable to open ' . $file . ' for reading'); + } + + $xml = new \SimpleXMLElement($file, null, true); + $config = array( + 'types' => array(), + 'commands' => array() + ); + + // Register any custom type definitions + $types = $xml->types->type; + if ($types) { + foreach ($types as $type) { + $attr = $type->attributes(); + $name = (string) $attr->name; + $config['types'][$name] = array(); + foreach ($attr as $key => $value) { + $config['types'][$name][(string) $key] = (string) $value; + } + } + } + + // Parse the commands in the XML doc + $commands = $xml->commands->command; + if ($commands) { + foreach ($commands as $command) { + $attr = $command->attributes(); + $name = (string) $attr->name; + $config['commands'][$name] = array( + 'params' => array() + ); + foreach ($attr as $key => $value) { + $config['commands'][$name][(string) $key] = (string) $value; + } + $config['commands'][$name]['doc'] = (string) $command->doc; + foreach ($command->param as $param) { + $attr = $param->attributes(); + $paramName = (string) $attr['name']; + $config['commands'][$name]['params'][$paramName] = array(); + foreach ($attr as $pk => $pv) { + $pv = (string) $pk == 'required' ? (string) $pv === 'true' : (string) $pv; + $config['commands'][$name]['params'][$paramName][(string) $pk] = (string) $pv; + } + } + } + } + + // Handle XML includes + $includes = $xml->includes->include; + if ($includes) { + foreach ($includes as $includeFile) { + $path = (string) $includeFile->attributes()->path; + if ($path[0] != DIRECTORY_SEPARATOR) { + $path = dirname($file) . DIRECTORY_SEPARATOR . $path; + } + $config = array_merge_recursive(self::parseXmlFile($path), $config); + } + } + + return $config; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php new file mode 100644 index 0000000..0f016fb --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/CommandException.php @@ -0,0 +1,7 @@ +invalidCommands = $commands; + parent::__construct( + 'Encountered commands in a batch transfer that use inconsistent clients. The batching ' . + 'strategy you use with a command transfer must divide command batches by client.' + ); + } + + /** + * Get the invalid commands + * + * @return array + */ + public function getCommands() + { + return $this->invalidCommands; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/JsonException.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/JsonException.php new file mode 100644 index 0000000..fae1c6c --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Exception/JsonException.php @@ -0,0 +1,7 @@ +errors = $errors; + } + + /** + * Get any validation errors + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Inspector.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Inspector.php new file mode 100644 index 0000000..ffa8da2 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Inspector.php @@ -0,0 +1,202 @@ +constraints = array( + 'blank' => array($base . 'Blank', null), + 'not_blank' => array($base . 'NotBlank', null), + 'integer' => array($base . 'Numeric', null), + 'float' => array($base . 'Numeric', null), + 'string' => array($base . 'Type', array('type' => 'string')), + 'file' => array($base . 'Type', array('type' => 'file')), + 'array' => array($base . 'Type', array('type' => 'array')), + 'bool' => array($base . 'Bool', null), + 'boolean' => array($base . 'Bool', null), + 'email' => array($base . 'Email', null), + 'ip' => array($base . 'Ip', null), + 'url' => array($base . 'Url', null), + 'class' => array($base . 'IsInstanceOf', null), + 'type' => array($base . 'Type', null), + 'any_match' => array($base . 'AnyMatch', null), + 'ctype' => array($base . 'Ctype', null), + 'choice' => array($base . 'Choice', null), + 'enum' => array($base . 'Choice', null), + 'regex' => array($base . 'Regex', null), + 'date' => array($base . 'Type', array('type' => 'string')), + 'date_time' => array($base . 'Type', array('type' => 'string')), + 'time' => array($base . 'Numeric', null) + ); + } + + /** + * Get an instance of the Inspector + * + * @return Inspector + */ + public static function getInstance() + { + // @codeCoverageIgnoreStart + if (!self::$instance) { + self::$instance = new self(); + } + // @codeCoverageIgnoreEnd + + return self::$instance; + } + + /** + * Validate and prepare configuration parameters + * + * @param array $config Configuration values to apply. + * @param array $defaults Default parameters + * @param array $required Required parameter names + * + * @return Collection + * @throws InvalidArgumentException if a parameter is missing + */ + public static function prepareConfig(array $config = null, array $defaults = null, array $required = null) + { + $collection = new Collection($defaults); + + foreach ((array) $config as $key => $value) { + $collection->set($key, $value); + } + + foreach ((array) $required as $key) { + if ($collection->hasKey($key) === false) { + throw new ValidationException( + "Config must contain a '{$key}' key" + ); + } + } + + return $collection; + } + + /** + * Enable/disable type validation of configuration settings. This is + * useful for very high performance requirements. + * + * @param bool $typeValidation Set to TRUE or FALSE + * + * @return Inspector + */ + public function setTypeValidation($typeValidation) + { + $this->typeValidation = $typeValidation; + } + + /** + * Check if type validation is enabled + * + * @return bool + */ + public function getTypeValidation() + { + return $this->typeValidation; + } + + /** + * Get an array of the registered constraints by name + * + * @return array + */ + public function getRegisteredConstraints() + { + return array_map(function($constraint) { + return $constraint[0]; + }, $this->constraints); + } + + /** + * Register a constraint class with a special name + * + * @param string $name Name of the constraint to register + * @param string $class Name of the class or object to use when filtering by this name + * @param array $default Default values to pass to the constraint + * + * @return Inspector + */ + public function registerConstraint($name, $class, array $default = array()) + { + $this->constraints[$name] = array($class, $default); + } + + /** + * Get a constraint by name + * + * @param string $name Constraint name + * + * @return ConstraintInterface + * @throws InvalidArgumentException if the constraint is not registered + */ + public function getConstraint($name) + { + if (!isset($this->constraints[$name])) { + throw new InvalidArgumentException($name . ' has not been registered'); + } + + if (!isset($this->constraintCache[$name])) { + $c = $this->constraints[$name][0]; + $this->constraintCache[$name] = new $c(); + } + + return $this->constraintCache[$name]; + } + + /** + * Validate a constraint by name: e.g. "type:Guzzle\Common\Collection"; + * type:string; choice:a,b,c; choice:'a','b','c'; etc... + * + * @param string $name Constraint to retrieve with optional CSV args after colon + * @param mixed $value Value to validate + * @param array $args Optional arguments to pass to the type validation + * + * @return bool|string Returns TRUE if valid, or an error message if invalid + */ + public function validateConstraint($name, $value, array $args = null) + { + if (!$args) { + $args = isset($this->constraints[$name][1]) ? $this->constraints[$name][1] : array(); + } + + return $this->getConstraint($name)->validate($value, $args); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/JsonLoader.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/JsonLoader.php new file mode 100644 index 0000000..f9d4824 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/JsonLoader.php @@ -0,0 +1,48 @@ +parseJsonFile($path), $data); + } + } + + return $data; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Plugin/PluginCollectionPlugin.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Plugin/PluginCollectionPlugin.php new file mode 100644 index 0000000..92feb34 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Plugin/PluginCollectionPlugin.php @@ -0,0 +1,50 @@ + + */ +class PluginCollectionPlugin implements EventSubscriberInterface +{ + /** + * @var $plugins array plugins to add + */ + private $plugins = array(); + + /** + * @param array $plugins plugins to add + */ + public function __construct(array $plugins) + { + $this->plugins = $plugins; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + 'service_builder.create_client' => 'onCreateClient' + ); + } + + /** + * Adds plugins to clients as they are created by the service builder + * + * @param Event $event Event emitted + */ + public function onCreateClient(Event $event) + { + foreach ($this->plugins as $plugin) { + $event['client']->addSubscriber($plugin); + } + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php new file mode 100644 index 0000000..2a78d9a --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIterator.php @@ -0,0 +1,321 @@ +originalCommand = $command; + + // Parse options from the array of options + $this->data = $data; + $this->limit = array_key_exists('limit', $data) ? $data['limit'] : 0; + $this->pageSize = array_key_exists('page_size', $data) ? $data['page_size'] : false; + } + + /** + * Get all of the resources as an array (be careful as this could issue a + * large number of requests if no limit is specified) + * + * @return array + */ + public function toArray() + { + return iterator_to_array($this, false); + } + + /** + * {@inheritdoc} + */ + public function setLimit($limit) + { + $this->limit = $limit; + $this->resetState(); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function setPageSize($pageSize) + { + $this->pageSize = $pageSize; + $this->resetState(); + + return $this; + } + + /** + * Get an option from the iterator + * + * @param string $key Key of the option to retrieve + * + * @return mixed|null Returns NULL if not set or the value if set + */ + public function get($key) + { + return array_key_exists($key, $this->data) ? $this->data[$key] : null; + } + + /** + * Set an option on the iterator + * + * @param string $key Key of the option to set + * @param mixed $value Value to set for the option + * + * @return ResourceIterator + */ + public function set($key, $value) + { + $this->data[$key] = $value; + + return $this; + } + + /** + * Return the current element. + * + * @return mixed Returns the current element. + */ + public function current() + { + return $this->resources ? current($this->resources) : false; + } + + /** + * Return the key of the current element. + * + * @return mixed + */ + public function key() + { + return max(0, $this->iteratedCount - 1); + } + + /** + * Return the total number of items that have been retrieved thus far. + * + * @return int + */ + public function count() + { + return $this->retrievedCount; + } + + /** + * Get the total number of requests sent + * + * @return int + */ + public function getRequestCount() + { + return $this->requestCount; + } + + /** + * Rewind the Iterator to the first element and send the original command + */ + public function rewind() + { + // Use the original command + $this->command = clone $this->originalCommand; + $this->resetState(); + $this->next(); + } + + /** + * Check if there is a current element after calls to rewind() or next(). + * + * @return bool Returns TRUE if the current element is valid or FALSE + */ + public function valid() + { + return !$this->invalid && (!$this->resources || $this->current() || $this->nextToken) + && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + /** + * Move forward to next element and may trigger subsequent requests + */ + public function next() + { + $this->iteratedCount++; + + // Check if a new set of resources needs to be retrieved + $sendRequest = false; + if (!$this->resources) { + $sendRequest = true; + } else { + // iterate over the internal array + $current = next($this->resources); + $sendRequest = $current === false && $this->nextToken && (!$this->limit || $this->iteratedCount < $this->limit + 1); + } + + if ($sendRequest) { + + $this->dispatch('resource_iterator.before_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + + // Get a new command object from the original command + $this->command = clone $this->originalCommand; + // Send a request and retrieve the newly loaded resources + $this->resources = $this->sendRequest(); + $this->requestCount++; + + // If no resources were found, then the last request was not needed + // and iteration must stop + if (empty($this->resources)) { + $this->invalid = true; + } else { + // Add to the number of retrieved resources + $this->retrievedCount += count($this->resources); + // Ensure that we rewind to the beginning of the array + reset($this->resources); + } + + $this->dispatch('resource_iterator.after_send', array( + 'iterator' => $this, + 'resources' => $this->resources + )); + } + } + + /** + * Retrieve the NextToken that can be used in other iterators. + * + * @return string Returns a NextToken + */ + public function getNextToken() + { + return $this->nextToken; + } + + /** + * Returns the value that should be specified for the page size for + * a request that will maintain any hard limits, but still honor the + * specified pageSize if the number of items retrieved + pageSize < hard + * limit + * + * @return int Returns the page size of the next request. + */ + protected function calculatePageSize() + { + if ($this->limit && $this->iteratedCount + $this->pageSize > $this->limit) { + return 1 + ($this->limit - $this->iteratedCount); + } + + return (int) $this->pageSize; + } + + /** + * Reset the internal state of the iterator without triggering a rewind() + */ + protected function resetState() + { + $this->iteratedCount = 0; + $this->retrievedCount = 0; + $this->nextToken = false; + $this->resources = null; + $this->invalid = false; + } + + /** + * Send a request to retrieve the next page of results. + * Hook for subclasses to implement. + * + * @return array Returns the newly loaded resources + */ + abstract protected function sendRequest(); +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php new file mode 100644 index 0000000..40701bf --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorApplyBatched.php @@ -0,0 +1,120 @@ +iterator = $iterator; + $this->callback = $callback; + } + + /** + * Apply the callback to the contents of the resource iterator + * + * @param int $perBatch The number of records to group per batch transfer + * + * @return int Returns the number of iterated resources + */ + public function apply($perBatch = 50) + { + $this->iterated = $this->batches = $batches = 0; + $that = $this; + $it = $this->iterator; + $callback = $this->callback; + + $batch = BatchBuilder::factory() + ->createBatchesWith(new BatchSizeDivisor($perBatch)) + ->transferWith(new BatchClosureTransfer(function (array $batch) use ($that, $callback, &$batches, $it) { + $batches++; + $that->dispatch('iterator_batch.before_batch', array('iterator' => $it, 'batch' => $batch)); + call_user_func_array($callback, array($it, $batch)); + $that->dispatch('iterator_batch.after_batch', array('iterator' => $it, 'batch' => $batch)); + })) + ->autoFlushAt($perBatch) + ->build(); + + $this->dispatch('iterator_batch.created_batch', array('batch' => $batch)); + + foreach ($this->iterator as $resource) { + $this->iterated++; + $batch->add($resource); + } + + $batch->flush(); + $this->batches = $batches; + + return $this->iterated; + } + + /** + * Get the total number of batches sent + * + * @return int + */ + public function getBatchCount() + { + return $this->batches; + } + + /** + * Get the total number of iterated resources + * + * @return int + */ + public function getIteratedCount() + { + return $this->iterated; + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php new file mode 100644 index 0000000..71f50e4 --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorClassFactory.php @@ -0,0 +1,58 @@ + CamelCaseIterator). + */ +class ResourceIteratorClassFactory implements ResourceIteratorFactoryInterface +{ + /** + * @var string + */ + protected $baseNamespace; + + /** + * @var InflectorInterface Inflector used to determine class names + */ + protected $inflector; + + /** + * @param string $baseNamespace Base namespace of all iterator object. + * @param InflectorInterface $inflector Inflector used to resolve class names + */ + public function __construct($baseNamespace, InflectorInterface $inflector = null) + { + $this->baseNamespace = $baseNamespace; + $this->inflector = $inflector ?: Inflector::getDefault(); + } + + /** + * Create a resource iterator + * + * @param CommandInterface $data Command used for building the iterator + * @param array $options Iterator options. + * + * @return ResourceIteratorInterface + */ + public function build($data, array $options = null) + { + if (!($data instanceof CommandInterface)) { + throw new InvalidArgumentException('The first argument must be an instance of CommandInterface'); + } + + // Determine the name of the class to load + $className = $this->baseNamespace . '\\' . $this->inflector->camel($data->getName()) . 'Iterator'; + + return new $className($data, $options); + } +} diff --git a/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php new file mode 100644 index 0000000..aface4b --- /dev/null +++ b/core/vendor/guzzle/guzzle/src/Guzzle/Service/Resource/ResourceIteratorFactoryInterface.php @@ -0,0 +1,19 @@ +assertEquals(array(), AbstractHasDispatcher::getAllEvents()); + } + + /** + * @covers Guzzle\Common\AbstractHasDispatcher::setEventDispatcher + * @covers Guzzle\Common\AbstractHasDispatcher::getEventDispatcher + */ + public function testAllowsDispatcherToBeInjected() + { + $d = new EventDispatcher(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertSame($mock, $mock->setEventDispatcher($d)); + $this->assertSame($d, $mock->getEventDispatcher()); + } + + /** + * @covers Guzzle\Common\AbstractHasDispatcher::getEventDispatcher + */ + public function testCreatesDefaultEventDispatcherIfNeeded() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventDispatcher', $mock->getEventDispatcher()); + } + + /** + * @covers Guzzle\Common\AbstractHasDispatcher::dispatch + */ + public function testHelperDispatchesEvents() + { + $data = array(); + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $mock->getEventDispatcher()->addListener('test', function(Event $e) use (&$data) { + $data = $e->getIterator()->getArrayCopy(); + }); + $mock->dispatch('test', array( + 'param' => 'abc' + )); + $this->assertEquals(array( + 'param' => 'abc', + ), $data); + } + + /** + * @covers Guzzle\Common\AbstractHasDispatcher::addSubscriber + */ + public function testHelperAttachesSubscribers() + { + $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); + $subscriber = $this->getMockForAbstractClass('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') + ->setMethods(array('addSubscriber')) + ->getMock(); + + $dispatcher->expects($this->once()) + ->method('addSubscriber'); + + $mock->setEventDispatcher($dispatcher); + $mock->addSubscriber($subscriber); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/AbstractBatchDecoratorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/AbstractBatchDecoratorTest.php new file mode 100644 index 0000000..48bece5 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/AbstractBatchDecoratorTest.php @@ -0,0 +1,33 @@ +getMock('Guzzle\Common\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface') + ); + + $decoratorA = $this->getMockBuilder('Guzzle\Common\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($batch)) + ->getMockForAbstractClass(); + + $decoratorB = $this->getMockBuilder('Guzzle\Common\Batch\AbstractBatchDecorator') + ->setConstructorArgs(array($decoratorA)) + ->getMockForAbstractClass(); + + $decoratorA->add('foo'); + $this->assertFalse($decoratorB->isEmpty()); + $this->assertFalse($batch->isEmpty()); + $this->assertEquals(array($decoratorB, $decoratorA), $decoratorB->getDecorators()); + $this->assertEquals(array(), $decoratorB->flush()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchBuilderTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchBuilderTest.php new file mode 100644 index 0000000..685b111 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchBuilderTest.php @@ -0,0 +1,86 @@ +getMock('Guzzle\Common\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface'); + } + + private function getMockBatchBuilder() + { + return BatchBuilder::factory() + ->transferWith($this->getMockTransfer()) + ->createBatchesWith($this->getMockDivisor()); + } + + public function testFactoryCreatesInstance() + { + $builder = BatchBuilder::factory(); + $this->assertInstanceOf('Guzzle\Common\Batch\BatchBuilder', $builder); + } + + public function testAddsAutoFlush() + { + $batch = $this->getMockBatchBuilder()->autoFlushAt(10)->build(); + $this->assertInstanceOf('Guzzle\Common\Batch\FlushingBatch', $batch); + } + + public function testAddsExceptionBuffering() + { + $batch = $this->getMockBatchBuilder()->bufferExceptions()->build(); + $this->assertInstanceOf('Guzzle\Common\Batch\ExceptionBufferingBatch', $batch); + } + + public function testAddHistory() + { + $batch = $this->getMockBatchBuilder()->keepHistory()->build(); + $this->assertInstanceOf('Guzzle\Common\Batch\HistoryBatch', $batch); + } + + public function testAddsNotify() + { + $batch = $this->getMockBatchBuilder()->notify(function() {})->build(); + $this->assertInstanceOf('Guzzle\Common\Batch\NotifyingBatch', $batch); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testTransferStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->createBatchesWith($this->getMockDivisor())->build(); + } + + /** + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testDivisorStrategyMustBeSet() + { + $batch = BatchBuilder::factory()->transferWith($this->getMockTransfer())->build(); + } + + public function testTransfersRequests() + { + $batch = BatchBuilder::factory()->transferRequests(10)->build(); + $this->assertInstanceOf('Guzzle\Http\BatchRequestTransfer', $this->readAttribute($batch, 'transferStrategy')); + } + + public function testTransfersCommands() + { + $batch = BatchBuilder::factory()->transferCommands(10)->build(); + $this->assertInstanceOf('Guzzle\Service\Command\BatchCommandTransfer', $this->readAttribute($batch, 'transferStrategy')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchClosureDivisorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchClosureDivisorTest.php new file mode 100644 index 0000000..84409bb --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchClosureDivisorTest.php @@ -0,0 +1,36 @@ +createBatches($queue); + $this->assertEquals(array(array('foo'), array('baz')), $batches); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchClosureTransferTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchClosureTransferTest.php new file mode 100644 index 0000000..9ab10e7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchClosureTransferTest.php @@ -0,0 +1,56 @@ +itemsTransferred = null; + $itemsTransferred =& $this->itemsTransferred; + + $this->transferStrategy = new BatchClosureTransfer(function (array $batch) use (&$itemsTransferred) { + $itemsTransferred = $batch; + return; + }); + } + + public function testTransfersBatch() + { + $batchedItems = array('foo', 'bar', 'baz'); + $this->transferStrategy->transfer($batchedItems); + + $this->assertEquals($batchedItems, $this->itemsTransferred); + } + + public function testTransferBailsOnEmptyBatch() + { + $batchedItems = array(); + $this->transferStrategy->transfer($batchedItems); + + $this->assertNull($this->itemsTransferred); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsCallable() + { + $foo = new BatchClosureTransfer('uh oh!'); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchSizeDivisorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchSizeDivisorTest.php new file mode 100644 index 0000000..73b0427 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchSizeDivisorTest.php @@ -0,0 +1,24 @@ +assertEquals(3, $d->getSize()); + $d->setSize(2); + $batches = $d->createBatches($queue); + $this->assertEquals(array(array('foo', 'baz'), array('bar')), $batches); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchTest.php new file mode 100644 index 0000000..b17c0fd --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/BatchTest.php @@ -0,0 +1,91 @@ +getMock('Guzzle\Common\Batch\BatchTransferInterface'); + } + + private function getMockDivisor() + { + return $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface'); + } + + public function testAddsItemsToQueue() + { + $batch = new Batch($this->getMockTransfer(), $this->getMockDivisor()); + $this->assertSame($batch, $batch->add('foo')); + $this->assertEquals(1, count($batch)); + } + + public function testFlushReturnsItems() + { + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer'); + + $divisor = $this->getMockDivisor(); + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnValue(array(array('foo', 'baz'), array('bar')))); + + $batch = new Batch($transfer, $divisor); + + $batch->add('foo')->add('baz')->add('bar'); + $items = $batch->flush(); + + $this->assertEquals(array('foo', 'baz', 'bar'), $items); + } + + public function testThrowsExceptionContainingTheFailedBatch() + { + $called = 0; + $originalException = new \Exception('Foo!'); + + $transfer = $this->getMockTransfer(); + $transfer->expects($this->exactly(2)) + ->method('transfer') + ->will($this->returnCallback(function () use (&$called, $originalException) { + if (++$called == 2) { + throw $originalException; + } + })); + + $divisor = $this->getMockDivisor(); + $batch = new Batch($transfer, $divisor); + + // PHPunit clones objects before passing them to a callback. + // Horrible hack to get around this! + $queue = $this->readAttribute($batch, 'queue'); + + $divisor->expects($this->once()) + ->method('createBatches') + ->will($this->returnCallback(function ($batch) use ($queue) { + foreach ($queue as $item) { + $items[] = $item; + } + return array_chunk($items, 2); + })); + + $batch->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertFalse($batch->isEmpty()); + + try { + $items = $batch->flush(); + $this->fail('Expected exception'); + } catch (BatchTransferException $e) { + $this->assertEquals($originalException, $e->getPrevious()); + $this->assertEquals(array('bar', 'bee'), array_values($e->getBatch())); + $this->assertEquals(1, count($batch)); + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/ExceptionBufferingBatchTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/ExceptionBufferingBatchTest.php new file mode 100644 index 0000000..0cdc232 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/ExceptionBufferingBatchTest.php @@ -0,0 +1,46 @@ +getMockBuilder('Guzzle\Common\Batch\BatchTransferInterface') + ->setMethods(array('transfer')) + ->getMock(); + + $d = new BatchSizeDivisor(1); + $batch = new Batch($t, $d); + + $called = 0; + $t->expects($this->exactly(3)) + ->method('transfer') + ->will($this->returnCallback(function ($batch) use (&$called) { + if (++$called === 2) { + throw new \Exception('Foo'); + } + })); + + $decorator = new ExceptionBufferingBatch($batch); + $decorator->add('foo')->add('baz')->add('bar'); + $result = $decorator->flush(); + + $e = $decorator->getExceptions(); + $this->assertEquals(1, count($e)); + $this->assertEquals(array('baz'), $e[0]->getBatch()); + + $decorator->clearExceptions(); + $this->assertEquals(0, count($decorator->getExceptions())); + + $this->assertEquals(array('foo', 'bar'), $result); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/FlushingBatchTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/FlushingBatchTest.php new file mode 100644 index 0000000..18d316e --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/FlushingBatchTest.php @@ -0,0 +1,40 @@ +getMock('Guzzle\Common\Batch\BatchTransferInterface', array('transfer')); + $d = $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface', array('createBatches')); + + $batch = new Batch($t, $d); + $queue = $this->readAttribute($batch, 'queue'); + + $d->expects($this->exactly(2)) + ->method('createBatches') + ->will($this->returnCallback(function () use ($queue) { + $items = array(); + foreach ($queue as $item) { + $items[] = $item; + } + return array($items); + })); + + $t->expects($this->exactly(2)) + ->method('transfer'); + + $flush = new FlushingBatch($batch, 3); + $this->assertEquals(3, $flush->getThreshold()); + $flush->setThreshold(2); + $flush->add('foo')->add('baz')->add('bar')->add('bee')->add('boo'); + $this->assertEquals(1, count($flush)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/HistoryBatchTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/HistoryBatchTest.php new file mode 100644 index 0000000..329cb8f --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/HistoryBatchTest.php @@ -0,0 +1,26 @@ +getMock('Guzzle\Common\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface') + ); + + $history = new HistoryBatch($batch); + $history->add('foo')->add('baz'); + $this->assertEquals(array('foo', 'baz'), $history->getHistory()); + $history->clearHistory(); + $this->assertEquals(array(), $history->getHistory()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/NotifyingBatchTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/NotifyingBatchTest.php new file mode 100644 index 0000000..ea10ef1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Batch/NotifyingBatchTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Common\Batch\Batch', array('flush'), array( + $this->getMock('Guzzle\Common\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface') + )); + + $batch->expects($this->once()) + ->method('flush') + ->will($this->returnValue(array('foo', 'baz'))); + + $data = array(); + $decorator = new NotifyingBatch($batch, function ($batch) use (&$data) { + $data[] = $batch; + }); + + $decorator->add('foo')->add('baz'); + $decorator->flush(); + $this->assertEquals(array(array('foo', 'baz')), $data); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresCallableIsValid() + { + $batch = new Batch( + $this->getMock('Guzzle\Common\Batch\BatchTransferInterface'), + $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface') + ); + $decorator = new NotifyingBatch($batch, 'foo'); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/CacheAdapterFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/CacheAdapterFactoryTest.php new file mode 100644 index 0000000..b39df21 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/CacheAdapterFactoryTest.php @@ -0,0 +1,129 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory::factory + * @expectedException InvalidArgumentException + */ + public function testEnsuresConfigIsArray() + { + CacheAdapterFactory::factory(new \stdClass()); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory::factory + * @expectedException InvalidArgumentException + * @expectedExceptionMessage cache.provider is a required CacheAdapterFactory option + */ + public function testEnsuresRequiredProviderOption() + { + CacheAdapterFactory::factory(array( + 'cache.adapter' => $this->adapter + )); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory::factory + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + * @expectedExceptionMessage cache.adapter is a required CacheAdapterFactory option + */ + public function testEnsuresRequiredAdapterOption() + { + CacheAdapterFactory::factory(array( + 'cache.provider' => $this->cache + )); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory::factory + * @expectedException InvalidArgumentException + * @expectedExceptionMessage foo is not a valid class for cache.adapter + */ + public function testEnsuresClassesExist() + { + CacheAdapterFactory::factory(array( + 'cache.provider' => 'abc', + 'cache.adapter' => 'foo' + )); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory::factory + * @covers Guzzle\Common\Cache\CacheAdapterFactory::createObject + */ + public function testCreatesProviderFromConfig() + { + $cache = CacheAdapterFactory::factory(array( + 'cache.provider' => 'Doctrine\Common\Cache\ApcCache', + 'cache.adapter' => 'Guzzle\Common\Cache\DoctrineCacheAdapter' + )); + + $this->assertInstanceOf('Guzzle\Common\Cache\DoctrineCacheAdapter', $cache); + $this->assertInstanceOf('Doctrine\Common\Cache\ApcCache', $cache->getCacheObject()); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory::factory + * @covers Guzzle\Common\Cache\CacheAdapterFactory::createObject + */ + public function testCreatesProviderFromConfigWithArguments() + { + $cache = CacheAdapterFactory::factory(array( + 'cache.provider' => 'Doctrine\Common\Cache\ApcCache', + 'cache.provider.args' => array(), + 'cache.adapter' => 'Guzzle\Common\Cache\DoctrineCacheAdapter', + 'cache.adapter.args' => array() + )); + + $this->assertInstanceOf('Guzzle\Common\Cache\DoctrineCacheAdapter', $cache); + $this->assertInstanceOf('Doctrine\Common\Cache\ApcCache', $cache->getCacheObject()); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory + * @expectedException Guzzle\Common\Exception\RuntimeException + */ + public function testWrapsExceptionsOnObjectCreation() + { + CacheAdapterFactory::factory(array( + 'cache.provider' => 'Guzzle\Tests\Mock\ExceptionMock', + 'cache.adapter' => 'Guzzle\Tests\Mock\ExceptionMock' + )); + } + + /** + * @covers Guzzle\Common\Cache\CacheAdapterFactory + */ + public function testCreatesNullCacheAdapterByDefault() + { + $adapter = CacheAdapterFactory::factory(array()); + $this->assertInstanceOf('Guzzle\\Common\\Cache\\NullCacheAdapter', $adapter); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/CacheAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/CacheAdapterTest.php new file mode 100644 index 0000000..a37c498 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/CacheAdapterTest.php @@ -0,0 +1,86 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + /** + * @covers \Guzzle\Common\Cache\AbstractCacheAdapter::getCacheObject + */ + public function testGetCacheObject() + { + $this->assertEquals($this->cache, $this->adapter->getCacheObject()); + } + + /** + * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::save + */ + public function testSave() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + } + + /** + * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::fetch + */ + public function testFetch() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + /** + * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::contains + */ + public function testContains() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->contains('test')); + } + + /** + * @covers \Guzzle\Common\Cache\DoctrineCacheAdapter::delete + */ + public function testDelete() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertTrue($this->adapter->delete('test')); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/ClosureCacheAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/ClosureCacheAdapterTest.php new file mode 100644 index 0000000..9092cfd --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/ClosureCacheAdapterTest.php @@ -0,0 +1,114 @@ +callables = array( + 'contains' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data); + }, + 'delete' => function($id, $options = array()) use ($that) { + unset($that->data[$id]); + return true; + }, + 'fetch' => function($id, $options = array()) use ($that) { + return array_key_exists($id, $that->data) ? $that->data[$id] : null; + }, + 'save' => function($id, $data, $lifeTime, $options = array()) use ($that) { + $that->data[$id] = $data; + return true; + } + ); + + $this->adapter = new ClosureCacheAdapter($this->callables); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->cache = null; + $this->callables = null; + parent::tearDown(); + } + + /** + * @covers Guzzle\Common\Cache\ClosureCacheAdapter::__construct + * @expectedException InvalidArgumentException + */ + public function testEnsuresCallablesArePresent() + { + $callables = $this->callables; + unset($callables['delete']); + $cache = new ClosureCacheAdapter($callables); + } + + /** + * @covers Guzzle\Common\Cache\ClosureCacheAdapter::__construct + */ + public function testAllCallablesMustBePresent() + { + $cache = new ClosureCacheAdapter($this->callables); + } + + /** + * @covers Guzzle\Common\Cache\ClosureCacheAdapter::save + * @covers Guzzle\Common\Cache\ClosureCacheAdapter::fetch + */ + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + /** + * @covers Guzzle\Common\Cache\ClosureCacheAdapter::contains + */ + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + /** + * @covers Guzzle\Common\Cache\ClosureCacheAdapter::delete + */ + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/NullCacheAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/NullCacheAdapterTest.php new file mode 100644 index 0000000..b6e66a6 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/NullCacheAdapterTest.php @@ -0,0 +1,20 @@ +assertEquals(false, $c->contains('foo')); + $this->assertEquals(true, $c->delete('foo')); + $this->assertEquals(false, $c->fetch('foo')); + $this->assertEquals(true, $c->save('foo', 'bar')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/Zf1CacheAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/Zf1CacheAdapterTest.php new file mode 100644 index 0000000..2fb5a71 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/Zf1CacheAdapterTest.php @@ -0,0 +1,21 @@ +assertTrue($adapter->save('id', 'data')); + $this->assertTrue($adapter->delete('id')); + $this->assertEquals('foo', $adapter->fetch('id')); + $this->assertEquals('123456', $adapter->contains('id')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/Zf2CacheAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/Zf2CacheAdapterTest.php new file mode 100644 index 0000000..096206f --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Cache/Zf2CacheAdapterTest.php @@ -0,0 +1,99 @@ +cache = StorageFactory::factory(array( + 'adapter' => 'memory' + )); + $this->adapter = new Zf2CacheAdapter($this->cache); + } + + /** + * Cleans up the environment after running a test. + */ + protected function tearDown() + { + $this->adapter = null; + $this->cache = null; + parent::tearDown(); + } + + /** + * @covers Guzzle\Common\Cache\Zf2CacheAdapter::__construct + */ + public function testConstructorAddsDefaultOptions() + { + $default = array( + 'contains' => array( + 'namespace' => 'foo' + ) + ); + $adapter = new Zf2CacheAdapter(StorageFactory::factory(array( + 'adapter' => 'memory' + )), $default); + + // Access the protected property + $class = new \ReflectionClass($adapter); + $property = $class->getProperty('defaultOptions'); + $property->setAccessible(true); + $defaultOptions = $property->getValue($adapter); + + $this->assertEquals(array_merge($default, array( + 'delete' => array(), + 'fetch' => array(), + 'save' => array() + )), $defaultOptions); + } + + /** + * @covers Guzzle\Common\Cache\Zf2CacheAdapter::save + * @covers Guzzle\Common\Cache\Zf2CacheAdapter::fetch + */ + public function testCachesDataUsingCallables() + { + $this->assertTrue($this->adapter->save('test', 'data', 1000)); + $this->assertEquals('data', $this->adapter->fetch('test')); + } + + /** + * @covers Guzzle\Common\Cache\Zf2CacheAdapter::contains + */ + public function testChecksIfCacheContainsKeys() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->assertFalse($this->adapter->contains('foo')); + } + + /** + * @covers Guzzle\Common\Cache\Zf2CacheAdapter::delete + */ + public function testDeletesFromCacheByKey() + { + $this->adapter->save('test', 'data', 1000); + $this->assertTrue($this->adapter->contains('test')); + $this->adapter->delete('test'); + $this->assertFalse($this->adapter->contains('test')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php new file mode 100644 index 0000000..fd13be6 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/CollectionTest.php @@ -0,0 +1,418 @@ +coll = new Collection(); + } + + /** + * @covers Guzzle\Common\Collection::__construct + */ + public function testConstructorCanBeCalledWithNoParams() + { + $this->coll = new Collection(); + $p = $this->coll->getAll(); + $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); + } + + /** + * @covers Guzzle\Common\Collection::__construct + * @covers Guzzle\Common\Collection::getAll + */ + public function testConstructorCanBeCalledWithParams() + { + $testData = array( + 'test' => 'value', + 'test_2' => 'value2' + ); + $this->coll = new Collection($testData); + $this->assertEquals($this->coll->getAll(), $testData, '-> getAll() must return the data passed in the constructor'); + } + + /** + * @covers Guzzle\Common\Collection::__toString + */ + public function testCollectionCanBeConvertingIntoString() + { + $data = new Collection(); + $this->assertEquals('Guzzle\Common\Collection@' . spl_object_hash($data), (string) $data, '-> __toString() must represent the collection'); + } + + /** + * Test the IteratorAggregate implementation of theCollection object + * + * @covers Guzzle\Common\Collection::getIterator + */ + public function testImplementsIteratorAggregate() + { + $this->coll->set('key', 'value'); + $this->assertInstanceOf('ArrayIterator', $this->coll->getIterator()); + $this->assertEquals(1, count($this->coll)); + $total = 0; + foreach ($this->coll as $key => $value) { + $this->assertEquals('key', $key); + $this->assertEquals('value', $value); + $total++; + } + $this->assertEquals(1, $total); + } + + /** + * @covers Guzzle\Common\Collection::add + */ + public function testCanAddValuesToExistingKeysByUsingArray() + { + $this->coll->add('test', 'value1'); + $this->assertEquals($this->coll->getAll(), array('test' => 'value1')); + $this->coll->add('test', 'value2'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2'))); + $this->coll->add('test', 'value3'); + $this->assertEquals($this->coll->getAll(), array('test' => array('value1', 'value2', 'value3'))); + } + + /** + * @covers Guzzle\Common\Collection::merge + * @covers Guzzle\Common\Collection::getAll + */ + public function testHandlesMergingInDisparateDataSources() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + $this->coll->merge($params); + $this->assertEquals($this->coll->getAll(), $params); + + // Pass an invalid value and expect the same unaltered object + $this->assertEquals($this->coll->merge(false), $this->coll); + + // Pass the same object to itself + $this->assertEquals($this->coll->merge($this->coll), $this->coll); + } + + /** + * @covers Guzzle\Common\Collection::clear + * @covers Guzzle\Common\Collection::remove + */ + public function testCanClearAllDataOrSpecificKeys() + { + $this->coll->merge(array( + 'test' => 'value1', + 'test2' => 'value2' + )); + + // Clear a specific parameter by name + $this->coll->remove('test'); + + $this->assertEquals($this->coll->getAll(), array( + 'test2' => 'value2' + )); + + // Clear all parameters + $this->coll->clear(); + + $this->assertEquals($this->coll->getAll(), array()); + } + + /** + * @covers Guzzle\Common\Collection::get + * @covers Guzzle\Common\Collection::getAll + */ + public function testGetsValuesByKey() + { + $this->assertNull($this->coll->get('test')); + $this->coll->add('test', 'value'); + $this->assertEquals('value', $this->coll->get('test')); + $this->coll->set('test2', 'v2'); + $this->coll->set('test3', 'v3'); + $this->assertEquals(array( + 'test' => 'value', + 'test2' => 'v2' + ), $this->coll->getAll(array('test', 'test2'))); + } + + /** + * @covers Guzzle\Common\Collection::getKeys + * @covers Guzzle\Common\Collection::remove + */ + public function testProvidesKeys() + { + $this->assertEquals(array(), $this->coll->getKeys()); + $this->coll->merge(array( + 'test1' => 'value1', + 'test2' => 'value2' + )); + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + // Returns the cached array previously returned + $this->assertEquals(array('test1', 'test2'), $this->coll->getKeys()); + $this->coll->remove('test1'); + $this->assertEquals(array('test2'), $this->coll->getKeys()); + $this->coll->add('test3', 'value3'); + $this->assertEquals(array('test2', 'test3'), $this->coll->getKeys()); + } + + /** + * @covers Guzzle\Common\Collection::hasKey + */ + public function testChecksIfHasKey() + { + $this->assertFalse($this->coll->hasKey('test')); + $this->coll->add('test', 'value'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->coll->add('test2', 'value2'); + $this->assertEquals(true, $this->coll->hasKey('test')); + $this->assertEquals(true, $this->coll->hasKey('test2')); + $this->assertFalse($this->coll->hasKey('testing')); + $this->assertEquals(false, $this->coll->hasKey('AB-C', 'junk')); + } + + /** + * @covers Guzzle\Common\Collection::hasValue + */ + public function testChecksIfHasValue() + { + $this->assertFalse($this->coll->hasValue('value')); + $this->coll->add('test', 'value'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->coll->add('test2', 'value2'); + $this->assertEquals('test', $this->coll->hasValue('value')); + $this->assertEquals('test2', $this->coll->hasValue('value2')); + $this->assertFalse($this->coll->hasValue('val')); + } + + /** + * @covers Guzzle\Common\Collection::getAll + */ + public function testCanGetAllValuesByArray() + { + $this->coll->add('foo', 'bar'); + $this->coll->add('tEsT', 'value'); + $this->coll->add('tesTing', 'v2'); + $this->coll->add('key', 'v3'); + $this->assertNull($this->coll->get('test')); + $this->assertEquals(array( + 'foo' => 'bar', + 'tEsT' => 'value', + 'tesTing' => 'v2' + ), $this->coll->getAll(array( + 'foo', 'tesTing', 'tEsT' + ))); + } + + /** + * @covers Guzzle\Common\Collection::count + */ + public function testImplementsCount() + { + $data = new Collection(); + $this->assertEquals(0, $data->count()); + $data->add('key', 'value'); + $this->assertEquals(1, count($data)); + $data->add('key', 'value2'); + $this->assertEquals(1, count($data)); + $data->add('key_2', 'value3'); + $this->assertEquals(2, count($data)); + } + + /** + * @covers Guzzle\Common\Collection::merge + */ + public function testAddParamsByMerging() + { + $params = array( + 'test' => 'value1', + 'test2' => 'value2', + 'test3' => array('value3', 'value4') + ); + + // Add some parameters + $this->coll->merge($params); + + // Add more parameters by merging them in + $this->coll->merge(array( + 'test' => 'another', + 'different_key' => 'new value' + )); + + $this->assertEquals(array( + 'test' => array('value1', 'another'), + 'test2' => 'value2', + 'test3' => array('value3', 'value4'), + 'different_key' => 'new value' + ), $this->coll->getAll()); + } + + /** + * @covers Guzzle\Common\Collection::filter + */ + public function testAllowsFunctionalFilter() + { + $this->coll->merge(array( + 'fruit' => 'apple', + 'number' => 'ten', + 'prepositions' => array('about', 'above', 'across', 'after'), + 'same_number' => 'ten' + )); + + $filtered = $this->coll->filter(function($key, $value) { + return $value == 'ten'; + }); + + $this->assertNotEquals($filtered, $this->coll); + + $this->assertEquals(array( + 'number' => 'ten', + 'same_number' => 'ten' + ), $filtered->getAll()); + } + + /** + * @covers Guzzle\Common\Collection::map + */ + public function testAllowsFunctionalMapping() + { + $this->coll->merge(array( + 'number_1' => 1, + 'number_2' => 2, + 'number_3' => 3 + )); + + $mapped = $this->coll->map(function($key, $value) { + return $value * $value; + }); + + $this->assertNotEquals($mapped, $this->coll); + + $this->assertEquals(array( + 'number_1' => 1, + 'number_2' => 4, + 'number_3' => 9 + ), $mapped->getAll()); + } + + /** + * @covers Guzzle\Common\Collection::offsetGet + * @covers Guzzle\Common\Collection::offsetSet + * @covers Guzzle\Common\Collection::offsetUnset + * @covers Guzzle\Common\Collection::offsetExists + */ + public function testImplementsArrayAccess() + { + $this->coll->merge(array( + 'k1' => 'v1', + 'k2' => 'v2' + )); + + $this->assertTrue($this->coll->offsetExists('k1')); + $this->assertFalse($this->coll->offsetExists('Krull')); + + $this->coll->offsetSet('k3', 'v3'); + $this->assertEquals('v3', $this->coll->offsetGet('k3')); + $this->assertEquals('v3', $this->coll->get('k3')); + + $this->coll->offsetUnset('k1'); + $this->assertFalse($this->coll->offsetExists('k1')); + } + + /** + * @covers Guzzle\Common\Collection::filter + * @covers Guzzle\Common\Collection::map + */ + public function testUsesStaticWhenCreatingNew() + { + $qs = new QueryString(array( + 'a' => 'b', + 'c' => 'd' + )); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->map(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->map(function($a, $b) {}, array(), false)); + + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $qs->filter(function($a, $b) {})); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $qs->filter(function($a, $b) {}, false)); + } + + /** + * @covers Guzzle\Common\Collection::replace + */ + public function testCanReplaceAllData() + { + $this->assertSame($this->coll, $this->coll->replace(array( + 'a' => '123' + ))); + + $this->assertEquals(array( + 'a' => '123' + ), $this->coll->getAll()); + } + + /** + * @covers Guzzle\Common\Collection::getPregMatchValue + */ + public function testReturnsValuesForPregMatch() + { + $c = new Collection(array('foo' => 'bar')); + $this->assertEquals('bar', $c->getPregMatchValue(array(1 => 'foo'))); + } + + public function dataProvider() + { + return array( + array('this_is_a_test', '{ a }_is_a_{ b }', array( + 'a' => 'this', + 'b' => 'test' + )), + array('this_is_a_test', '{abc}_is_a_{ 0 }', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', '{ abc }_is_{ not_found }a_{ 0 }', array( + 'abc' => 'this', + 0 => 'test' + )), + array('this_is_a_test', 'this_is_a_test', array( + 'abc' => 'this' + )), + array('_is_a_', '{ abc }_is_{ not_found }a_{ 0 }', array()), + array('_is_a_', '{abc}_is_{not_found}a_{0}', array()), + ); + } + + /** + * @covers Guzzle\Common\Collection::inject + * @dataProvider dataProvider + */ + public function testInjectsConfigData($output, $input, $config) + { + $collection = new Collection($config); + $this->assertEquals($output, $collection->inject($input)); + } + + /** + * @covers Guzzle\Common\Collection::keySearch + */ + public function testCanSearchByKey() + { + $collection = new Collection(array( + 'foo' => 'bar', + 'BaZ' => 'pho' + )); + + $this->assertEquals('foo', $collection->keySearch('FOO')); + $this->assertEquals('BaZ', $collection->keySearch('baz')); + $this->assertEquals(false, $collection->keySearch('Bar')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php new file mode 100644 index 0000000..b0f20e3 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/EventTest.php @@ -0,0 +1,62 @@ + '123', + 'other' => '456', + 'event' => 'test.notify' + )); + } + + /** + * @covers Guzzle\Common\Event::__construct + */ + public function testAllowsParameterInjection() + { + $event = new Event(array( + 'test' => '123' + )); + $this->assertEquals('123', $event['test']); + } + + /** + * @covers Guzzle\Common\Event::offsetGet + * @covers Guzzle\Common\Event::offsetSet + * @covers Guzzle\Common\Event::offsetUnset + * @covers Guzzle\Common\Event::offsetExists + */ + public function testImplementsArrayAccess() + { + $event = $this->getEvent(); + $this->assertEquals('123', $event['test']); + $this->assertNull($event['foobar']); + + $this->assertTrue($event->offsetExists('test')); + $this->assertFalse($event->offsetExists('foobar')); + + unset($event['test']); + $this->assertFalse($event->offsetExists('test')); + + $event['test'] = 'new'; + $this->assertEquals('new', $event['test']); + } + + /** + * @covers Guzzle\Common\Event::getIterator + */ + public function testImplementsIteratorAggregate() + { + $event = $this->getEvent(); + $this->assertInstanceOf('ArrayIterator', $event->getIterator()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php new file mode 100644 index 0000000..a74ee0a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php @@ -0,0 +1,21 @@ +getMock('Guzzle\Common\Batch\BatchTransferInterface'); + $d = $this->getMock('Guzzle\Common\Batch\BatchDivisorInterface'); + $transferException = new BatchTransferException(array('foo'), array(1, 2), $e, $t, $d); + $this->assertEquals(array('foo'), $transferException->getBatch()); + $this->assertSame($t, $transferException->getTransferStrategy()); + $this->assertSame($d, $transferException->getDivisorStrategy()); + $this->assertSame($e, $transferException->getPrevious()); + $this->assertEquals(array(1, 2), $transferException->getTransferredItems()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php new file mode 100644 index 0000000..39ce1da --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php @@ -0,0 +1,46 @@ +getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertEquals("Test\nTesting", $e->getMessage()); + } + + public function testActsAsArray() + { + $e = new ExceptionCollection(); + $exceptions = $this->getExceptions(); + $e->add($exceptions[0]); + $e->add($exceptions[1]); + $this->assertEquals(2, count($e)); + $this->assertEquals($exceptions, $e->getIterator()->getArrayCopy()); + } + + public function testCanAddSelf() + { + $e1 = new ExceptionCollection(); + $e1->add(new \Exception("Test")); + $e2 = new ExceptionCollection(); + $e2->add(new \Exception("Test 2")); + + $e1->add($e2); + $this->assertEquals("Test\nTest 2", $e1->getMessage()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/InflectorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/InflectorTest.php new file mode 100644 index 0000000..f275bdc --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/InflectorTest.php @@ -0,0 +1,37 @@ +assertSame(Inflector::getDefault(), Inflector::getDefault()); + } + + public function testSnake() + { + $this->assertEquals('camel_case', Inflector::getDefault()->snake('camelCase')); + $this->assertEquals('camel_case', Inflector::getDefault()->snake('CamelCase')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCaseWords')); + $this->assertEquals('camel_case_words', Inflector::getDefault()->snake('CamelCase_words')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('test', Inflector::getDefault()->snake('test')); + $this->assertEquals('expect100_continue', Inflector::getDefault()->snake('Expect100Continue')); + } + + public function testCamel() + { + $this->assertEquals('CamelCase', Inflector::getDefault()->camel('camel_case')); + $this->assertEquals('CamelCaseWords', Inflector::getDefault()->camel('camel_case_words')); + $this->assertEquals('Test', Inflector::getDefault()->camel('test')); + $this->assertEquals('Expect100Continue', ucfirst(Inflector::getDefault()->camel('expect100_continue'))); + // Get from cache + $this->assertEquals('Test', Inflector::getDefault()->camel('test', false)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/MemoizingInflectorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/MemoizingInflectorTest.php new file mode 100644 index 0000000..b1e08cd --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/MemoizingInflectorTest.php @@ -0,0 +1,46 @@ +getMock('Guzzle\Common\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->will($this->returnValue('foo_bar')); + $mock->expects($this->once())->method('camel')->will($this->returnValue('FooBar')); + + $inflector = new MemoizingInflector($mock); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + } + + public function testProtectsAgainstCacheOverflow() + { + $inflector = new MemoizingInflector(new Inflector(), 10); + for ($i = 1; $i < 11; $i++) { + $inflector->camel('foo_' . $i); + $inflector->snake('Foo' . $i); + } + + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(10, count($cache['snake'])); + $this->assertEquals(10, count($cache['camel'])); + + $inflector->camel('baz!'); + $inflector->snake('baz!'); + + // Now ensure that 20% of the cache was removed (2), then the item was added + $cache = $this->readAttribute($inflector, 'cache'); + $this->assertEquals(9, count($cache['snake'])); + $this->assertEquals(9, count($cache['camel'])); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/PreComputedInflectorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/PreComputedInflectorTest.php new file mode 100644 index 0000000..d6d9c88 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Inflection/PreComputedInflectorTest.php @@ -0,0 +1,45 @@ +getMock('Guzzle\Common\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->once())->method('snake')->with('Test')->will($this->returnValue('test')); + $mock->expects($this->once())->method('camel')->with('Test')->will($this->returnValue('Test')); + $inflector = new PreComputedInflector($mock, array('FooBar' => 'foo_bar'), array('foo_bar' => 'FooBar')); + $this->assertEquals('FooBar', $inflector->camel('foo_bar')); + $this->assertEquals('foo_bar', $inflector->snake('FooBar')); + $this->assertEquals('Test', $inflector->camel('Test')); + $this->assertEquals('test', $inflector->snake('Test')); + } + + public function testMirrorsPrecomputedValues() + { + $mock = $this->getMock('Guzzle\Common\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array(), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + } + + public function testMirrorsPrecomputedValuesByMerging() + { + $mock = $this->getMock('Guzzle\Common\Inflection\Inflector', array('snake', 'camel')); + $mock->expects($this->never())->method('snake'); + $mock->expects($this->never())->method('camel'); + $inflector = new PreComputedInflector($mock, array('Zeep' => 'zeep'), array('foo' => 'Foo'), true); + $this->assertEquals('Zeep', $inflector->camel('zeep')); + $this->assertEquals('zeep', $inflector->snake('Zeep')); + $this->assertEquals('Foo', $inflector->camel('foo')); + $this->assertEquals('foo', $inflector->snake('Foo')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/ArrayLogAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/ArrayLogAdapterTest.php new file mode 100644 index 0000000..59b1de6 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/ArrayLogAdapterTest.php @@ -0,0 +1,26 @@ +log('test', \LOG_NOTICE, 'localhost'); + $this->assertEquals(array(array('message' => 'test', 'priority' => \LOG_NOTICE, 'extras' => 'localhost')), $adapter->getLogs()); + } + + public function testClearLog() + { + $adapter = new ArrayLogAdapter(); + + $adapter->log('test', \LOG_NOTICE, 'localhost'); + $adapter->clearLogs(); + $this->assertEquals(array(), $adapter->getLogs()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/ClosureLogAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/ClosureLogAdapterTest.php new file mode 100644 index 0000000..c09ae06 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/ClosureLogAdapterTest.php @@ -0,0 +1,39 @@ +adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that) { + $that->modified = array($message, $priority, $extras); + }); + + $this->adapter->log('test', \LOG_NOTICE, 'localhost'); + $this->assertEquals(array('test', \LOG_NOTICE, 'localhost'), $this->modified); + } + + /** + * @covers \Guzzle\Common\Log\ClosureLogAdapter + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenNotCallable() + { + $abc = 123; + $this->adapter = new ClosureLogAdapter($abc); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/MonologLogAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/MonologLogAdapterTest.php new file mode 100644 index 0000000..7a5eb6f --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/MonologLogAdapterTest.php @@ -0,0 +1,28 @@ +pushHandler($handler); + $adapter = new MonologLogAdapter($log); + + $adapter->log('test!', LOG_INFO); + + $this->assertTrue($handler->hasInfoRecords()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zend_Log.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zend_Log.php new file mode 100644 index 0000000..3457c2c --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zend_Log.php @@ -0,0 +1,634 @@ +_priorities = array_flip($r->getConstants()); + + if ($writer !== null) { + $this->addWriter($writer); + } + } + + /** + * Factory to construct the logger and one or more writers + * based on the configuration array + * + * @param array|Zend_Config Array or instance of Zend_Config + * @return Zend_Log + * @throws Zend_Log_Exception + */ + static public function factory($config = array()) + { + if ($config instanceof Zend_Config) { + $config = $config->toArray(); + } + + if (!is_array($config) || empty($config)) { + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception('Configuration must be an array or instance of Zend_Config'); + } + + $log = new self; + + if (array_key_exists('timestampFormat', $config)) { + if (null != $config['timestampFormat'] && '' != $config['timestampFormat']) { + $log->setTimestampFormat($config['timestampFormat']); + } + unset($config['timestampFormat']); + } + + if (!is_array(current($config))) { + $log->addWriter(current($config)); + } else { + foreach($config as $writer) { + $log->addWriter($writer); + } + } + + return $log; + } + + + /** + * Construct a writer object based on a configuration array + * + * @param array $spec config array with writer spec + * @return Zend_Log_Writer_Abstract + * @throws Zend_Log_Exception + */ + protected function _constructWriterFromConfig($config) + { + $writer = $this->_constructFromConfig('writer', $config, $this->_defaultWriterNamespace); + + if (!$writer instanceof Zend_Log_Writer_Abstract) { + $writerName = is_object($writer) + ? get_class($writer) + : 'The specified writer'; + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception("{$writerName} does not extend Zend_Log_Writer_Abstract!"); + } + + if (isset($config['filterName'])) { + $filter = $this->_constructFilterFromConfig($config); + $writer->addFilter($filter); + } + + if (isset($config['formatterName'])) { + $formatter = $this->_constructFormatterFromConfig($config); + $writer->setFormatter($formatter); + } + + return $writer; + } + + /** + * Construct filter object from configuration array or Zend_Config object + * + * @param array|Zend_Config $config Zend_Config or Array + * @return Zend_Log_Filter_Interface + * @throws Zend_Log_Exception + */ + protected function _constructFilterFromConfig($config) + { + $filter = $this->_constructFromConfig('filter', $config, $this->_defaultFilterNamespace); + + if (!$filter instanceof Zend_Log_Filter_Interface) { + $filterName = is_object($filter) + ? get_class($filter) + : 'The specified filter'; + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception("{$filterName} does not implement Zend_Log_Filter_Interface"); + } + + return $filter; + } + + /** + * Construct formatter object from configuration array or Zend_Config object + * + * @param array|Zend_Config $config Zend_Config or Array + * @return Zend_Log_Formatter_Interface + * @throws Zend_Log_Exception + */ + protected function _constructFormatterFromConfig($config) + { + $formatter = $this->_constructFromConfig('formatter', $config, $this->_defaultFormatterNamespace); + + if (!$formatter instanceof Zend_Log_Formatter_Interface) { + $formatterName = is_object($formatter) + ? get_class($formatter) + : 'The specified formatter'; + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception($formatterName . ' does not implement Zend_Log_Formatter_Interface'); + } + + return $formatter; + } + + /** + * Construct a filter or writer from config + * + * @param string $type 'writer' of 'filter' + * @param mixed $config Zend_Config or Array + * @param string $namespace + * @return object + * @throws Zend_Log_Exception + */ + protected function _constructFromConfig($type, $config, $namespace) + { + if ($config instanceof Zend_Config) { + $config = $config->toArray(); + } + + if (!is_array($config) || empty($config)) { + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception( + 'Configuration must be an array or instance of Zend_Config' + ); + } + + $params = isset($config[ $type .'Params' ]) ? $config[ $type .'Params' ] : array(); + $className = $this->getClassName($config, $type, $namespace); + if (!class_exists($className)) { + require_once 'Zend/Loader.php'; + Zend_Loader::loadClass($className); + } + + $reflection = new ReflectionClass($className); + if (!$reflection->implementsInterface('Zend_Log_FactoryInterface')) { + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception( + $className . ' does not implement Zend_Log_FactoryInterface and can not be constructed from config.' + ); + } + + return call_user_func(array($className, 'factory'), $params); + } + + /** + * Get the writer or filter full classname + * + * @param array $config + * @param string $type filter|writer + * @param string $defaultNamespace + * @return string full classname + * @throws Zend_Log_Exception + */ + protected function getClassName($config, $type, $defaultNamespace) + { + if (!isset($config[$type . 'Name'])) { + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception("Specify {$type}Name in the configuration array"); + } + + $className = $config[$type . 'Name']; + $namespace = $defaultNamespace; + + if (isset($config[$type . 'Namespace'])) { + $namespace = $config[$type . 'Namespace']; + } + + // PHP >= 5.3.0 namespace given? + if (substr($namespace, -1) == '\\') { + return $namespace . $className; + } + + // emtpy namespace given? + if (strlen($namespace) === 0) { + return $className; + } + + return $namespace . '_' . $className; + } + + /** + * Packs message and priority into Event array + * + * @param string $message Message to log + * @param integer $priority Priority of message + * @return array Event array + */ + protected function _packEvent($message, $priority) + { + return array_merge(array( + 'timestamp' => date($this->_timestampFormat), + 'message' => $message, + 'priority' => $priority, + 'priorityName' => $this->_priorities[$priority] + ), + $this->_extras + ); + } + + /** + * Class destructor. Shutdown log writers + * + * @return void + */ + public function __destruct() + { + foreach($this->_writers as $writer) { + $writer->shutdown(); + } + } + + /** + * Undefined method handler allows a shortcut: + * $log->priorityName('message') + * instead of + * $log->log('message', Zend_Log::PRIORITY_NAME) + * + * @param string $method priority name + * @param string $params message to log + * @return void + * @throws Zend_Log_Exception + */ + public function __call($method, $params) + { + $priority = strtoupper($method); + if (($priority = array_search($priority, $this->_priorities)) !== false) { + switch (count($params)) { + case 0: + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception('Missing log message'); + case 1: + $message = array_shift($params); + $extras = null; + break; + default: + $message = array_shift($params); + $extras = array_shift($params); + break; + } + $this->log($message, $priority, $extras); + } else { + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception('Bad log priority'); + } + } + + /** + * Log a message at a priority + * + * @param string $message Message to log + * @param integer $priority Priority of message + * @param mixed $extras Extra information to log in event + * @return void + * @throws Zend_Log_Exception + */ + public function log($message, $priority, $extras = null) + { + // sanity checks + if (empty($this->_writers)) { + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception('No writers were added'); + } + + if (! isset($this->_priorities[$priority])) { + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception('Bad log priority'); + } + + // pack into event required by filters and writers + $event = $this->_packEvent($message, $priority); + + // Check to see if any extra information was passed + if (!empty($extras)) { + $info = array(); + if (is_array($extras)) { + foreach ($extras as $key => $value) { + if (is_string($key)) { + $event[$key] = $value; + } else { + $info[] = $value; + } + } + } else { + $info = $extras; + } + if (!empty($info)) { + $event['info'] = $info; + } + } + + // abort if rejected by the global filters + foreach ($this->_filters as $filter) { + if (! $filter->accept($event)) { + return; + } + } + + // send to each writer + foreach ($this->_writers as $writer) { + $writer->write($event); + } + } + + /** + * Add a custom priority + * + * @param string $name Name of priority + * @param integer $priority Numeric priority + * @throws Zend_Log_Exception + */ + public function addPriority($name, $priority) + { + // Priority names must be uppercase for predictability. + $name = strtoupper($name); + + if (isset($this->_priorities[$priority]) + || false !== array_search($name, $this->_priorities)) { + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception('Existing priorities cannot be overwritten'); + } + + $this->_priorities[$priority] = $name; + return $this; + } + + /** + * Add a filter that will be applied before all log writers. + * Before a message will be received by any of the writers, it + * must be accepted by all filters added with this method. + * + * @param int|Zend_Config|array|Zend_Log_Filter_Interface $filter + * @return Zend_Log + * @throws Zend_Log_Exception + */ + public function addFilter($filter) + { + if (is_int($filter)) { + /** @see Zend_Log_Filter_Priority */ + require_once 'Zend/Log/Filter/Priority.php'; + $filter = new Zend_Log_Filter_Priority($filter); + + } elseif ($filter instanceof Zend_Config || is_array($filter)) { + $filter = $this->_constructFilterFromConfig($filter); + + } elseif(! $filter instanceof Zend_Log_Filter_Interface) { + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception('Invalid filter provided'); + } + + $this->_filters[] = $filter; + return $this; + } + + /** + * Add a writer. A writer is responsible for taking a log + * message and writing it out to jar. + * + * @param mixed $writer Zend_Log_Writer_Abstract or Config array + * @return Zend_Log + */ + public function addWriter($writer) + { + if (is_array($writer) || $writer instanceof Zend_Config) { + $writer = $this->_constructWriterFromConfig($writer); + } + + if (!$writer instanceof Zend_Log_Writer_Abstract) { + /** @see Zend_Log_Exception */ + require_once 'Zend/Log/Exception.php'; + throw new Zend_Log_Exception( + 'Writer must be an instance of Zend_Log_Writer_Abstract' + . ' or you should pass a configuration array' + ); + } + + $this->_writers[] = $writer; + return $this; + } + + /** + * Set an extra item to pass to the log writers. + * + * @param string $name Name of the field + * @param string $value Value of the field + * @return Zend_Log + */ + public function setEventItem($name, $value) + { + $this->_extras = array_merge($this->_extras, array($name => $value)); + return $this; + } + + /** + * Register Logging system as an error handler to log php errors + * Note: it still calls the original error handler if set_error_handler is able to return it. + * + * Errors will be mapped as: + * E_NOTICE, E_USER_NOTICE => NOTICE + * E_WARNING, E_CORE_WARNING, E_USER_WARNING => WARN + * E_ERROR, E_USER_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR => ERR + * E_DEPRECATED, E_STRICT, E_USER_DEPRECATED => DEBUG + * (unknown/other) => INFO + * + * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler + * + * @return Zend_Log + */ + public function registerErrorHandler() + { + // Only register once. Avoids loop issues if it gets registered twice. + if ($this->_registeredErrorHandler) { + return $this; + } + + $this->_origErrorHandler = set_error_handler(array($this, 'errorHandler')); + + // Construct a default map of phpErrors to Zend_Log priorities. + // Some of the errors are uncatchable, but are included for completeness + $this->_errorHandlerMap = array( + E_NOTICE => Zend_Log::NOTICE, + E_USER_NOTICE => Zend_Log::NOTICE, + E_WARNING => Zend_Log::WARN, + E_CORE_WARNING => Zend_Log::WARN, + E_USER_WARNING => Zend_Log::WARN, + E_ERROR => Zend_Log::ERR, + E_USER_ERROR => Zend_Log::ERR, + E_CORE_ERROR => Zend_Log::ERR, + E_RECOVERABLE_ERROR => Zend_Log::ERR, + E_STRICT => Zend_Log::DEBUG, + ); + // PHP 5.3.0+ + if (defined('E_DEPRECATED')) { + $this->_errorHandlerMap['E_DEPRECATED'] = Zend_Log::DEBUG; + } + if (defined('E_USER_DEPRECATED')) { + $this->_errorHandlerMap['E_USER_DEPRECATED'] = Zend_Log::DEBUG; + } + + $this->_registeredErrorHandler = true; + return $this; + } + + /** + * Error Handler will convert error into log message, and then call the original error handler + * + * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + * @return boolean + */ + public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + $errorLevel = error_reporting(); + + if ($errorLevel && $errno) { + if (isset($this->_errorHandlerMap[$errno])) { + $priority = $this->_errorHandlerMap[$errno]; + } else { + $priority = Zend_Log::INFO; + } + $this->log($errstr, $priority, array('errno'=>$errno, 'file'=>$errfile, 'line'=>$errline, 'context'=>$errcontext)); + } + + if ($this->_origErrorHandler !== null) { + return call_user_func($this->_origErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext); + } + return false; + } + + /** + * Set timestamp format for log entries. + * + * @param string $format + * @return Zend_Log + */ + public function setTimestampFormat($format) + { + $this->_timestampFormat = $format; + return $this; + } + + /** + * Get timestamp format used for log entries. + * + * @return string + */ + public function getTimestampFormat() + { + return $this->_timestampFormat; + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zf1LogAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zf1LogAdapterTest.php new file mode 100644 index 0000000..a7dc943 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zf1LogAdapterTest.php @@ -0,0 +1,67 @@ +stream = fopen('php://temp', 'r+'); + $this->log = new \Zend_Log(new \Zend_Log_Writer_Stream($this->stream)); + $this->adapter = new Zf1LogAdapter($this->log); + } + + /** + * @covers Guzzle\Common\Log\Zf1LogAdapter::log + */ + public function testLogsMessagesToAdaptedObject() + { + // Test without a priority + $this->adapter->log('test', \LOG_NOTICE, 'guzzle.common.log.adapter.zend_log_adapter', 'localhost'); + rewind($this->stream); + $this->assertEquals(1, substr_count(stream_get_contents($this->stream), 'test')); + + // Test with a priority + $this->adapter->log('test', \LOG_ALERT); + rewind($this->stream); + $this->assertEquals(2, substr_count(stream_get_contents($this->stream), 'test')); + } + + /** + * @covers Guzzle\Common\Log\AbstractLogAdapter::getLogObject + */ + public function testExposesAdaptedLogObject() + { + $this->assertEquals($this->log, $this->adapter->getLogObject()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zf2LogAdapterTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zf2LogAdapterTest.php new file mode 100644 index 0000000..c13188c --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Log/Zf2LogAdapterTest.php @@ -0,0 +1,67 @@ +stream = fopen('php://temp', 'r+'); + $this->log = new Logger(); + $this->log->addWriter(new Stream($this->stream)); + $this->adapter = new Zf2LogAdapter($this->log); + + } + + /** + * @covers Guzzle\Common\Log\Zf2LogAdapter::log + */ + public function testLogsMessagesToAdaptedObject() + { + // Test without a priority + $this->adapter->log('Zend_Test!', \LOG_NOTICE); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(1, substr_count($contents, 'Zend_Test!')); + + // Test with a priority + $this->adapter->log('Zend_Test!', \LOG_ALERT); + rewind($this->stream); + $contents = stream_get_contents($this->stream); + $this->assertEquals(2, substr_count($contents, 'Zend_Test!')); + } + + /** + * @covers Guzzle\Common\Log\AbstractLogAdapter::getLogObject + */ + public function testExposesAdaptedLogObject() + { + $this->assertEquals($this->log, $this->adapter->getLogObject()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/StreamTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/StreamTest.php new file mode 100644 index 0000000..cb016af --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/StreamTest.php @@ -0,0 +1,165 @@ +assertEquals($handle, $stream->getStream()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->isWritable()); + $this->assertTrue($stream->isLocal()); + $this->assertTrue($stream->isSeekable()); + $this->assertEquals('php', $stream->getWrapper()); + $this->assertEquals('temp', $stream->getStreamType()); + $this->assertEquals(4, $stream->getSize()); + $this->assertEquals('php://temp', $stream->getUri()); + $this->assertEquals(array(), $stream->getWrapperData()); + $this->assertFalse($stream->isConsumed()); + unset($stream); + } + + /** + * @covers Guzzle\Common\Stream::__destruct + */ + public function testStreamClosesHandleOnDestruct() + { + $handle = fopen('php://temp', 'r'); + $stream = new Stream($handle); + unset($stream); + $this->assertFalse(is_resource($handle)); + } + + /** + * @covers Guzzle\Common\Stream::__toString + */ + public function testConvertsToString() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertEquals('data', (string)$stream); + unset($stream); + + $handle = fopen(__DIR__ . '/../TestData/FileBody.txt', 'w'); + $stream = new Stream($handle); + $this->assertEquals('', (string)$stream); + unset($stream); + } + + /** + * @covers Guzzle\Common\Stream::isConsumed + */ + public function testIsConsumed() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertFalse($stream->isConsumed()); + $stream->read(4); + $this->assertTrue($stream->isConsumed()); + } + + /** + * @covers Guzzle\Common\Stream::setSize + * @covers Guzzle\Common\Stream::getSize + */ + public function testAllowsSettingManualSize() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $stream->setSize(10); + $this->assertEquals(10, $stream->getSize()); + unset($stream); + } + + /** + * @covers Guzzle\Common\Stream::read + * @covers Guzzle\Common\Stream::write + * @covers Guzzle\Common\Stream::seek + * @covers Guzzle\Common\Stream::isReadable + * @covers Guzzle\Common\Stream::isSeekable + */ + public function testWrapsStream() + { + $handle = fopen('php://temp', 'w+'); + fwrite($handle, 'data'); + $stream = new Stream($handle); + $this->assertTrue($stream->isSeekable()); + $this->assertTrue($stream->isReadable()); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('da', $stream->read(2)); + $this->assertEquals('ta', $stream->read(2)); + $this->assertTrue($stream->seek(0)); + $this->assertEquals('data', $stream->read(4)); + $stream->write('_appended'); + $stream->seek(0); + $this->assertEquals('data_appended', $stream->read(13)); + } + + /** + * @covers Guzzle\Common\Stream::getSize + * @covers Guzzle\Common\Stream::__construct + */ + public function testGetSize() + { + $size = filesize(__DIR__ . '/../../../bootstrap.php'); + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals($handle, $stream->getStream()); + $this->assertEquals($size, $stream->getSize()); + $this->assertEquals($size, $stream->getSize()); + unset($stream); + + // Make sure that false is returned when the size cannot be determined + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Lenght: 0\r\n\r\n"); + $handle = fopen('http://localhost:' . $this->getServer()->getPort(), 'r'); + $stream = new Stream($handle); + $this->assertEquals(false, $stream->getSize()); + unset($stream); + } + + /** + * @covers Guzzle\Common\Stream::getMetaData + */ + public function testAbstractsMetaData() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals('plainfile', $stream->getMetaData('wrapper_type')); + $this->assertEquals(null, $stream->getMetaData('wrapper_data')); + $this->assertInternalType('array', $stream->getMetaData()); + } + + /** + * @covers Guzzle\Common\Stream::write + */ + public function testDoesNotAttemptToWriteToReadonlyStream() + { + $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); + $stream = new Stream($handle); + $this->assertEquals(0, $stream->write('foo')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/AnyMatchTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/AnyMatchTest.php new file mode 100644 index 0000000..c60b526 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/AnyMatchTest.php @@ -0,0 +1,39 @@ +markTestSkipped('Inspector not present'); + } + + $i = Inspector::getInstance(); + + return array( + array($c, 'a', array('constraints' => 'type:string', 'inspector' => $i), true, null), + array($c, 'a', array('type:string', 'inspector' => $i), true, null), + array($c, 'foo', array('constraints' => 'type:string;type:numeric'), true, null), + array($c, new \stdClass(), array('constraints' => 'type:string;type:numeric', 'inspector' => $i), 'Value type must match one of type:string OR type:numeric', null), + array($c, 'foo', array('constraints' => 'type:numeric;type:boolean;ip;email', 'inspector' => $i), 'Value type must match one of type:numeric OR type:boolean OR ip OR email', null), + array($c, 'http://www.example.com', array('constraints' => 'ip;url', 'inspector' => $i), true, null), + array($c, '192.168.16.148', array('constraints' => 'ip;url', 'inspector' => $i), true, null), + array($c, 'foo', array('constraints' => 'email;choice:foo,bar;ip;array', 'inspector' => $i), true, null), + array($c, 'bar', array('constraints' => 'email;choice:foo,bar;ip;array', 'inspector' => $i), true, null), + array($c, '192.168.16.48', array('constraints' => 'email;choice:foo,bar;ip;array', 'inspector' => $i), true, null), + array($c, array(), array('constraints' => 'email;choice:foo,bar;ip;array', 'inspector' => $i), true, null), + array($c, 'michael@awesome.com', array('constraints' => 'email;choice:foo,bar;ip;array', 'inspector' => $i), true, null), + array($c, new \stdClass(), array('constraints' => 'type:object', 'inspector' => $i), true, null) + ); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/BlankTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/BlankTest.php new file mode 100644 index 0000000..ca37e31 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/BlankTest.php @@ -0,0 +1,20 @@ + array('foo', 'bar')), true, null), + array($c, 'baz', array('options' => array('foo', 'bar')), 'Value must be one of: foo, bar', null) + ); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/CtypeTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/CtypeTest.php new file mode 100644 index 0000000..ab1bcf9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/CtypeTest.php @@ -0,0 +1,23 @@ + 'alpha'), true, null), + array($c, 'a', array('alpha'), true, null), + array($c, '2', array('type' => 'alpha'), 'Value must be of type alpha', null), + array($c, ' ', array('type' => 'space'), true, null), + array($c, 'a', array('type' => 'foo'), null, 'Guzzle\Common\Exception\InvalidArgumentException') + ); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/EmailTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/EmailTest.php new file mode 100644 index 0000000..a12e3e0 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/EmailTest.php @@ -0,0 +1,18 @@ + 'stdClass'), 'Value must be an instance of stdClass', null), + array($c, new \stdClass(), array('class' => 'stdClass'), true, null), + array($c, new IsInstanceOf(), array('class' => 'Guzzle.Common.Validation.IsInstanceOf'), true, null), + array($c, 'a', null, true, 'Guzzle\Common\Exception\InvalidArgumentException'), + array($c, new \stdClass(), null, true, 'Guzzle\Common\Exception\InvalidArgumentException') + ); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/NotBlankTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/NotBlankTest.php new file mode 100644 index 0000000..9c285ed --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/NotBlankTest.php @@ -0,0 +1,19 @@ + '/[a-z]+/'), true, null), + array($c, 'foo', array('/[a-z]+/'), true, null), + array($c, 'foo', array('pattern' => '/[0-9]+/'), 'foo does not match the regular expression', null), + array($c, 'baz', null, null, 'Guzzle\Common\Exception\InvalidArgumentException') + ); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/StringTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/StringTest.php new file mode 100644 index 0000000..60fb0ad --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/StringTest.php @@ -0,0 +1,19 @@ + 'string'), true, null), + array($c, 'a', array('string'), true, null), + array($c, '2', array('type' => 'array'), 'Value must be of type array', null) + ); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/UrlTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/UrlTest.php new file mode 100644 index 0000000..3e76601 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Common/Validation/UrlTest.php @@ -0,0 +1,19 @@ +validate($value, $options); + $this->assertEquals($result, $r); + } catch (\Exception $e) { + if (!$exception) { + throw $e; + } + + if (!($e instanceof $exception)) { + throw $e; + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/GuzzleTestCase.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/GuzzleTestCase.php new file mode 100644 index 0000000..571386f --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/GuzzleTestCase.php @@ -0,0 +1,230 @@ +isRunning()) { + self::$server->flush(); + } else { + self::$server->start(); + } + } catch (\Exception $e) { + fwrite(STDERR, $e->getMessage()); + } + } + + return self::$server; + } + + /** + * Set the service builder to use for tests + * + * @param ServiceBuilder $builder Service builder + */ + public static function setServiceBuilder(ServiceBuilderInterface $builder) + { + self::$serviceBuilder = $builder; + } + + /** + * Get a service builder object that can be used throughout the service tests + * + * @return ServiceBuilder + */ + public static function getServiceBuilder() + { + if (!self::$serviceBuilder) { + throw new RuntimeException('No service builder has been set via setServiceBuilder()'); + } + + return self::$serviceBuilder; + } + + /** + * Check if an event dispatcher has a subscriber + * + * @param HasDispatcherInterface $dispatcher + * @param EventSubscriberInterface $subscriber + * + * @return bool + */ + protected function hasSubscriber(HasDispatcherInterface $dispatcher, EventSubscriberInterface $subscriber) + { + $class = get_class($subscriber); + $all = array_keys(call_user_func(array($class, 'getSubscribedEvents'))); + + foreach ($all as $i => $event) { + foreach ($dispatcher->getEventDispatcher()->getListeners($event) as $e) { + if ($e[0] === $subscriber) { + unset($all[$i]); + break; + } + } + } + + return count($all) == 0; + } + + /** + * Get a wildcard observer for an event dispatcher + * + * @param HasEventDispatcherInterface $hasEvent + * + * @return MockObserver + */ + public function getWildcardObserver(HasDispatcherInterface $hasDispatcher) + { + $class = get_class($hasDispatcher); + $o = new MockObserver(); + $events = call_user_func(array($class, 'getAllEvents')); + foreach ($events as $event) { + $hasDispatcher->getEventDispatcher()->addListener($event, array($o, 'update')); + } + + return $o; + } + + /** + * Set the mock response base path + * + * @param string $path Path to mock response folder + * + * @return GuzzleTestCase + */ + public static function setMockBasePath($path) + { + self::$mockBasePath = $path; + } + + /** + * Mark a request as being mocked + * + * @param RequestInterface $request + */ + public function addMockedRequest(RequestInterface $request) + { + $this->requests[] = $request; + + return $this; + } + + /** + * Get all of the mocked requests + * + * @return array + */ + public function getMockedRequests() + { + return $this->requests; + } + + /** + * Get a mock response for a client by mock file name + * + * @param string $path Relative path to the mock response file + * + * @return Response + */ + public function getMockResponse($path) + { + return MockPlugin::getMockFile(self::$mockBasePath . DIRECTORY_SEPARATOR . $path); + } + + /** + * Set a mock response from a mock file on the next client request. + * + * This method assumes that mock response files are located under the + * Command/Mock/ directory of the Service being tested + * (e.g. Unfuddle/Command/Mock/). A mock response is added to the next + * request sent by the client. + * + * @param Client $client Client object to modify + * @param string $paths Path to files within the Mock folder of the service + * + * @return MockPlugin returns the created mock plugin + */ + public function setMockResponse(Client $client, $paths) + { + $this->requests = array(); + $that = $this; + $mock = new MockPlugin(null, true); + $client->getEventDispatcher()->removeSubscriber($mock); + $mock->getEventDispatcher()->addListener('mock.request', function(Event $event) use ($that) { + $that->addMockedRequest($event['request']); + }); + + foreach ((array) $paths as $path) { + $mock->addResponse($this->getMockResponse($path)); + } + + $client->getEventDispatcher()->addSubscriber($mock); + + return $mock; + } + + /** + * Compare HTTP headers and use special markup to filter values + * A header prefixed with '!' means it must not exist + * A header prefixed with '_' means it must be ignored + * A header value of '*' means anything after the * will be ignored + * + * @param array $filteredHeaders Array of special headers + * @param array $actualHeaders Array of headers to check against + * + * @return array|false Returns an array of the differences or FALSE if none + */ + public function compareHeaders($filteredHeaders, $actualHeaders) + { + $comparison = new HeaderComparison(); + + return $comparison->compare($filteredHeaders, $actualHeaders); + } + + /** + * Case insensitive assertContains + * + * @param string $needle Search string + * @param string $haystack Search this + * @param string $message Optional failure message + */ + public function assertContainsIns($needle, $haystack, $message = null) + { + $this->assertContains(strtolower($needle), strtolower($haystack), $message); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/BatchRequestTransferTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/BatchRequestTransferTest.php new file mode 100644 index 0000000..b93285e --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/BatchRequestTransferTest.php @@ -0,0 +1,76 @@ +setCurlMulti(new CurlMulti()); + + $client2 = new Client('http://www.example.com'); + $client2->setCurlMulti(new CurlMulti()); + + $request1 = $client1->get(); + $request2 = $client2->get(); + $request3 = $client1->get(); + $request4 = $client2->get(); + $request5 = $client1->get(); + + $queue = new \SplQueue(); + $queue[] = $request1; + $queue[] = $request2; + $queue[] = $request3; + $queue[] = $request4; + $queue[] = $request5; + + $batch = new BatchRequestTransfer(2); + $this->assertEquals(array( + array($request1, $request3), + array($request3), + array($request2, $request4) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreRequests() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchRequestTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = new Client('http://localhost:123'); + $request = $client->get(); + + $multi = $this->getMock('Guzzle\Http\Curl\CurlMultiInterface'); + $client->setCurlMulti($multi); + $multi->expects($this->once()) + ->method('add') + ->with($request); + $multi->expects($this->once()) + ->method('send'); + + $batch = new BatchRequestTransfer(2); + $batch->transfer(array($request)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchRequestTransfer(2); + $batch->transfer(array()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php new file mode 100644 index 0000000..b9f4303 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/ClientTest.php @@ -0,0 +1,592 @@ +assertEquals('http://www.google.com/', $client->getBaseUrl()); + $this->assertSame($client, $client->setConfig(array( + 'test' => '123' + ))); + $this->assertEquals(array('test' => '123'), $client->getConfig()->getAll()); + $this->assertEquals('123', $client->getConfig('test')); + $this->assertSame($client, $client->setBaseUrl('http://www.test.com/{{test}}')); + $this->assertEquals('http://www.test.com/123', $client->getBaseUrl()); + $this->assertEquals('http://www.test.com/{{test}}', $client->getBaseUrl(false)); + + try { + $client->setConfig(false); + } catch (\InvalidArgumentException $e) { + } + } + + /** + * @covers Guzzle\Http\Client::getAllEvents + */ + public function testDescribesEvents() + { + $this->assertEquals(array('client.create_request'), Client::getAllEvents()); + } + + /** + * @covers Guzzle\Http\Client::__toString + */ + public function testConvertsToString() + { + $client = new Client(); + $hash = spl_object_hash($client); + $this->assertEquals($hash, (string) $client); + } + + /** + * @covers Guzzle\Http\Client::__construct + */ + public function testConstructorCanAcceptConfig() + { + $client = new Client('http://www.test.com/', array( + 'data' => '123' + )); + $this->assertEquals('123', $client->getConfig('data')); + } + + /** + * @covers Guzzle\Http\Client::setConfig + */ + public function testCanUseCollectionAsConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(new Collection(array( + 'api' => 'v1', + 'key' => 'value', + 'base_url' => 'http://www.google.com/' + ))); + $this->assertEquals('v1', $client->getConfig('api')); + } + + /** + * @covers Guzzle\Http\Client + */ + public function testExpandsUriTemplatesUsingConfig() + { + $client = new Client('http://www.google.com/'); + $client->setConfig(array( + 'api' => 'v1', + 'key' => 'value', + 'foo' => 'bar' + )); + $this->assertEquals('Testing...api/v1/key/value', $client->expandTemplate('Testing...api/{api}/key/{{key}}')); + + // Make sure that the client properly validates and injects config + $this->assertEquals('bar', $client->getConfig('foo')); + } + + /** + * @covers Guzzle\Http\Client::__construct + * @covers Guzzle\Http\Client::createRequest + */ + public function testClientAttachersObserversToRequests() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $logPlugin = $this->getLogPlugin(); + $client->getEventDispatcher()->addSubscriber($logPlugin); + + // Get a request from the client and ensure the the observer was + // attached to the new request + $request = $client->createRequest(); + $this->assertTrue($this->hasSubscriber($request, $logPlugin)); + } + + /** + * @covers Guzzle\Http\Client::getBaseUrl + * @covers Guzzle\Http\Client::setBaseUrl + */ + public function testClientReturnsValidBaseUrls() + { + $client = new Client('http://www.{{foo}}.{{data}}/', array( + 'data' => '123', + 'foo' => 'bar' + )); + $this->assertEquals('http://www.bar.123/', $client->getBaseUrl()); + $client->setBaseUrl('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $client->getBaseUrl()); + } + + /** + * @covers Guzzle\Http\Client::setUserAgent + * @covers Guzzle\Http\Client::createRequest + * @covers Guzzle\Http\Client::prepareRequest + */ + public function testSetsUserAgent() + { + $client = new Client('http://www.test.com/', array( + 'api' => 'v1' + )); + + $this->assertSame($client, $client->setUserAgent('Test/1.0Ab', true)); + $this->assertEquals('Test/1.0Ab ' . Utils::getDefaultUserAgent(), $client->get()->getHeader('User-Agent')); + $client->setUserAgent('Test/1.0Ab'); + $this->assertEquals('Test/1.0Ab', $client->get()->getHeader('User-Agent')); + } + + /** + * @covers Guzzle\Http\Client::createRequest + * @covers Guzzle\Http\Client::prepareRequest + */ + public function testClientAddsCurlOptionsToRequests() + { + $client = new Client('http://www.test.com/', array( + 'api' => 'v1', + // Adds the option using the curl values + 'curl.CURLOPT_HTTPAUTH' => 'CURLAUTH_DIGEST', + 'curl.abc' => 'not added', + 'curl.blacklist' => 'abc' + )); + + $request = $client->createRequest(); + $options = $request->getCurlOptions(); + $this->assertEquals(CURLAUTH_DIGEST, $options->get(CURLOPT_HTTPAUTH)); + $this->assertNull($options->get('curl.abc')); + $this->assertNull($options->get('curl.blacklist')); + } + + /** + * @covers Guzzle\Http\Client::createRequest + * @covers Guzzle\Http\Client::prepareRequest + */ + public function testClientAddsCustomCurlOptionsToRequests() + { + $client = new Client('http://www.test.com/', array( + 'api' => 'v1', + 'curl.debug' => true, + 'curl.foo' => 'bar' + )); + $request = $client->createRequest(); + $this->assertTrue($request->getCurlOptions()->get('debug')); + $this->assertEquals('bar', $request->getCurlOptions()->get('foo')); + } + + /** + * @covers Guzzle\Http\Client::prepareRequest + */ + public function testClientAddsParamsToRequests() + { + $client = new Client('http://www.example.com', array( + 'api' => 'v1', + 'params.foo' => 'bar', + 'params.baz' => 'jar', + )); + $request = $client->createRequest(); + $this->assertEquals('bar', $request->getParams()->get('foo')); + $this->assertEquals('jar', $request->getParams()->get('baz')); + } + + public function urlProvider() + { + $u = $this->getServer()->getUrl() . 'base/'; + $u2 = $this->getServer()->getUrl() . 'base?z=1'; + return array( + array($u, '', $u), + array($u, 'relative/path/to/resource', $u . 'relative/path/to/resource'), + array($u, 'relative/path/to/resource?a=b&c=d', $u . 'relative/path/to/resource?a=b&c=d'), + array($u, '/absolute/path/to/resource', $this->getServer()->getUrl() . 'absolute/path/to/resource'), + array($u, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'), + array($u2, '/absolute/path/to/resource?a=b&c=d', $this->getServer()->getUrl() . 'absolute/path/to/resource?a=b&c=d'), + array($u2, 'relative/path/to/resource', $this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1'), + array($u2, 'relative/path/to/resource?another=query', $this->getServer()->getUrl() . 'base/relative/path/to/resource?z=1&another=query') + ); + } + + /** + * @dataProvider urlProvider + * @covers Guzzle\Http\Client::createRequest + */ + public function testBuildsRelativeUrls($baseUrl, $url, $result) + { + $client = new Client($baseUrl); + $this->assertEquals($client->get($url)->getUrl(), $result); + } + + /** + * @covers Guzzle\Http\Client + */ + public function testAllowsConfigsToBeChangedAndInjectedInBaseUrl() + { + $client = new Client('http://{{a}}/{{b}}'); + $this->assertEquals('http:///', $client->getBaseUrl()); + $this->assertEquals('http://{{a}}/{{b}}', $client->getBaseUrl(false)); + $client->setConfig(array( + 'a' => 'test.com', + 'b' => 'index.html' + )); + $this->assertEquals('http://test.com/index.html', $client->getBaseUrl()); + } + + /** + * @covers Guzzle\Http\Client::createRequest + */ + public function testCreatesRequestsWithDefaultValues() + { + $client = new Client($this->getServer()->getUrl() . 'base'); + + // Create a GET request + $request = $client->createRequest(); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a DELETE request + $request = $client->createRequest('DELETE'); + $this->assertEquals('DELETE', $request->getMethod()); + $this->assertEquals($client->getBaseUrl(), $request->getUrl()); + + // Create a HEAD request with custom headers + $request = $client->createRequest('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + $this->assertEquals('http://www.test.com/', $request->getUrl()); + + // Create a PUT request + $request = $client->createRequest('PUT'); + $this->assertEquals('PUT', $request->getMethod()); + + // Create a PUT request with injected config + $client->getConfig()->set('a', 1)->set('b', 2); + $request = $client->createRequest('PUT', '/path/{{a}}?q={{b}}'); + $this->assertEquals($request->getUrl(), $this->getServer()->getUrl() . 'path/1?q=2'); + } + + /** + * @covers Guzzle\Http\Client::get + * @covers Guzzle\Http\Client::delete + * @covers Guzzle\Http\Client::head + * @covers Guzzle\Http\Client::put + * @covers Guzzle\Http\Client::post + * @covers Guzzle\Http\Client::options + * @covers Guzzle\Http\Client::patch + */ + public function testClientHasHelperMethodsForCreatingRequests() + { + $url = $this->getServer()->getUrl(); + $client = new Client($url . 'base'); + $this->assertEquals('GET', $client->get()->getMethod()); + $this->assertEquals('PUT', $client->put()->getMethod()); + $this->assertEquals('POST', $client->post()->getMethod()); + $this->assertEquals('HEAD', $client->head()->getMethod()); + $this->assertEquals('DELETE', $client->delete()->getMethod()); + $this->assertEquals('OPTIONS', $client->options()->getMethod()); + $this->assertEquals('PATCH', $client->patch()->getMethod()); + $this->assertEquals($url . 'base/abc', $client->get('abc')->getUrl()); + $this->assertEquals($url . 'zxy', $client->put('/zxy')->getUrl()); + $this->assertEquals($url . 'zxy?a=b', $client->post('/zxy?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->head('?a=b')->getUrl()); + $this->assertEquals($url . 'base?a=b', $client->delete('/base?a=b')->getUrl()); + } + + /** + * @covers Guzzle\Http\Client::createRequest + */ + public function testClientInjectsConfigsIntoUrls() + { + $client = new Client('http://www.test.com/api/v1', array( + 'test' => '123' + )); + $request = $client->get('relative/{{test}}'); + $this->assertEquals('http://www.test.com/api/v1/relative/123', $request->getUrl()); + } + + /** + * @covers Guzzle\Http\Client + */ + public function testAllowsEmptyBaseUrl() + { + $client = new Client(); + $request = $client->get('http://www.google.com/'); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $request->setResponse(new Response(200), true); + $request->send(); + } + + /** + * @covers Guzzle\Http\Client::send + * @covers Guzzle\Http\Client::setCurlMulti + * @covers Guzzle\Http\Client::getCurlMulti + */ + public function testAllowsCustomCurlMultiObjects() + { + $mock = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('add', 'send')); + $mock->expects($this->once()) + ->method('add'); + $mock->expects($this->once()) + ->method('send'); + + $client = new Client(); + $client->setCurlMulti($mock); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $client->send($request); + } + + /** + * @covers Guzzle\Http\Client::send + */ + public function testClientSendsMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + + $responses = array( + new Response(200), + new Response(201), + new Response(202) + ); + + $mock->addResponse($responses[0]); + $mock->addResponse($responses[1]); + $mock->addResponse($responses[2]); + + $client->getEventDispatcher()->addSubscriber($mock); + + $requests = array( + $client->get(), + $client->head(), + $client->put('/', null, 'test') + ); + + $this->assertEquals(array( + $responses[0], + $responses[1], + $responses[2] + ), $client->send($requests)); + } + + /** + * @covers Guzzle\Http\Client::send + */ + public function testClientSendsSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(200); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $this->assertEquals($response, $client->send($client->get())); + } + + /** + * @covers Guzzle\Http\Client::send + * @expectedException Guzzle\Http\Exception\BadResponseException + */ + public function testClientThrowsExceptionForSingleRequest() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $response = new Response(404); + $mock->addResponse($response); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send($client->get()); + } + + /** + * @covers Guzzle\Http\Client::send + * @expectedException Guzzle\Common\Exception\ExceptionCollection + */ + public function testClientThrowsExceptionForMultipleRequests() + { + $client = new Client($this->getServer()->getUrl()); + $mock = new MockPlugin(); + $mock->addResponse(new Response(200)); + $mock->addResponse(new Response(404)); + $client->getEventDispatcher()->addSubscriber($mock); + $client->send(array($client->get(), $client->head())); + } + + /** + * @covers Guzzle\Http\Client + */ + public function testQueryStringsAreNotDoubleEncoded() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + 'data' => array( + 'test' => 'a&b' + ) + )); + + $request = $client->get('{/path*}{?query,data*}'); + $this->assertEquals('http://test.com/foo/bar?query=hi%20there&test=a%26b', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + $this->assertEquals('a&b', $request->getQuery()->get('test')); + } + + /** + * @covers Guzzle\Http\Client + */ + public function testQueryStringsAreNotDoubleEncodedUsingAbsolutePaths() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + )); + $request = $client->get('http://test.com{?query}'); + $this->assertEquals('http://test.com/?query=hi%20there', $request->getUrl()); + $this->assertEquals('hi there', $request->getQuery()->get('query')); + } + + /** + * @covers Guzzle\Http\Client::setUriTemplate + * @covers Guzzle\Http\Client::getUriTemplate + */ + public function testAllowsUriTemplateInjection() + { + $client = new Client('http://test.com', array( + 'path' => array('foo', 'bar'), + 'query' => 'hi there', + )); + + $a = $client->getUriTemplate(); + $this->assertSame($a, $client->getUriTemplate()); + $client->setUriTemplate(new UriTemplate()); + $this->assertNotSame($a, $client->getUriTemplate()); + } + + /** + * @covers Guzzle\Http\Client::expandTemplate + */ + public function testAllowsCustomVariablesWhenExpandingTemplates() + { + $client = new Client('http://test.com', array( + 'test' => 'hi', + )); + + $uri = $client->expandTemplate('http://{test}{?query*}', array( + 'query' => array( + 'han' => 'solo' + ) + )); + + $this->assertEquals('http://hi?han=solo', $uri); + } + + /** + * @covers Guzzle\Http\Client::createRequest + * @expectedException InvalidArgumentException + */ + public function testUriArrayMustContainExactlyTwoElements() + { + $client = new Client(); + $client->createRequest('GET', array('haha!')); + } + + /** + * @covers Guzzle\Http\Client::createRequest + * @expectedException InvalidArgumentException + */ + public function testUriArrayMustContainAnArray() + { + $client = new Client(); + $client->createRequest('GET', array('haha!', 'test')); + } + + /** + * @covers Guzzle\Http\Client::createRequest + * @covers Guzzle\Http\Client::get + * @covers Guzzle\Http\Client::put + * @covers Guzzle\Http\Client::post + * @covers Guzzle\Http\Client::head + * @covers Guzzle\Http\Client::options + */ + public function testUriArrayAllowsCustomTemplateVariables() + { + $client = new Client(); + $vars = array( + 'var' => 'hi' + ); + $this->assertEquals('/hi', (string) $client->createRequest('GET', array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->get(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->put(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->post(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->head(array('/{var}', $vars))->getUrl()); + $this->assertEquals('/hi', (string) $client->options(array('/{var}', $vars))->getUrl()); + } + + /** + * @covers Guzzle\Http\Client::setDefaultHeaders + * @covers Guzzle\Http\Client::getDefaultHeaders + * @covers Guzzle\Http\Client::createRequest + */ + public function testAllowsDefaultHeaders() + { + $default = array( + 'X-Test' => 'Hi!' + ); + $other = array( + 'X-Other' => 'Foo' + ); + + $client = new Client(); + $client->setDefaultHeaders($default); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + $client->setDefaultHeaders(new Collection($default)); + $this->assertEquals($default, $client->getDefaultHeaders()->getAll()); + + $request = $client->createRequest('GET', null, $other); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET', null, new Collection($other)); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + $this->assertEquals('Foo', $request->getHeader('X-Other')); + + $request = $client->createRequest('GET'); + $this->assertEquals('Hi!', $request->getHeader('X-Test')); + } + + /** + * @covers Guzzle\Http\Client::setDefaultHeaders + * @expectedException InvalidArgumentException + */ + public function testValidatesDefaultHeaders() + { + $client = new Client(); + $client->setDefaultHeaders('foo'); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieJar/ArrayCookieJarTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieJar/ArrayCookieJarTest.php new file mode 100644 index 0000000..8e99c38 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieJar/ArrayCookieJarTest.php @@ -0,0 +1,314 @@ +jar = new ArrayCookieJar(); + } + + protected function getTestCookies() + { + return array( + new Cookie(array('name' => 'foo', 'value' => 'bar', 'domain' => 'foo.com', 'path' => '/', 'discard' => true)), + new Cookie(array('name' => 'test', 'value' => '123', 'domain' => 'baz.com', 'path' => '/foo', 'expires' => 2)), + new Cookie(array('name' => 'you', 'value' => '123', 'domain' => 'bar.com', 'path' => '/boo', 'expires' => time() + 1000)) + ); + } + + /** + * Provides test data for cookie cookieJar retrieval + */ + public function getCookiesDataProvider() + { + return array( + array(array('foo', 'baz', 'test', 'muppet', 'googoo'), '', '', '', false), + array(array('foo', 'baz', 'muppet', 'googoo'), '', '', '', true), + array(array('googoo'), 'www.example.com', '', '', false), + array(array('muppet', 'googoo'), 'test.y.example.com', '', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/', '', false), + array(array('muppet'), 'x.y.example.com', '/acme/test/', '', false), + array(array('googoo'), 'x.y.example.com', '/test/acme/test/', '', false), + array(array('foo', 'baz'), 'example.com', '', '', false), + array(array('baz'), 'example.com', '', 'baz', false), + ); + } + + public function testStoresAndRetrievesCookies() + { + $cookies = $this->getTestCookies(); + foreach ($cookies as $cookie) { + $this->assertTrue($this->jar->add($cookie)); + } + + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(3, count($this->jar->getIterator())); + $this->assertEquals($cookies, $this->jar->all(null, null, null, false, false)); + } + + public function testRemovesExpiredCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeExpired(); + $this->assertEquals(array($cookies[0], $cookies[2]), $this->jar->all()); + } + + public function testRemovesTemporaryCookies() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + $this->jar->removeTemporary(); + $this->assertEquals(array($cookies[2]), $this->jar->all()); + } + + public function testIsSerializable() + { + $this->assertEquals('[]', $this->jar->serialize()); + $this->jar->unserialize('[]'); + $this->assertEquals(array(), $this->jar->all()); + + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove discard and expired cookies + $serialized = $this->jar->serialize(); + $data = json_decode($serialized, true); + $this->assertEquals(1, count($data)); + + $a = new ArrayCookieJar(); + $a->unserialize($serialized); + $this->assertEquals(1, count($a)); + } + + public function testRemovesSelectively() + { + $cookies = $this->getTestCookies(); + foreach ($this->getTestCookies() as $cookie) { + $this->jar->add($cookie); + } + + // Remove foo.com cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + // Try again, removing no further cookies + $this->jar->remove('foo.com'); + $this->assertEquals(2, count($this->jar)); + + // Remove bar.com cookies with path of /boo + $this->jar->remove('bar.com', '/boo'); + $this->assertEquals(1, count($this->jar)); + + // Remove cookie by name + $this->jar->remove(null, null, 'test'); + $this->assertEquals(0, count($this->jar)); + } + + public function testDoesNotAddIncompleteCookies() + { + $this->assertEquals(false, $this->jar->add(new Cookie())); + $this->assertEquals(false, $this->jar->add(new Cookie(array( + 'name' => 'foo' + )))); + $this->assertEquals(false, $this->jar->add(new Cookie(array( + 'name' => 'foo', + 'domain' => 'foo.com' + )))); + } + + /** + * @covers Guzzle\Http\CookieJar\ArrayCookieJar + */ + public function testOverwritesCookiesThatAreOlderOrDiscardable() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + unset($data['discard']); + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals(false, $c[0]->getDiscard()); + + // Make sure it doesn't duplicate the cookie + $this->jar->add(new Cookie($data)); + $this->assertEquals(1, count($this->jar)); + + // Make sure the more future-ful expiration date supersede the other + $data['expires'] = time() + 2000; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + $c = $this->jar->all(); + $this->assertNotEquals($t, $c[0]->getExpires()); + } + + /** + * @covers Guzzle\Http\CookieJar\ArrayCookieJar + */ + public function testOverwritesCookiesThatHaveChanged() + { + $t = time() + 1000; + $data = array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => '.example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true, + 'discard' => true, + 'expires' => $t + ); + + // Make sure that the discard cookie is overridden with the non-discard + $this->assertTrue($this->jar->add(new Cookie($data))); + + $data['value'] = 'boo'; + $this->assertTrue($this->jar->add(new Cookie($data))); + $this->assertEquals(1, count($this->jar)); + + $c = $this->jar->all(); + $this->assertEquals('boo', $c[0]->getValue()); + } + + public function testAddsCookiesFromResponseWithNoRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => array( + "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "FPCK3=AgBNbvoQAGpGEABZLRAAbFsQAF1tEABkDhAAeO0=; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1", + "CH=deleted; expires=Wed, 03-Mar-2010 02:17:39 GMT; path=/; domain=127.0.0.1", + "CH=AgBNbvoQAAEcEAApuhAAMJcQADQvEAAvGxAALe0QAD6uEAATwhAAC1AQAC8t; expires=Sat, 02-Apr-2019 02:17:40 GMT; path=/; domain=127.0.0.1" + ) + )); + + $this->jar->addCookiesFromResponse($response); + $this->assertEquals(3, count($this->jar)); + $this->assertEquals(1, count($this->jar->all(null, null, 'fpc'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'FPCK3'))); + $this->assertEquals(1, count($this->jar->all(null, null, 'CH'))); + } + + public function testAddsCookiesFromResponseWithRequest() + { + $response = new Response(200, array( + 'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;" + )); + $response->setRequest(new Request('GET', 'http://www.example.com')); + $this->jar->addCookiesFromResponse($response); + $this->assertEquals(1, count($this->jar)); + } + + public function getMatchingCookiesDataProvider() + { + return array( + array('https://example.com', array(0)), + array('http://example.com', array()), + array('https://example.com:8912', array()), + array('https://foo.example.com', array()), + array('http://foo.example.com/test/acme/', array(4)) + ); + } + + /** + * @dataProvider getMatchingCookiesDataProvider + */ + public function testReturnsCookiesMatchingRequests($url, $cookies) + { + $bag = array( + new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(443, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'baz', + 'value' => 'foobar', + 'domain' => 'example.com', + 'path' => '/', + 'max_age' => '86400', + 'port' => array(80, 8080), + 'version' => '1', + 'secure' => true + )), + new Cookie(array( + 'name' => 'test', + 'value' => '123', + 'domain' => 'www.foobar.com', + 'path' => '/path/', + 'discard' => true + )), + new Cookie(array( + 'name' => 'muppet', + 'value' => 'cookie_monster', + 'domain' => '.y.example.com', + 'path' => '/acme/', + 'comment' => 'Comment goes here...', + 'expires' => time() + 86400 + )), + new Cookie(array( + 'name' => 'googoo', + 'value' => 'gaga', + 'domain' => '.example.com', + 'path' => '/test/acme/', + 'max_age' => 1500, + 'version' => 2 + )) + ); + + foreach ($bag as $cookie) { + $this->jar->add($cookie); + } + + $request = new Request('GET', $url); + $results = $this->jar->getMatchingCookies($request); + $this->assertEquals(count($results), count($cookies)); + foreach ($cookies as $i) { + $this->assertContains($bag[$i], $results); + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieJar/FileCookieJarTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieJar/FileCookieJarTest.php new file mode 100644 index 0000000..24f9dd8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieJar/FileCookieJarTest.php @@ -0,0 +1,69 @@ +file = tempnam('/tmp', 'file-cookies'); + } + + /** + * @covers Guzzle\Http\CookieJar\FileCookieJar + */ + public function testLoadsFromFileFile() + { + $jar = new FileCookieJar($this->file); + $this->assertEquals(array(), $jar->all()); + unlink($this->file); + } + + /** + * @covers Guzzle\Http\CookieJar\FileCookieJar + */ + public function testPersistsToFileFile() + { + $jar = new FileCookieJar($this->file); + $jar->add(new Cookie(array( + 'name' => 'foo', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'baz', + 'value' => 'bar', + 'domain' => 'foo.com', + 'expires' => time() + 1000 + ))); + $jar->add(new Cookie(array( + 'name' => 'boo', + 'value' => 'bar', + 'domain' => 'foo.com', + ))); + + $this->assertEquals(3, count($jar)); + unset($jar); + + // Make sure it wrote to the file + $contents = file_get_contents($this->file); + $this->assertNotEmpty($contents); + + // Load the cookieJar from the file + $jar = new FileCookieJar($this->file); + + // Weeds out temporary and session cookies + $this->assertEquals(2, count($jar)); + unset($jar); + unlink($this->file); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieTest.php new file mode 100644 index 0000000..a612761 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/CookieTest.php @@ -0,0 +1,159 @@ +assertEquals('/', $cookie->getPath()); + $this->assertEquals(array(), $cookie->getPorts()); + } + + public function testConvertsDateTimeMaxAgeToUnixTimestamp() + { + $cookie = new Cookie(array( + 'expires' => 'November 20, 1984' + )); + $this->assertTrue(is_numeric($cookie->getExpires())); + } + + public function testAddsExpiresBasedOnMaxAge() + { + $t = time(); + $cookie = new Cookie(array( + 'max_age' => 100 + )); + $this->assertEquals($t + 100, $cookie->getExpires()); + } + + public function testHoldsValues() + { + $t = time(); + $data = array( + 'name' => 'foo', + 'value' => 'baz', + 'path' => '/bar', + 'domain' => 'baz.com', + 'expires' => $t, + 'max_age' => 100, + 'comment' => 'Hi', + 'comment_url' => 'foo.com', + 'port' => array(1, 2), + 'version' => 2, + 'secure' => true, + 'discard' => true, + 'http_only' => true, + 'data' => array( + 'foo' => 'baz', + 'bar' => 'bam' + ) + ); + + $cookie = new Cookie($data); + $this->assertEquals($data, $cookie->toArray()); + + $this->assertEquals('foo', $cookie->getName()); + $this->assertEquals('baz', $cookie->getValue()); + $this->assertEquals('baz.com', $cookie->getDomain()); + $this->assertEquals('/bar', $cookie->getPath()); + $this->assertEquals($t, $cookie->getExpires()); + $this->assertEquals(100, $cookie->getMaxAge()); + $this->assertEquals('Hi', $cookie->getComment()); + $this->assertEquals('foo.com', $cookie->getCommentUrl()); + $this->assertEquals(array(1, 2), $cookie->getPorts()); + $this->assertEquals(2, $cookie->getVersion()); + $this->assertTrue($cookie->getSecure()); + $this->assertTrue($cookie->getDiscard()); + $this->assertTrue($cookie->getHttpOnly()); + $this->assertEquals('baz', $cookie->getAttribute('foo')); + $this->assertEquals('bam', $cookie->getAttribute('bar')); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'bam' + ), $cookie->getAttributes()); + + $cookie->setName('a') + ->setValue('b') + ->setPath('c') + ->setDomain('bar.com') + ->setExpires(10) + ->setMaxAge(200) + ->setComment('e') + ->setCommentUrl('f') + ->setPorts(array(80)) + ->setVersion(3) + ->setSecure(false) + ->setHttpOnly(false) + ->setDiscard(false) + ->setAttribute('snoop', 'dog'); + + $this->assertEquals('a', $cookie->getName()); + $this->assertEquals('b', $cookie->getValue()); + $this->assertEquals('c', $cookie->getPath()); + $this->assertEquals('bar.com', $cookie->getDomain()); + $this->assertEquals(10, $cookie->getExpires()); + $this->assertEquals(200, $cookie->getMaxAge()); + $this->assertEquals('e', $cookie->getComment()); + $this->assertEquals('f', $cookie->getCommentUrl()); + $this->assertEquals(array(80), $cookie->getPorts()); + $this->assertEquals(3, $cookie->getVersion()); + $this->assertFalse($cookie->getSecure()); + $this->assertFalse($cookie->getDiscard()); + $this->assertFalse($cookie->getHttpOnly()); + $this->assertEquals('dog', $cookie->getAttribute('snoop')); + } + + public function testDeterminesIfExpired() + { + $c = new Cookie(); + $c->setExpires(10); + $this->assertTrue($c->isExpired()); + $c->setExpires(time() + 10000); + $this->assertFalse($c->isExpired()); + } + + public function testMatchesPorts() + { + $cookie = new Cookie(); + // Always matches when nothing is set + $this->assertTrue($cookie->matchesPort(2)); + + $cookie->setPorts(array(1, 2)); + $this->assertTrue($cookie->matchesPort(2)); + $this->assertFalse($cookie->matchesPort(100)); + } + + public function testMatchesDomain() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesDomain('baz.com')); + + $cookie->setDomain('baz.com'); + $this->assertTrue($cookie->matchesDomain('baz.com')); + $this->assertFalse($cookie->matchesDomain('bar.com')); + + $cookie->setDomain('.baz.com'); + $this->assertTrue($cookie->matchesDomain('.baz.com')); + $this->assertTrue($cookie->matchesDomain('foo.baz.com')); + $this->assertFalse($cookie->matchesDomain('baz.bar.com')); + $this->assertFalse($cookie->matchesDomain('baz.com')); + } + + public function testMatchesPath() + { + $cookie = new Cookie(); + $this->assertTrue($cookie->matchesPath('/foo')); + + $cookie->setPath('/foo'); + $this->assertTrue($cookie->matchesPath('/foo')); + $this->assertTrue($cookie->matchesPath('/foo/bar')); + $this->assertFalse($cookie->matchesPath('/bar')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php new file mode 100644 index 0000000..c7db47d --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php @@ -0,0 +1,831 @@ +getServer()->getUrl()); + try { + $ha = new CurlHandle($h, false); + $this->fail('Expected InvalidArgumentException'); + } catch (\InvalidArgumentException $e) { + } + + $ha = new CurlHandle($h, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + + $ha = new CurlHandle($h, new Collection(array( + CURLOPT_URL => $this->getServer()->getUrl() + ))); + $this->assertEquals($this->getServer()->getUrl(), $ha->getOptions()->get(CURLOPT_URL)); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::__construct + * @covers Guzzle\Http\Curl\CurlHandle::getHandle + * @covers Guzzle\Http\Curl\CurlHandle::getUrl + * @covers Guzzle\Http\Curl\CurlHandle::getOptions + */ + public function testConstructorInitializesObject() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_URL => $this->getServer()->getUrl() + )); + $this->assertSame($handle, $h->getHandle()); + $this->assertInstanceOf('Guzzle\\Http\\Url', $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), (string) $h->getUrl()); + $this->assertEquals($this->getServer()->getUrl(), $h->getOptions()->get(CURLOPT_URL)); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::getStderr + */ + public function testStoresStdErr() + { + $request = RequestFactory::getInstance()->create('GET', 'http://test.com'); + $request->getCurlOptions()->set('debug', true); + $h = CurlHandle::factory($request); + $this->assertEquals($h->getStderr(true), $h->getOptions()->get(CURLOPT_STDERR)); + $this->assertInternalType('resource', $h->getStderr(true)); + $this->assertInternalType('string', $h->getStderr(false)); + $r = $h->getStderr(true); + fwrite($r, 'test'); + $this->assertEquals('test', $h->getStderr(false)); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::setErrorNo + * @covers Guzzle\Http\Curl\CurlHandle::getErrorNo + */ + public function testStoresCurlErrorNumber() + { + $h = new CurlHandle(curl_init('http://test.com'), array(CURLOPT_URL => 'http://test.com')); + $this->assertEquals(CURLE_OK, $h->getErrorNo()); + $h->setErrorNo(CURLE_OPERATION_TIMEOUTED); + $this->assertEquals(CURLE_OPERATION_TIMEOUTED, $h->getErrorNo()); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::getStderr + */ + public function testAccountsForMissingStdErr() + { + $handle = curl_init('http://www.test.com/'); + $h = new CurlHandle($handle, array( + CURLOPT_URL => 'http://www.test.com/' + )); + $this->assertNull($h->getStderr(false)); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::isAvailable + */ + public function testDeterminesIfResourceIsAvailable() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array()); + $this->assertTrue($h->isAvailable()); + + // Mess it up by closing the handle + curl_close($handle); + $this->assertFalse($h->isAvailable()); + + // Mess it up by unsetting the handle + $handle = null; + $this->assertFalse($h->isAvailable()); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::getError + * @covers Guzzle\Http\Curl\CurlHandle::getErrorNo + * @covers Guzzle\Http\Curl\CurlHandle::getInfo + */ + public function testWrapsErrorsAndInfo() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + $settings = array( + CURLOPT_PORT => 123, + CURLOPT_CONNECTTIMEOUT_MS => 1, + CURLOPT_TIMEOUT_MS => 1 + ); + + $handle = curl_init($this->getServer()->getUrl()); + curl_setopt_array($handle, $settings); + $h = new CurlHandle($handle, $settings); + @curl_exec($handle); + + $errors = array( + CURLE_COULDNT_CONNECT => "couldn't connect to host", + CURLE_OPERATION_TIMEOUTED => 'timeout was reached' + ); + + $this->assertTrue(in_array(strtolower($h->getError()), $errors)); + $this->assertTrue($h->getErrorNo() > 0); + + $this->assertEquals($this->getServer()->getUrl(), $h->getInfo(CURLINFO_EFFECTIVE_URL)); + $this->assertInternalType('array', $h->getInfo()); + + curl_close($handle); + $this->assertEquals(null, $h->getInfo('url')); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::getOptions + */ + public function testWrapsCurlOptions() + { + $handle = curl_init($this->getServer()->getUrl()); + $h = new CurlHandle($handle, array( + CURLOPT_AUTOREFERER => true, + CURLOPT_BUFFERSIZE => 1024 + )); + + $this->assertEquals(true, $h->getOptions()->get(CURLOPT_AUTOREFERER)); + $this->assertEquals(1024, $h->getOptions()->get(CURLOPT_BUFFERSIZE)); + } + + /** + * Data provider for factory tests + * + * @return array + */ + public function dataProvider() + { + $testFile = __DIR__ . '/../../../../../phpunit.xml.dist'; + $postBody = new QueryString(array( + 'file' => '@' . $testFile + )); + + $qs = new QueryString(array( + 'x' => 'y', + 'z' => 'a' + )); + + $userAgent = Utils::getDefaultUserAgent(); + $auth = base64_encode('michael:123'); + $testFileSize = filesize($testFile); + + return array( + // Send a regular GET + array('GET', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_HTTPHEADER => array('Host: www.google.com', 'User-Agent: ' . $userAgent), + )), + // Test that custom request methods can be used + array('TRACE', 'http://www.google.com/', null, null, array( + CURLOPT_CUSTOMREQUEST => 'TRACE' + )), + // Send a GET using a port + array('GET', 'http://127.0.0.1:8080', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_PORT => 8080, + CURLOPT_HTTPHEADER => array('Host: 127.0.0.1:8080', 'User-Agent: ' . $userAgent), + )), + // Send a HEAD request + array('HEAD', 'http://www.google.com/', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_HTTPHEADER => array('Host: www.google.com', 'User-Agent: ' . $userAgent), + CURLOPT_NOBODY => 1 + )), + // Send a GET using basic auth + array('GET', 'https://michael:123@localhost/index.html?q=2', null, null, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_HTTPHEADER => array( + 'Host: localhost', + 'Authorization: Basic ' . $auth, + 'User-Agent: ' . $userAgent + ), + CURLOPT_PORT => 443 + )), + // Send a GET request with custom headers + array('GET', 'http://localhost:8124/', array( + 'x-test-data' => 'Guzzle' + ), null, array( + CURLOPT_PORT => 8124, + CURLOPT_HTTPHEADER => array( + 'Host: localhost:8124', + 'x-test-data: Guzzle', + 'User-Agent: ' . $userAgent + ) + ), array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'x-test-data' => 'Guzzle' + )), + // Send a POST using a query string + array('POST', 'http://localhost:8124/post.php', null, $qs, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_POSTFIELDS => 'x=y&z=a', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Content-Type: application/x-www-form-urlencoded' + ) + ), array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded', + '!Transfer-Encoding' => null + )), + // Send a PUT using raw data + array('PUT', 'http://localhost:8124/put.php', null, EntityBody::factory(fopen($testFile, 'r+')), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_READFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_INFILESIZE => filesize($testFile), + CURLOPT_HTTPHEADER => array ( + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Expect: 100-Continue' + ) + ), array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Expect' => '100-Continue', + 'Content-Length' => $testFileSize, + '!Transfer-Encoding' => null + )), + // Send a POST request using an array of fields + array('POST', 'http://localhost:8124/post.php', null, array( + 'x' => 'y', + 'a' => 'b' + ), array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => 'x=y&a=b', + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Content-Type: application/x-www-form-urlencoded' + ) + ), array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '7', + '!Expect' => null, + 'Content-Type' => 'application/x-www-form-urlencoded', + '!Transfer-Encoding' => null + )), + // Send a POST request using a POST file + array('POST', 'http://localhost:8124/post.php', null, $postBody, array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => array( + 'file' => '@' . $testFile . ';type=application/xml' + ), + CURLOPT_HTTPHEADER => array ( + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Expect: 100-Continue', + 'Content-Type: multipart/form-data' + ) + ), array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '*', + 'Expect' => '100-Continue', + 'Content-Type' => 'multipart/form-data; boundary=*', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data and a custom content-type + array('POST', 'http://localhost:8124/post.php', array( + 'Content-Type' => 'application/json' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_POST => 1, + CURLOPT_HTTPHEADER => array ( + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Expect: 100-Continue', + 'Content-Type: application/json', + 'Content-Length: 14' + ), + ), array( + '_Accept-Encoding' => '*', + '_Accept' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + 'Expect' => '100-Continue', + 'Content-Length' => '14', + '!Transfer-Encoding' => null + )), + // Send a POST request with raw POST data, a custom content-type, and use chunked encoding + array('POST', 'http://localhost:8124/post.php', array( + 'Content-Type' => 'application/json', + 'Transfer-Encoding' => 'chunked' + ), '{"hi":"there"}', array( + CURLOPT_RETURNTRANSFER => 0, + CURLOPT_HEADER => 0, + CURLOPT_FOLLOWLOCATION => 1, + CURLOPT_MAXREDIRS => 5, + CURLOPT_CONNECTTIMEOUT => 10, + CURLOPT_USERAGENT => $userAgent, + CURLOPT_WRITEFUNCTION => 'callback', + CURLOPT_HEADERFUNCTION => 'callback', + CURLOPT_ENCODING => '', + CURLOPT_POST => 1, + CURLOPT_HTTPHEADER => array ( + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Expect: 100-Continue', + 'Content-Type: application/json', + 'Transfer-Encoding: chunked' + ), + ), array( + '_Accept-Encoding' => '*', + '_Accept' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Type' => 'application/json', + 'Expect' => '100-Continue', + 'Transfer-Encoding' => 'chunked', + '!Content-Length' => '' + )), + // Send a POST that does not have a body defined + array('POST', 'http://localhost:8124/foo.php', null, null, array( + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Content-Length: 0' + ) + ), array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Expect' => null, + '!Content-Type' => '*', + '!Transfer-Encoding' => null + )), + // Send a PUT that does not have a body defined + array('PUT', 'http://localhost:8124/empty-put.php', null, null, array( + CURLOPT_HTTPHEADER => array ( + 'Expect:', + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Content-Length: 0' + ) + ), array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '0', + '!Expect' => null, + '!Content-Type' => null, + '!Transfer-Encoding' => null + )), + // Send a PATCH request + array('PATCH', 'http://localhost:8124/patch.php', null, 'body', array( + CURLOPT_INFILESIZE => 4, + CURLOPT_HTTPHEADER => array ( + 'Host: localhost:8124', + 'User-Agent: ' . $userAgent, + 'Expect: 100-Continue' + ) + ) + /*, array( + '_Accept' => '*', + '_Accept-Encoding' => '*', + 'Host' => '*', + 'User-Agent' => '*', + 'Content-Length' => '4', + 'Expect' => '100-Continue', + '!Content-Type' => null, + '!Transfer-Encoding' => null + )*/ + ), + ); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::factory + * @covers Guzzle\Http\Curl\CurlHandle::updateRequestFromTransfer + * @covers Guzzle\Http\Curl\RequestMediator + * @dataProvider dataProvider + */ + public function testFactoryCreatesCurlBasedOnRequest($method, $url, $headers, $body, $options, $expectedHeaders = null) + { + $request = RequestFactory::getInstance()->create($method, $url, $headers, $body); + $request->getCurlOptions()->set('debug', true); + + $originalRequest = clone $request; + $curlTest = clone $request; + $handle = CurlHandle::factory($curlTest); + + $this->assertInstanceOf('Guzzle\\Http\\Curl\\CurlHandle', $handle); + $o = $curlTest->getParams()->get('curl.last_options'); + + // Headers are case-insensitive + if (isset($o[CURLOPT_HTTPHEADER])) { + $o[CURLOPT_HTTPHEADER] = array_map('strtolower', $o[CURLOPT_HTTPHEADER]); + } + if (isset($options[CURLOPT_HTTPHEADER])) { + $options[CURLOPT_HTTPHEADER] = array_map('strtolower', $options[CURLOPT_HTTPHEADER]); + } + + $check = 0; + foreach ($options as $key => $value) { + $check++; + $this->assertArrayHasKey($key, $o, '-> Check number ' . $check); + if ($key != CURLOPT_HTTPHEADER && $key != CURLOPT_POSTFIELDS && (is_array($o[$key])) || $o[$key] instanceof \Closure) { + $this->assertEquals('callback', $value, '-> Check number ' . $check); + } else { + $this->assertTrue($value == $o[$key], '-> Check number ' . $check . ' - ' . var_export($value, true) . ' != ' . var_export($o[$key], true)); + } + } + + // If we are testing the actual sent headers + if ($expectedHeaders) { + + // Send the request to the test server + $client = new Client($this->getServer()->getUrl()); + $request->setClient($client); + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + // Get the request that was sent and create a request that we expected + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals($method, $requests[0]->getMethod()); + + $test = $this->compareHeaders($expectedHeaders, $requests[0]->getHeaders()); + $this->assertFalse($test, $test . "\nSent: \n" . $request . "\n\n" . $requests[0]); + + // Ensure only one Content-Length header is sent + if ($request->getHeader('Content-Length')) { + $this->assertEquals((string) $request->getHeader('Content-Length'), (string) $requests[0]->getHeader('Content-Length')); + } + } + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle + */ + public function testFactoryUsesSpecifiedProtocol() + { + $request = RequestFactory::getInstance()->create('GET', 'http://localhost:8124/'); + $request->setProtocolVersion('1.1'); + $handle = CurlHandle::factory($request); + $options = $request->getParams()->get('curl.last_options'); + $this->assertEquals(CURL_HTTP_VERSION_1_1, $options[CURLOPT_HTTP_VERSION]); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle + */ + public function testUploadsPutData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->getCurlOptions()->set('debug', true); + $request->setBody(EntityBody::factory('test'), 'text/plain', false); + $request->getCurlOptions()->set('progress', true); + + $o = $this->getWildcardObserver($request); + $request->send(); + + // Make sure that the events were dispatched + $this->assertTrue($o->has('curl.callback.progress')); + + // Ensure that the request was received exactly as intended + $r = $this->getServer()->getReceivedRequests(true); + + $this->assertEquals(strtolower($request), strtolower($r[0])); + $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); + $this->assertEquals(4, (string) $r[0]->getHeader('Content-Length')); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle + */ + public function testUploadsPutDataUsingChunkedEncodingWhenLengthCannotBeDetermined() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi" + )); + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->setBody(EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')), 'text/plain'); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[1]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[1]->hasHeader('Content-Length')); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle + */ + public function testUploadsPutDataUsingChunkedEncodingWhenForced() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $client = new Client($this->getServer()->getUrl()); + $request = $client->put('/'); + $request->setBody(EntityBody::factory('hi!'), 'text/plain', true); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertEquals('chunked', $r[0]->getHeader('Transfer-Encoding')); + $this->assertFalse($r[0]->hasHeader('Content-Length')); + $this->assertEquals('hi!', $r[0]->getBody(true)); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle + */ + public function testSendsPostRequestsWithFields() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFields(array( + 'a' => 'b', + 'c' => 'ay! ~This is a test, isn\'t it?' + )); + $request->send(); + + // Ensure the CURLOPT_POSTFIELDS option was set properly + $options = $request->getParams()->get('curl.last_options'); + $this->assertEquals('a=b&c=ay%21%20~This%20is%20a%20test%2C%20isn%27t%20it%3F', $options[CURLOPT_POSTFIELDS]); + + // Make sure that the request was sent correctly + $r = $this->getServer()->getReceivedRequests(true); + + $this->assertEquals(strtolower($request), strtolower($r[0])); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle + */ + public function testSendsPostRequestsWithFiles() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $request->setClient(new Client()); + $request->addPostFiles(array( + 'foo' => __FILE__, + )); + $request->addPostFields(array( + 'bar' => 'baz', + 'arr' => array('a' => 1, 'b' => 2), + )); + $request->send(); + + // Ensure the CURLOPT_POSTFIELDS option was set properly + $options = $request->getParams()->get('curl.last_options'); + $this->assertEquals(array( + 'foo' => '@' . __FILE__ . ';type=text/x-php', + 'bar' => 'baz', + 'arr[a]' => '1', + 'arr[b]' => '2', + ), $options[CURLOPT_POSTFIELDS]); + + // Ensure that a Content-Length header was sent by cURL + $this->assertTrue($request->hasHeader('Content-Length')); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::factory + */ + public function testHeadersCanBeBlacklisted() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->setClient(new Client('http://www.example.com', array( + 'curl.blacklist' => array('header.Accept', 'header.Foo', CURLOPT_ENCODING) + ))); + $request->setHeader('Foo', 'Bar'); + $handle = CurlHandle::factory($request); + $headers = $handle->getOptions()->get(CURLOPT_HTTPHEADER); + $this->assertTrue(in_array('Accept:', $headers)); + $this->assertTrue(in_array('Foo:', $headers)); + $this->assertFalse($handle->getOptions()->hasKey(CURLOPT_ENCODING)); + + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); + $request->send(); + + $r = $this->getServer()->getReceivedRequests(true); + $this->assertFalse($r[0]->hasHeader('Accept')); + $this->assertFalse($r[0]->hasHeader('Foo')); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::factory + */ + public function testAllowsHeadersSetToNull() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->setClient(new Client()); + $request->setBody('test'); + $request->setHeader('Expect', null); + $request->setHeader('Accept', null); + $handle = CurlHandle::factory($request); + $headers = $handle->getOptions()->get(CURLOPT_HTTPHEADER); + $this->assertTrue(in_array('Expect:', $headers)); + $this->assertTrue(in_array('Accept:', $headers)); + } + + /** + * @covers Guzzle\Http\Curl\CurlHandle::updateRequestFromTransfer + */ + public function testEnsuresRequestsHaveResponsesWhenUpdatingFromTransfer() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $handle = CurlHandle::factory($request); + $handle->updateRequestFromTransfer($request); + } + + public function testCurlFollowsRedirectsUsingPost() + { + $this->markTestIncomplete('This is a bug with PHP: https://bugs.php.net/bug.php?id=47204'); + + $url = $this->getServer()->getUrl(); + $this->getServer()->enqueue(array( + "HTTP/1.1 303 See Other\r\nServer: Apache-Coyote/1.1\r\nLocation: {$url}\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($url); + $request = $client->post('/', null, '{}'); + $request->removeHeader('Expect'); + $request->send(); + + $received = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($received)); + } + + public function testAllowsWireTransferInfoToBeEnabled() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set('debug', true); + $handle = CurlHandle::factory($request); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_STDERR)); + $this->assertNotNull($handle->getOptions()->get(CURLOPT_VERBOSE)); + } + + public function testAddsCustomCurlOptions() + { + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, 200); + $handle = CurlHandle::factory($request); + $this->assertEquals(200, $handle->getOptions()->get(CURLOPT_TIMEOUT)); + } + + public function testSendsPostUploadsWithContentDispositionHeaders() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); + + $fileToUpload = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json'; + + $client = new Client($this->getServer()->getUrl()); + $request = $client->post(); + $request->addPostFile('foo', $fileToUpload, 'application/json'); + $request->addPostFile('foo', __FILE__); + + $request->send(); + $requests = $this->getServer()->getReceivedRequests(true); + $body = (string) $requests[0]->getBody(); + + $this->assertContains('Content-Disposition: form-data; name="foo[0]"; filename="', $body); + $this->assertContains('Content-Type: application/json', $body); + $this->assertContains('Content-Type: text/x-php', $body); + $this->assertContains('Content-Disposition: form-data; name="foo[1]"; filename="', $body); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php new file mode 100644 index 0000000..06614f7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php @@ -0,0 +1,600 @@ +updates = new Collection(); + $this->multi = new MockMulti(); + $this->mock = $this->getWildcardObserver($this->multi); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::getInstance + */ + public function testReturnsCachedInstance() + { + $c = CurlMulti::getInstance(); + $this->assertInstanceOf('Guzzle\\Http\\Curl\\CurlMultiInterface', $c); + $this->assertSame($c, CurlMulti::getInstance()); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::__construct + * @covers Guzzle\Http\Curl\CurlMulti::__destruct + */ + public function testConstructorCreateMultiHandle() + { + $this->assertInternalType('resource', $this->multi->getHandle()); + $this->assertEquals('curl_multi', get_resource_type($this->multi->getHandle())); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::__destruct + */ + public function testDestructorClosesMultiHandle() + { + $handle = $this->multi->getHandle(); + $this->multi->__destruct(); + $this->assertFalse(is_resource($handle)); + } + + /** + * @covers Guzzle\Http\Curl\curlMulti::add + * @covers Guzzle\Http\Curl\curlMulti::all + * @covers Guzzle\Http\Curl\curlMulti::count + */ + public function testRequestsCanBeAddedAndCounted() + { + $multi = new CurlMulti(); + $mock = $this->getWildcardObserver($multi); + $request1 = new Request('GET', 'http://www.google.com/'); + $multi->add($request1); + $this->assertEquals(array($request1), $multi->all()); + + $request2 = new Request('POST', 'http://www.google.com/'); + $multi->add($request2); + $this->assertEquals(array($request1, $request2), $multi->all()); + $this->assertEquals(2, count($multi)); + + $this->assertTrue($mock->has(CurlMulti::ADD_REQUEST)); + $this->assertFalse($mock->has(CurlMulti::REMOVE_REQUEST)); + $this->assertFalse($mock->has(CurlMulti::POLLING)); + $this->assertFalse($mock->has(CurlMulti::COMPLETE)); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::remove + * @covers Guzzle\Http\Curl\CurlMulti::all + */ + public function testRequestsCanBeRemoved() + { + $request1 = new Request('GET', 'http://www.google.com/'); + $this->multi->add($request1); + $request2 = new Request('PUT', 'http://www.google.com/'); + $this->multi->add($request2); + $this->assertEquals(array($request1, $request2), $this->multi->all()); + $this->assertSame($this->multi, $this->multi->remove($request1)); + $this->assertEquals(array($request2), $this->multi->all()); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::reset + */ + public function testsResetRemovesRequestsAndResetsState() + { + $request1 = new Request('GET', 'http://www.google.com/'); + $this->multi->add($request1); + $this->multi->reset(); + $this->assertEquals(array(), $this->multi->all()); + $this->assertEquals('idle', $this->multi->getState()); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + * @covers Guzzle\Http\Curl\CurlMulti::getState + */ + public function testSendsRequestsInParallel() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nBody"); + $this->assertEquals('idle', $this->multi->getState()); + $request = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request); + $this->multi->send(); + + $this->assertEquals('idle', $this->multi->getState()); + + $this->assertTrue($this->mock->has(CurlMulti::ADD_REQUEST)); + $this->assertTrue($this->mock->has(CurlMulti::COMPLETE)); + + $this->assertEquals('Body', $request->getResponse()->getBody()->__toString()); + + // Sending it again will not do anything because there are no requests + $this->multi->send(); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + */ + public function testSendsRequestsThroughCurl() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n\r\n" . + "data" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $mock1 = $this->getWildcardObserver($request1); + $request2 = new Request('GET', $this->getServer()->getUrl()); + $mock2 = $this->getWildcardObserver($request2); + + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->send(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + + $this->assertTrue($response1->getBody(true) == 'data' || $response2->getBody(true) == 'data'); + $this->assertTrue($response1->getBody(true) == '' || $response2->getBody(true) == ''); + $this->assertTrue($response1->getStatusCode() == '204' || $response2->getStatusCode() == '204'); + $this->assertNotEquals((string) $response1, (string) $response2); + + $this->assertTrue($mock1->has('request.before_send')); + $this->assertTrue($mock2->has('request.before_send')); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + */ + public function testSendsThroughCurlAndAggregatesRequestExceptions() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n" . + "Content-Type: text/html; charset=utf-8\r\n" . + "Content-Length: 4\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n" . + "data", + + "HTTP/1.1 204 No content\r\n" . + "Content-Length: 0\r\n" . + "Server: Jetty(6.1.3)\r\n" . + "\r\n", + + "HTTP/1.1 404 Not Found\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $request1 = new Request('GET', $this->getServer()->getUrl()); + $request2 = new Request('HEAD', $this->getServer()->getUrl()); + $request3 = new Request('GET', $this->getServer()->getUrl()); + $this->multi->add($request1); + $this->multi->add($request2); + $this->multi->add($request3); + + try { + $this->multi->send(); + $this->fail('ExceptionCollection not thrown when aggregating request exceptions'); + } catch (ExceptionCollection $e) { + + $this->assertInstanceOf('ArrayIterator', $e->getIterator()); + $this->assertEquals(1, count($e)); + $exceptions = $e->getIterator(); + + $response1 = $request1->getResponse(); + $response2 = $request2->getResponse(); + $response3 = $request3->getResponse(); + + $this->assertNotEquals((string) $response1, (string) $response2); + $this->assertNotEquals((string) $response3, (string) $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response1); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response2); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response3); + + $failed = $exceptions[0]->getResponse(); + $this->assertEquals(404, $failed->getStatusCode()); + $this->assertEquals(1, count($e)); + + // Test the IteratorAggregate functionality + foreach ($e as $except) { + $this->assertEquals($failed, $except->getResponse()); + } + } + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + * @covers Guzzle\Http\Curl\CurlMulti::processResponse + */ + public function testCurlErrorsAreCaught() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + try { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $request->setClient(new Client()); + $request->getCurlOptions()->set(CURLOPT_FRESH_CONNECT, true); + $request->getCurlOptions()->set(CURLOPT_FORBID_REUSE, true); + $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT_MS, 5); + $request->send(); + $this->fail('CurlException not thrown'); + } catch (CurlException $e) { + $m = $e->getMessage(); + $this->assertContains('[curl] ', $m); + $this->assertContains('[url] http://127.0.0.1:9876/', $m); + $this->assertContains('[debug] ', $m); + $this->assertContains('[info] array (', $m); + } + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti + */ + public function testRemovesQueuedRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); + $request->setClient(new Client()); + $request->setResponse(new Response(200), true); + $this->multi->add($request); + $this->multi->send(); + $this->assertTrue($this->mock->has(CurlMulti::ADD_REQUEST)); + $this->assertTrue($this->mock->has(CurlMulti::POLLING) === false); + $this->assertTrue($this->mock->has(CurlMulti::COMPLETE) !== false); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti + */ + public function testRemovesQueuedRequestsAddedInTransit() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.receive.status_line', function(Event $event) use ($client) { + // Create a request using a queued response + $request = $client->get()->setResponse(new Response(200), true); + $request->send(); + }); + + $r->send(); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti + */ + public function testProperlyBlocksBasedOnRequestsInScope() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\ntest1", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\ntest2", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\ntest3", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\ntest4", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\ntest5", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\ntest6", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + + $requests = array( + $client->get(), + $client->get() + ); + + $sendHeadFunction = function($event) use ($client) { + $client->head()->send(); + }; + + // Sends 2 new requests in the middle of a CurlMulti loop while other requests + // are completing. This causes the scope of the multi handle to go up. + $callback = function(Event $event) use ($client, $sendHeadFunction) { + $client->getConfig()->set('called', $client->getConfig('called') + 1); + if ($client->getConfig('called') <= 2) { + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.complete', $sendHeadFunction); + $request->send(); + } + }; + + $requests[0]->getEventDispatcher()->addListener('request.complete', $callback); + + $client->send($requests); + + $this->assertEquals(4, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti + * @expectedException RuntimeException + * @expectedExceptionMessage Testing! + */ + public function testCatchesExceptionsBeforeSendingCurlMulti() + { + $client = new Client($this->getServer()->getUrl()); + $multi = new CurlMulti(); + $client->setCurlMulti($multi); + $multi->getEventDispatcher()->addListener(CurlMulti::BEFORE_SEND, function() { + throw new \RuntimeException('Testing!'); + }); + $client->get()->send(); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti + * @covers Guzzle\Http\Curl\CurlMulti::removeErroredRequest + * @expectedException Guzzle\Common\Exception\ExceptionCollection + * @expectedExceptionMessage Thrown before sending! + */ + public function testCatchesExceptionsBeforeSendingRequests() + { + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function() { + throw new \RuntimeException('Thrown before sending!'); + }); + $client->send(array($request)); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti + * @covers Guzzle\Http\Curl\CurlMulti::removeErroredRequest + * @expectedException Guzzle\Http\Exception\BadResponseException + */ + public function testCatchesExceptionsWhenRemovingQueuedRequests() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.sent', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + $r->send(); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti + * @covers Guzzle\Http\Curl\CurlMulti::removeErroredRequest + * @expectedException Guzzle\Http\Exception\BadResponseException + */ + public function testCatchesExceptionsWhenRemovingQueuedRequestsBeforeSending() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $client = new Client($this->getServer()->getUrl()); + $r = $client->get(); + $r->getEventDispatcher()->addListener('request.before_send', function() use ($client) { + // Create a request using a queued response + $client->get()->setResponse(new Response(404), true)->send(); + }); + $r->send(); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + * @covers Guzzle\Http\Curl\CurlMulti::removeErroredRequest + * @expectedException Guzzle\Common\Exception\ExceptionCollection + * @expectedExceptionMessage test + */ + public function testCatchesRandomExceptionsThrownDuringPerform() + { + $client = new Client($this->getServer()->getUrl()); + $multi = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('perform')); + $multi->expects($this->once()) + ->method('perform') + ->will($this->throwException(new \Exception('test'))); + $multi->add($client->get()); + $multi->send(); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + */ + public function testDoesNotSendRequestsDecliningToBeSent() + { + if (!defined('CURLOPT_TIMEOUT_MS')) { + $this->markTestSkipped('Update curl'); + } + + // Create a client that is bound to fail connecting + $client = new Client('http://localhost:123', array( + 'curl.CURLOPT_PORT' => 123, + 'curl.CURLOPT_CONNECTTIMEOUT_MS' => 1, + )); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + + // Listen for request exceptions, and when they occur, first change the + // state of the request back to transferring, and then just allow it to + // exception out + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use ($multi) { + $retries = $event['request']->getParams()->get('retries'); + // Allow the first failure to retry + if ($retries == 0) { + $event['request']->setState('transfer'); + $event['request']->getParams()->set('retries', 1); + // Remove the request to try again + $multi->remove($event['request']); + $multi->add($event['request'], true); + } + }); + + try { + $multi->send(); + $this->fail('Did not throw an exception at all!?!'); + } catch (\Exception $e) { + $this->assertEquals(1, $request->getParams()->get('retries')); + } + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + */ + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithRetry() + { + $this->getServer()->flush(); + $client = new Client($this->getServer()->getUrl()); + $request = $client->get(); + $request->getEventDispatcher()->addListener('request.before_send', function(Event $event) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::send + */ + public function testDoesNotThrowExceptionsWhenRequestsRecoverWithSuccess() + { + // Attempt a port that 99.9% is not listening + $client = new Client('http://localhost:123'); + $request = $client->get(); + // Ensure it times out quickly if needed + $request->getCurlOptions()->set(CURLOPT_TIMEOUT_MS, 1)->set(CURLOPT_CONNECTTIMEOUT_MS, 1); + + $request->getEventDispatcher()->addListener('request.exception', function(Event $event) use (&$count) { + $event['request']->setResponse(new Response(200)); + }); + + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + + // Ensure that the exception was caught, and the response was set manually + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::reset + */ + public function testHardResetReopensMultiHandle() + { + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $message = ''; + $plugin = new LogPlugin(new ClosureLogAdapter(function($msg) use (&$message) { + $message .= $msg . "\n"; + }), LogPlugin::LOG_VERBOSE); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get(); + $multi = new CurlMulti(); + $multi->add($request); + $multi->send(); + $multi->reset(true); + $multi->add($request); + $multi->send(); + + $this->assertNotContains('Re-using existing connection', $message); + } + + /** + * @covers Guzzle\Http\Curl\CurlMulti::checkCurlResult + */ + public function testThrowsMeaningfulExceptionsForCurlMultiErrors() + { + $multi = new CurlMulti(); + + // Set the state of the multi object to sending to trigger the exception + $reflector = new \ReflectionMethod('Guzzle\Http\Curl\CurlMulti', 'checkCurlResult'); + $reflector->setAccessible(true); + + // Successful + $reflector->invoke($multi, 0); + + // Known error + try { + $reflector->invoke($multi, CURLM_BAD_HANDLE); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertContains('The passed-in handle is not a valid CURLM handle.', $e->getMessage()); + $this->assertContains('CURLM_BAD_HANDLE', $e->getMessage()); + $this->assertContains(strval(CURLM_BAD_HANDLE), $e->getMessage()); + } + + // Unknown error + try { + $reflector->invoke($multi, 255); + $this->fail('Expected an exception here'); + } catch (CurlException $e) { + $this->assertEquals('Unexpected cURL error: 255', $e->getMessage()); + } + } + + /** + * @covers Guzzle\Http\Curl\curlMulti::add + */ + public function testAddsAsyncRequestsNormallyWhenNotSending() + { + $multi = new CurlMulti(); + $request = new Request('GET', 'http://www.google.com/'); + $multi->add($request, true); + + // Ensure that the request was added at the correct next scope + $requests = $this->readAttribute($multi, 'requests'); + $this->assertEquals(array($request), $requests[0]); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php new file mode 100644 index 0000000..a292a81 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php @@ -0,0 +1,49 @@ +getProperty('version'); + $refProperty->setAccessible(true); + $refProperty->setValue($instance, array()); + + $this->assertEquals($info, $instance->getAll()); + $this->assertEquals($info, $instance->getAll()); + + $this->assertEquals($info['version'], $instance->get('version')); + $this->assertFalse($instance->get('foo')); + } + + public function testDeterminesIfCurlCanFollowLocation() + { + if (!ini_get('open_basedir')) { + $this->assertTrue(CurlVersion::getInstance()->get('follow_location')); + } else { + $this->assertFalse(CurlVersion::getInstance()->get('follow_location')); + } + } + + public function testIsSingleton() + { + $refObject = new \ReflectionClass('Guzzle\Http\Curl\CurlVersion'); + $refProperty = $refObject->getProperty('instance'); + $refProperty->setAccessible(true); + $refProperty->setValue(null, null); + + $this->assertInstanceOf('Guzzle\Http\Curl\CurlVersion', CurlVersion::getInstance()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php new file mode 100644 index 0000000..3913aee --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php @@ -0,0 +1,53 @@ +events[] = $event; + } + + public function testEmitsEvents() + { + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('foo'); + $request->setResponse(new Response(200)); + + // Ensure that IO events are emitted + $request->getParams()->set('curl.emit_io', true); + + // Attach listeners for each event type + $request->getEventDispatcher()->addListener('curl.callback.progress', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.read', array($this, 'event')); + $request->getEventDispatcher()->addListener('curl.callback.write', array($this, 'event')); + + $mediator = new RequestMediator($request, true); + + $mediator->progress('a', 'b', 'c', 'd'); + $this->assertEquals(1, count($this->events)); + $this->assertEquals('curl.callback.progress', $this->events[0]->getName()); + + $this->assertEquals(3, $mediator->writeResponseBody('foo', 'bar')); + $this->assertEquals(2, count($this->events)); + $this->assertEquals('curl.callback.write', $this->events[1]->getName()); + $this->assertEquals('bar', $this->events[1]['write']); + $this->assertSame($request, $this->events[1]['request']); + + $this->assertEquals('foo', $mediator->readRequestBody('a', 'b', 3)); + $this->assertEquals(3, count($this->events)); + $this->assertEquals('curl.callback.read', $this->events[2]->getName()); + $this->assertEquals('foo', $this->events[2]['read']); + $this->assertSame($request, $this->events[2]['request']); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php new file mode 100644 index 0000000..8f64e7b --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php @@ -0,0 +1,179 @@ +assertEquals('data', (string)$body); + $this->assertEquals(4, $body->getContentLength()); + $this->assertEquals('php', $body->getWrapper()); + $this->assertEquals('temp', $body->getStreamType()); + + $handle = fopen(__DIR__ . '/../../../../phpunit.xml.dist', 'r'); + if (!$handle) { + $this->fail('Could not open test file'); + } + $body = EntityBody::factory($handle); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertTrue($body->isLocal()); + $this->assertEquals(__DIR__ . '/../../../../phpunit.xml.dist', $body->getUri()); + $this->assertEquals(filesize(__DIR__ . '/../../../../phpunit.xml.dist'), $body->getContentLength()); + + // make sure that a body will return as the same object + $this->assertTrue($body === EntityBody::factory($body)); + } + + /** + * @covers Guzzle\Http\EntityBody::factory + */ + public function testFactoryCreatesTempStreamByDefault() + { + $body = EntityBody::factory(''); + $this->assertEquals('php', $body->getWrapper()); + $this->assertEquals('temp', $body->getStreamType()); + $body = EntityBody::factory(); + $this->assertEquals('php', $body->getWrapper()); + $this->assertEquals('temp', $body->getStreamType()); + } + + /** + * @covers Guzzle\Http\EntityBody::factory + */ + public function testFactoryCanCreateFromObject() + { + $body = EntityBody::factory(new QueryString(array('foo' => 'bar'))); + $this->assertEquals('?foo=bar', (string) $body); + } + + /** + * @covers Guzzle\Http\EntityBody::factory + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testFactoryEnsuresObjectsHaveToStringMethod() + { + EntityBody::factory(new \stdClass('a')); + } + + /** + * @covers Guzzle\Http\EntityBody::compress + * @covers Guzzle\Http\EntityBody::uncompress + * @covers Guzzle\Http\EntityBody::getContentEncoding + * @covers Guzzle\Http\EntityBody::setStreamFilterContentEncoding + * @covers Guzzle\Http\EntityBody::handleCompression + * @covers Guzzle\Http\EntityBody::getContentLength + */ + public function testHandlesCompression() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must initially return FALSE'); + $size = $body->getContentLength(); + $body->compress(); + $this->assertEquals('gzip', $body->getContentEncoding(), '-> getContentEncoding() must return the correct encoding after compressing'); + $this->assertEquals(gzdeflate('testing 123...testing 123'), (string) $body); + $this->assertTrue($body->getContentLength() < $size); + $this->assertTrue($body->uncompress()); + $this->assertEquals('testing 123...testing 123', (string) $body); + $this->assertFalse($body->getContentEncoding(), '-> getContentEncoding() must reset to FALSE'); + + if (in_array('bzip2.*', stream_get_filters())) { + $this->assertTrue($body->compress('bzip2.compress')); + $this->assertEquals('compress', $body->getContentEncoding(), '-> compress() must set \'compress\' as the Content-Encoding'); + } + + $this->assertFalse($body->compress('non-existent'), '-> compress() must return false when a non-existent stream filter is used'); + + // Release the body + unset($body); + + // Use gzip compression on the initial content. This will include a + // gzip header which will need to be stripped when deflating the stream + $body = EntityBody::factory(gzencode('test')); + $this->assertSame($body, $body->setStreamFilterContentEncoding('zlib.deflate')); + $this->assertTrue($body->uncompress('zlib.inflate')); + $this->assertEquals('test', (string)$body); + unset($body); + + // Test using a very long string + $largeString = ''; + for ($i = 0; $i < 25000; $i++) { + $largeString .= chr(rand(33, 126)); + } + $body = EntityBody::factory($largeString); + $this->assertEquals($largeString, (string)$body); + $this->assertTrue($body->compress()); + $this->assertNotEquals($largeString, (string)$body); + $compressed = (string)$body; + $this->assertTrue($body->uncompress()); + $this->assertEquals($largeString, (string)$body); + $this->assertEquals($compressed, gzdeflate($largeString)); + + $body = EntityBody::factory(fopen(__DIR__ . '/../TestData/compress_test', 'w')); + $this->assertFalse($body->compress()); + unset($body); + + unlink(__DIR__ . '/../TestData/compress_test'); + } + + /** + * @covers Guzzle\Http\EntityBody::getContentType + */ + public function testDeterminesContentType() + { + // Test using a string/temp stream + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertEquals('application/octet-stream', $body->getContentType()); + + // Use a local file + $body = EntityBody::factory(fopen(__FILE__, 'r')); + $this->assertEquals('text/x-php', $body->getContentType()); + } + + /** + * @covers Guzzle\Http\EntityBody::getContentMd5 + */ + public function testCreatesMd5Checksum() + { + $body = EntityBody::factory('testing 123...testing 123'); + $this->assertEquals(md5('testing 123...testing 123'), $body->getContentMd5()); + + $server = $this->getServer()->enqueue( + "HTTP/1.1 200 OK" . "\r\n" . + "Content-Length: 3" . "\r\n\r\n" . + "abc" + ); + + $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); + $this->assertFalse($body->getContentMd5()); + } + + /** + * @covers Guzzle\Http\EntityBody::factory + */ + public function testGetTypeFormBodyFactoring() + { + $body = EntityBody::factory(array('key1' => 'val1', 'key2' => 'val2')); + $this->assertEquals('key1=val1&key2=val2', (string)$body); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php new file mode 100644 index 0000000..df3e4b7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php @@ -0,0 +1,27 @@ +assertNull($e->getError()); + $this->assertNull($e->getErrorNo()); + $this->assertSame($e, $e->setError('test', 12)); + $this->assertEquals('test', $e->getError()); + $this->assertEquals(12, $e->getErrorNo()); + + $handle = new CurlHandle(curl_init(), array()); + $e->setCurlHandle($handle); + $this->assertSame($handle, $e->getCurlHandle()); + $handle->close(); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php new file mode 100644 index 0000000..12cfd36 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php @@ -0,0 +1,66 @@ +setRequest($request); + $this->assertEquals($request, $e->getRequest()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException + */ + public function testBadResponseException() + { + $e = new BadResponseException('Message'); + $response = new Response(200); + $e->setResponse($response); + $this->assertEquals($response, $e->getResponse()); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesGenericErrorExceptionOnError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(307); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\BadResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesClientErrorExceptionOnClientError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(404); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ClientErrorResponseException', $e); + } + + /** + * @covers Guzzle\Http\Exception\BadResponseException::factory + */ + public function testCreatesServerErrorExceptionOnServerError() + { + $request = new Request('GET', 'http://www.example.com'); + $response = new Response(503); + $e = BadResponseException::factory($request, $response); + $this->assertInstanceOf('Guzzle\Http\Exception\ServerErrorResponseException', $e); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php new file mode 100644 index 0000000..d2f6086 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php @@ -0,0 +1,318 @@ +mock = $this->getMockForAbstractClass('Guzzle\Http\Message\AbstractMessage'); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getParams + */ + public function testGetParams() + { + $request = new Request('GET', 'http://example.com'); + $this->assertInstanceOf('Guzzle\\Common\\Collection', $request->getParams()); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::addHeaders + */ + public function testAddHeaders() + { + $this->mock->setHeader('A', 'B'); + + $this->assertEquals($this->mock, $this->mock->addHeaders(array( + 'X-Data' => '123' + ))); + + $this->assertTrue($this->mock->hasHeader('X-Data') !== false); + $this->assertTrue($this->mock->hasHeader('A') !== false); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::setHeader + */ + public function testAllowsHeaderToSetAsHeader() + { + $h = new Header('A', 'B'); + $this->mock->setHeader('A', $h); + $this->assertSame($h, $this->mock->getHeader('A')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getHeaders + */ + public function testGetHeader() + { + $this->mock->setHeader('Test', '123'); + $this->assertEquals('123', $this->mock->getHeader('Test')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getHeaders + * @covers Guzzle\Http\Message\AbstractMessage::getHeader + * @covers Guzzle\Http\Message\AbstractMessage::setHeaders + */ + public function testGetHeaders() + { + $this->assertSame($this->mock, $this->mock->setHeaders(array( + 'a' => 'b', + 'c' => 'd' + ))); + + $this->assertEquals(array( + 'a' => array('b'), + 'c' => array('d') + ), $this->mock->getHeaders()->getAll()); + + foreach ($this->mock->getHeaders(true) as $key => $value) { + $this->assertInternalType('string', $key); + $this->assertInstanceOf('Guzzle\Http\Message\Header', $value); + } + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getHeaderLines + */ + public function testGetHeaderLinesUsesGlue() + { + $this->mock->setHeaders(array('a' => 'b', 'c' => 'd')); + $this->mock->addHeader('a', 'e'); + $this->mock->getHeader('a')->setGlue('!'); + $this->assertEquals(array( + 'a: b!e', + 'c: d' + ), $this->mock->getHeaderLines()); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::hasHeader + */ + public function testHasHeader() + { + $this->assertFalse($this->mock->hasHeader('Foo')); + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->setHeader('foo', 'yoo'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->assertEquals(true, $this->mock->hasHeader('foo')); + $this->assertEquals(false, $this->mock->hasHeader('bar')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::removeHeader + * @covers Guzzle\Http\Message\AbstractMessage::setHeader + */ + public function testRemoveHeader() + { + $this->mock->setHeader('Foo', 'Bar'); + $this->assertEquals(true, $this->mock->hasHeader('Foo')); + $this->mock->removeHeader('Foo'); + $this->assertFalse($this->mock->hasHeader('Foo')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage + */ + public function testHoldsCacheControlDirectives() + { + $mock = $this->mock; + + // Set a directive using a header + $mock->setHeader('Cache-Control', 'max-age=100'); + $this->assertEquals(100, $mock->getCacheControlDirective('max-age')); + + // Set a header using the directive and check that the header was updated + $this->assertSame($mock, $mock->addCacheControlDirective('max-age', 80)); + $this->assertEquals(80, $mock->getCacheControlDirective('max-age')); + $this->assertEquals('max-age=80', $mock->getHeader('Cache-Control')); + + // Remove the directive + $this->assertEquals($mock, $mock->removeCacheControlDirective('max-age')); + $this->assertEquals('', $mock->getHeader('Cache-Control')); + $this->assertEquals(null, $mock->getCacheControlDirective('max-age')); + // Remove a non-existent directive + $this->assertEquals($mock, $mock->removeCacheControlDirective('max-age')); + + // Has directive + $this->assertFalse($mock->hasCacheControlDirective('max-age')); + $mock->addCacheControlDirective('must-revalidate'); + $this->assertTrue($mock->hasCacheControlDirective('must-revalidate')); + + // Make sure that it works with multiple Cache-Control headers + $mock->setHeader('Cache-Control', 'must-revalidate, max-age=100'); + $mock->addHeaders(array( + 'Cache-Control' => 'no-cache' + )); + + $this->assertEquals(true, $mock->getCacheControlDirective('no-cache')); + $this->assertEquals(true, $mock->getCacheControlDirective('must-revalidate')); + $this->assertEquals(100, $mock->getCacheControlDirective('max-age')); + } + + public function tokenizedHeaderProvider() + { + return array( + array('ISO-8859-1,utf-8;q=0.7,*;q=0.7"', ';', array( + 'ISO-8859-1,utf-8', + 'q' => array('0.7,*', '0.7"') + )), + array('gzip,deflate', ',', array('gzip', 'deflate')), + array('en-us,en;q=0.5', ';', array( + 'en-us,en', + 'q' => '0.5' + )) + ); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getTokenizedHeader + * @dataProvider tokenizedHeaderProvider + */ + public function testConvertsTokenizedHeadersToArray($string, $token, $result) + { + $this->mock->setHeader('test', $string); + $this->assertEquals($result, $this->mock->getTokenizedHeader('test', $token)->getAll()); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::setTokenizedHeader + * @dataProvider tokenizedHeaderProvider + */ + public function testConvertsArrayToTokenizedHeader($string, $token, $result) + { + $this->mock->setTokenizedHeader('test', $result, $token); + $this->assertEquals($string, $this->mock->getHeader('test')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::setTokenizedHeader + * @expectedException InvalidArgumentException + */ + public function testTokenizedHeaderMustBeArrayToSet() + { + $this->mock->setTokenizedHeader('test', false); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getTokenizedHeader + */ + public function testReturnsNullWhenTokenizedHeaderNotFound() + { + $this->assertNull($this->mock->getTokenizedHeader('foo')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getTokenizedHeader + */ + public function testMultipleTokenizedHeadersAreCombined() + { + $this->mock->addHeader('test', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7'); + $this->mock->addHeader('test', 'foo;q=123,*;q=456;q=0.7'); + $this->mock->addHeader('Content-Length', 0); + + $this->assertEquals(array( + 0 => 'ISO-8859-1,utf-8', + 'q' => array('0.7,*', '0.7', '123,*', '456'), + 2 => 'foo', + ), $this->mock->getTokenizedHeader('test')->getAll()); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::getHeader + */ + public function testReturnsNullWhenHeaderIsNotFound() + { + $this->assertNull($this->mock->getHeader('foo')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::addHeaders + * @covers Guzzle\Http\Message\AbstractMessage::addHeader + * @covers Guzzle\Http\Message\AbstractMessage::getHeader + */ + public function testAddingHeadersWithMultipleValuesUsesCaseInsensitiveKey() + { + $this->mock->addHeaders(array( + 'test' => '123', + 'Test' => '456' + )); + $this->mock->addHeader('TEST', '789'); + + $headers = array( + 'test' => array('123'), + 'Test' => array('456'), + 'TEST' => array('789'), + ); + $header = $this->mock->getHeader('test'); + $this->assertInstanceOf('Guzzle\Http\Message\Header', $header); + $this->assertSame($header, $this->mock->getHeader('TEST')); + $this->assertSame($header, $this->mock->getHeader('TeSt')); + + $this->assertEquals($headers, $header->raw()); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::setHeader + */ + public function testSettingHeadersUsesCaseInsensitiveKey() + { + $this->mock->setHeader('test', '123'); + $this->mock->setHeader('TEST', '456'); + $this->assertEquals('456', $this->mock->getHeader('test')); + $this->assertEquals('456', $this->mock->getHeader('TEST')); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage::addHeaders + */ + public function testAddingHeadersPreservesOriginalHeaderCase() + { + $this->mock->addHeaders(array( + 'test' => '123', + 'Test' => 'abc' + )); + $this->mock->addHeader('test', '456'); + $this->mock->addHeader('test', '789'); + + $header = $this->mock->getHeader('test'); + $this->assertEquals(array('123', '456', '789', 'abc'), $header->toArray()); + $this->mock->addHeader('Test', 'abc'); + + // Add a header of a different name + $this->assertEquals(array( + 'test' => array('123', '456', '789'), + 'Test' => array('abc', 'abc') + ), $this->mock->getHeader('test')->raw()); + } + + /** + * @covers Guzzle\Http\Message\AbstractMessage + */ + public function testCanStoreEmptyHeaders() + { + $this->mock->setHeader('Content-Length', 0); + $this->assertTrue($this->mock->hasHeader('Content-Length')); + $this->assertEquals(0, $this->mock->getHeader('Content-Length', true)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php new file mode 100644 index 0000000..e5fda5b --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php @@ -0,0 +1,485 @@ + '123' + )); + $request->setBody('Test'); + $this->assertEquals('123', $request->getHeader('X-Test')); + $this->assertEquals('100-Continue', $request->getHeader('Expect')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody + */ + public function testCanSetBodyWithoutOverridingContentType() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com', array( + 'Content-Type' => 'application/json' + )); + $request->setBody('{"a":"b"}'); + $this->assertEquals('application/json', $request->getHeader('Content-Type')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::__toString + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields + */ + public function testRequestIncludesBodyInMessage() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $this->assertEquals("PUT / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" + . "Expect: 100-Continue\r\n" + . "Content-Length: 4\r\n\r\n" + . "data", (string) $request); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::__toString + */ + public function testRequestIncludesPostBodyInMessageOnlyWhenNoPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => 'bar' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" + . "Content-Type: application/x-www-form-urlencoded\r\n\r\n" + . "foo=bar", (string) $request); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'foo' => '@' . __FILE__ + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" + . "Expect: 100-Continue\r\n" + . "Content-Type: multipart/form-data\r\n\r\n", (string) $request); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::__toString + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields + */ + public function testAddsPostFieldsAndSetsContentLength() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/', null, array( + 'data' => '123' + )); + $this->assertEquals("POST / HTTP/1.1\r\n" + . "Host: www.guzzle-project.com\r\n" + . "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" + . "Content-Type: application/x-www-form-urlencoded\r\n\r\n" + . "data=123", (string) $request); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::__toString + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields + */ + public function testAddsPostFilesAndSetsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/') + ->addPostFiles(array( + 'file' => __FILE__ + ))->addPostFields(array( + 'a' => 'b' + )); + $message = (string) $request; + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + $this->assertEquals('100-Continue', $request->getHeader('Expect')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::processPostFields + */ + public function testRequestBodyContainsPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/'); + $request->addPostFields(array( + 'test' => '123' + )); + $this->assertContains("\r\n\r\ntest=123", (string) $request); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::processPostFields + */ + public function testRequestBodyAddsContentLength() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/'); + $request->setBody(EntityBody::factory('test')); + $this->assertEquals(4, (string) $request->getHeader('Content-Length')); + $this->assertFalse($request->hasHeader('Transfer-Encoding')); + } + + /** + * Tests using a Transfer-Encoding chunked entity body already set + * @covers Guzzle\Http\Message\EntityEnclosingRequest::processPostFields + * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody + */ + public function testRequestBodyDoesNotUseContentLengthWhenChunked() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/'); + $request->setBody(EntityBody::factory('test'), null, true); + $this->assertNull($request->getHeader('Content-Length')); + $this->assertTrue($request->hasHeader('Transfer-Encoding')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getBody + * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody + */ + public function testRequestHasMutableBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.guzzle-project.com/', null, 'data'); + $body = $request->getBody(); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $body); + $this->assertSame($body, $request->getBody()); + + $newBody = EntityBody::factory('foobar'); + $request->setBody($newBody); + $this->assertEquals('foobar', (string) $request->getBody()); + $this->assertSame($newBody, $request->getBody()); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFields + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFiles + */ + public function testSetPostFields() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $request->getPostFields()); + + $fields = new QueryString(array( + 'a' => 'b' + )); + $request->addPostFields($fields); + $this->assertEquals($fields->getAll(), $request->getPostFields()->getAll()); + $this->assertEquals(array(), $request->getPostFiles()); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFiles + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles + */ + public function testSetPostFiles() + { + $request = RequestFactory::getInstance()->create('POST', $this->getServer()->getUrl()) + ->setClient(new Client()) + ->addPostFiles(array(__FILE__)) + ->addPostFields(array( + 'test' => 'abc' + )); + + $request->getCurlOptions()->set('debug', true); + + $this->assertEquals(array( + 'test' => 'abc' + ), $request->getPostFields()->getAll()); + + $this->assertEquals(array( + 'file' => array(new PostFile('file', __FILE__, 'text/x-php')) + ), $request->getPostFiles()); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->send(); + + $this->assertNotNull($request->getHeader('Content-Length')); + $this->assertContains('multipart/form-data; boundary=', (string) $request->getHeader('Content-Type'), '-> cURL must add the boundary'); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFile + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testSetPostFilesThrowsExceptionWhenFileIsNotFound() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array( + 'file' => 'filenotfound.ini' + )); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFile + * @expectedException Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenNonStringsAreAddedToPost() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', new \stdClass()); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFile + */ + public function testAllowsContentTypeInPostUploads() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__, 'text/plain'); + + $this->assertEquals(array( + new PostFile('foo', __FILE__, 'text/plain') + ), $request->getPostFile('foo')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFile + */ + public function testGuessesContentTypeOfPostUpload() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFile('foo', __FILE__); + + $this->assertEquals(array( + new PostFile('foo', __FILE__, 'text/x-php') + ), $request->getPostFile('foo')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles + */ + public function testAllowsContentDispositionFieldsInPostUploadsWhenSettingInBulk() + { + $postFile = new PostFile('foo', __FILE__, 'text/x-php'); + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') + ->addPostFiles(array( + 'foo' => $postFile + )); + + $this->assertEquals(array($postFile), $request->getPostFile('foo')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::setPostField + * @covers Guzzle\Http\Message\EntityEnclosingRequest::processPostFields + */ + public function testPostRequestsUseApplicationXwwwForUrlEncodedForArrays() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertContains("\r\n\r\na=b", (string) $request); + $this->assertEquals('application/x-www-form-urlencoded', $request->getHeader('Content-Type')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::processPostFields + */ + public function testProcessMethodAddsContentType() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->setPostField('a', 'b'); + $this->assertEquals('application/x-www-form-urlencoded', $request->getHeader('Content-Type')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::processPostFields + */ + public function testPostRequestsUseMultipartFormDataWithFiles() + { + $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); + $request->addPostFiles(array('file' => __FILE__)); + $this->assertEquals('multipart/form-data', $request->getHeader('Content-Type')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody + * @covers Guzzle\Http\Message\EntityEnclosingRequest::processPostFields + */ + public function testCanSendMultipleRequestsUsingASingleRequestObject() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 201 Created\r\nContent-Length: 0\r\n\r\n", + )); + + // Send the first request + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()) + ->setBody('test') + ->setClient(new Client()); + $request->send(); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + + // Send the second request + $request->setBody('abcdefg', 'application/json', false); + $request->send(); + $this->assertEquals(201, $request->getResponse()->getStatusCode()); + + // Ensure that the same request was sent twice with different bodies + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(2, count($requests)); + $this->assertEquals(4, $requests[0]->getHeader('Content-Length', true)); + $this->assertEquals(7, $requests[1]->getHeader('Content-Length', true)); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostField + * @covers Guzzle\Http\Message\EntityEnclosingRequest::removePostField + */ + public function testRemovingPostFieldRebuildsPostFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com'); + $request->setPostField('test', 'value'); + $request->removePostField('test'); + $this->assertNull($request->getPostField('test')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody + */ + public function testUsesChunkedTransferWhenBodyLengthCannotBeDetermined() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::setBody + * @expectedException Guzzle\Http\Exception\RequestException + */ + public function testThrowsExceptionWhenContentLengthCannotBeDeterminedAndUsingHttp1() + { + $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request->setProtocolVersion('1.0'); + $request->setBody(fopen($this->getServer()->getUrl(), 'r')); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFiles + */ + public function testAllowsNestedPostData() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => array('b', 'c') + )); + $this->assertEquals(array( + 'a' => array('b', 'c') + ), $request->getPostFields()->getAll()); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields + */ + public function testAllowsEmptyFields() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '' + )); + $this->assertEquals(array( + 'a' => '' + ), $request->getPostFields()->getAll()); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles + * @expectedException Guzzle\Http\Exception\RequestException + */ + public function testFailsOnInvalidFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'a' => new \stdClass() + )); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFields + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFields + */ + public function testHandlesEmptyStrings() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFields(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + )); + $this->assertEquals(array( + 'a' => '', + 'b' => null, + 'c' => 'Foo' + ), $request->getPostFields()->getAll()); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFiles + * @covers Guzzle\Http\Message\EntityEnclosingRequest::getPostFile + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFile + * @covers Guzzle\Http\Message\EntityEnclosingRequest::removePostFile + */ + public function testHoldsPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFile('foo', __FILE__); + $request->addPostFile(new PostFile('foo', __FILE__)); + $this->assertEquals(array( + new PostFile('foo', __FILE__, 'text/x-php'), + new PostFile('foo', __FILE__, 'text/x-php') + ), $request->getPostFile('foo')); + + $this->assertEquals(array( + 'foo' => array( + new PostFile('foo', __FILE__, 'text/x-php'), + new PostFile('foo', __FILE__, 'text/x-php') + ) + ), $request->getPostFiles()); + + $request->removePostFile('foo'); + $this->assertEquals(array(), $request->getPostFiles()); + } + + /** + * @covers Guzzle\Http\Message\EntityEnclosingRequest::addPostFiles + */ + public function testAllowsAtPrefixWhenAddingPostFiles() + { + $request = new EntityEnclosingRequest('POST', 'http://test.com/'); + $request->addPostFiles(array( + 'foo' => '@' . __FILE__ + )); + + $this->assertEquals(array( + 'foo' => array( + new PostFile('foo', __FILE__, 'text/x-php'), + ) + ), $request->getPostFiles()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php new file mode 100644 index 0000000..dc11ca4 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php @@ -0,0 +1,118 @@ + 'Foo' + ), array( + 'Content-Length' => 'Foo' + ), false), + + // Missing header + array(array( + 'X-Foo' => 'Bar' + ), array(), array( + '- X-Foo' => 'Bar' + )), + + // Extra headers is present + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Bar', + 'X-Baz' => 'Jar' + ), array( + '+ X-Baz' => 'Jar' + )), + + // Header is present but must be absent + array(array( + '!X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), array( + '++ X-Foo' => 'Bar' + )), + + // Different values + array(array( + 'X-Foo' => 'Bar' + ), array( + 'X-Foo' => 'Baz' + ), array( + 'X-Foo' => 'Baz != Bar' + )), + + // Wildcard search passes + array(array( + 'X-Foo' => '*' + ), array( + 'X-Foo' => 'Bar' + ), false), + + // Wildcard search fails + array(array( + 'X-Foo' => '*' + ), array(), array( + '- X-Foo' => '*' + )), + + // Ignore extra header if present + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz', + 'X-Bar' => 'Jar' + ), false), + + // Ignore extra header if present and is not + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'X-Foo' => 'Baz' + ), false), + + // Case insensitive + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + ), false), + + // Case insensitive with collection + array(array( + 'X-Foo' => '*', + '_X-Bar' => '*', + ), new Collection(array( + 'x-foo' => 'Baz', + 'x-BAR' => 'baz' + )), false), + ); + } + + /** + * @dataProvider filterProvider + */ + public function testComparesHeaders($filters, $headers, $result) + { + $compare = new HeaderComparison(); + $this->assertEquals($result, $compare->compare($filters, $headers)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php new file mode 100644 index 0000000..797e81a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php @@ -0,0 +1,126 @@ + array('foo', 'Foo'), + 'Zoo' => 'bar', + ); + + public function testRawReturnsRawArray() + { + $i = new Header('Zoo', $this->test); + + $this->assertEquals(array( + 'zoo' => array('foo', 'Foo'), + 'Zoo' => array('bar') + ), $i->raw()); + } + + public function testStoresHeaderName() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('Zoo', $i->getName()); + } + + public function testConvertsToString() + { + $i = new Header('Zoo', $this->test); + $this->assertEquals('foo; Foo; bar', (string) $i); + $i->setGlue(', '); + $this->assertEquals('foo, Foo, bar', (string) $i); + } + + public function testNormalizesCases() + { + $h = new Header('Zoo', $this->test); + $h->normalize(); + $this->assertEquals(array( + 'Zoo' => array('foo', 'Foo', 'bar') + ), $h->raw()); + } + + public function testCanSearchForValues() + { + $h = new Header('Zoo', $this->test); + $this->assertTrue($h->hasValue('foo')); + $this->assertTrue($h->hasValue('Foo')); + $this->assertTrue($h->hasValue('bar')); + $this->assertFalse($h->hasValue('moo')); + + $this->assertFalse($h->hasValue('FoO')); + $this->assertTrue($h->hasValue('FoO', true)); + } + + public function testIsCountable() + { + $h = new Header('Zoo', $this->test); + $this->assertEquals(3, count($h)); + } + + public function testCanBeIterated() + { + $h = new Header('Zoo', $this->test); + $results = array(); + foreach ($h as $key => $value) { + $results[$key] = $value; + } + $this->assertEquals(array( + 'foo', 'Foo', 'bar' + ), $results); + } + + public function testAllowsFalseyValues() + { + // Allows 0 + $h = new Header('Foo', 0, ';'); + $this->assertEquals('0', (string) $h); + $this->assertEquals(1, count($h)); + $this->assertEquals(';', $h->getGlue()); + + // Does not add a null header by default + $h = new Header('Foo'); + $this->assertEquals('', (string) $h); + $this->assertEquals(0, count($h)); + + // Allows null array for a single null header + $h = new Header('Foo', array(null)); + $this->assertEquals('', (string) $h); + $this->assertEquals(1, count($h)); + + // Allows empty string + $h = new Header('Foo', ''); + $this->assertEquals('', (string) $h); + $this->assertEquals(1, count($h)); + } + + public function testUsesHeaderNameWhenNoneIsSupplied() + { + $h = new Header('Foo', 'bar', ';'); + $h->add('baz'); + $this->assertEquals(array('Foo'), array_keys($h->raw())); + } + + public function testCanCheckForExactHeaderValues() + { + $h = new Header('Foo', 'bar', ';'); + $this->assertTrue($h->hasExactHeader('Foo')); + $this->assertFalse($h->hasExactHeader('foo')); + } + + public function testCanRemoveValues() + { + $h = new Header('Foo', array('Foo', 'baz', 'bar')); + $h->removeValue('bar'); + $this->assertTrue($h->hasValue('Foo')); + $this->assertFalse($h->hasValue('bar')); + $this->assertTrue($h->hasValue('baz')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php new file mode 100644 index 0000000..a15a8a7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php @@ -0,0 +1,53 @@ +assertEquals('foo', $file->getFieldName()); + $this->assertEquals(__FILE__, $file->getFilename()); + $this->assertEquals('x-foo', $file->getContentType()); + } + + public function testRemovesLeadingAtSymbolFromPath() + { + $file = new PostFile('foo', '@' . __FILE__); + $this->assertEquals(__FILE__, $file->getFilename()); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresFileIsReadable() + { + $file = new PostFile('foo', '/foo/baz/bar'); + } + + public function testCanChangeContentType() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setContentType('Boo'); + $this->assertEquals('Boo', $file->getContentType()); + } + + public function testCanChangeFieldName() + { + $file = new PostFile('foo', '@' . __FILE__); + $file->setFieldName('Boo'); + $this->assertEquals('Boo', $file->getFieldName()); + } + + public function testReturnsCurlValueString() + { + $file = new PostFile('foo', __FILE__); + $this->assertEquals('@' . __FILE__ . ';type=text/x-php', $file->getCurlString()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php new file mode 100644 index 0000000..e4ea085 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php @@ -0,0 +1,335 @@ +assertSame($factory, RequestFactory::getInstance()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + * @covers Guzzle\Http\Message\RequestFactory::getInstance + */ + public function testCreatesNewGetRequests() + { + $request = RequestFactory::getInstance()->create('GET', 'http://www.google.com/'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\MessageInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $request); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request); + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/', $request->getPath()); + $this->assertEquals('/', $request->getResource()); + + // Create a GET request with a custom receiving body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $b = EntityBody::factory(); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl(), null, $b); + $request->setClient(new Client()); + $response = $request->send(); + $this->assertSame($b, $response->getBody()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesPutRequests() + { + // Test using a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + unset($request); + + // Test using an EntityBody + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, EntityBody::factory('Data')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using a resource + $resource = fopen('php://temp', 'w+'); + fwrite($resource, 'Data'); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, $resource); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('Data', (string) $request->getBody()); + + // Test using an object that can be cast as a string + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, Url::factory('http://www.example.com/')); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('http://www.example.com/', (string) $request->getBody()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesHeadAndDeleteRequests() + { + $request = RequestFactory::getInstance()->create('DELETE', 'http://www.test.com/'); + $this->assertEquals('DELETE', $request->getMethod()); + $request = RequestFactory::getInstance()->create('HEAD', 'http://www.test.com/'); + $this->assertEquals('HEAD', $request->getMethod()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesOptionsRequests() + { + $request = RequestFactory::getInstance()->create('OPTIONS', 'http://www.example.com/'); + $this->assertEquals('OPTIONS', $request->getMethod()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $request); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesNewPutRequestWithBody() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', null, 'Data'); + $this->assertEquals('Data', (string) $request->getBody()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesNewPostRequestWithFields() + { + // Use an array + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, array( + 'a' => 'b' + )); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + unset($request); + + // Use a collection + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new Collection(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + // Use a QueryString + $request = RequestFactory::getInstance()->create('POST', 'http://www.google.com/path?q=1&v=2', null, new QueryString(array( + 'a' => 'b' + ))); + $this->assertEquals(array('a' => 'b'), $request->getPostFields()->getAll()); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.test.com/', null, array( + 'a' => 'b', + 'file' => '@' . __FILE__ + )); + + $this->assertEquals(array( + 'a' => 'b' + ), $request->getPostFields()->getAll()); + + $this->assertEquals(array( + 'file' => array(new PostFile('file', __FILE__, 'text/x-php')) + ), $request->getPostFiles()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::fromParts + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesFromParts() + { + $parts = parse_url('http://michael:123@www.google.com:8080/path?q=1&v=2'); + + $request = RequestFactory::getInstance()->fromParts('PUT', $parts, null, 'Data'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string)$request->getBody()); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('123', $request->getPassword()); + $this->assertEquals('8080', $request->getPort()); + $this->assertEquals(array( + 'scheme' => 'http', + 'host' => 'www.google.com', + 'port' => 8080, + 'path' => '/path', + 'query' => 'q=1&v=2', + ), parse_url($request->getUrl())); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::fromMessage + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesFromMessage() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com:8080\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com:8080/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('www.google.com:8080', $request->getHeader('Host')); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string) $request->getBody()); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('123', $request->getPassword()); + $this->assertEquals('8080', $request->getPort()); + + // Test passing a blank message returns false + $this->assertFalse($request = RequestFactory::getInstance()->fromMessage('')); + + // Test passing a url with no port + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\nHost: www.google.com\r\nContent-Length: 4\r\nAuthorization: Basic {$auth}\r\n\r\nData"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); + $this->assertEquals('PUT', $request->getMethod()); + $this->assertEquals('http', $request->getScheme()); + $this->assertEquals('http://www.google.com/path?q=1&v=2', $request->getUrl()); + $this->assertEquals('www.google.com', $request->getHost()); + $this->assertEquals('/path', $request->getPath()); + $this->assertEquals('/path?q=1&v=2', $request->getResource()); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $request->getBody()); + $this->assertEquals('Data', (string)$request->getBody()); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('123', $request->getPassword()); + $this->assertEquals(80, $request->getPort()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesNewTraceRequest() + { + $request = RequestFactory::getInstance()->create('TRACE', 'http://www.google.com/'); + $this->assertFalse($request instanceof \Guzzle\Http\Message\EntityEnclosingRequest); + $this->assertEquals('TRACE', $request->getMethod()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesProperTransferEncodingRequests() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'hello'); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + $this->assertFalse($request->hasHeader('Content-Length')); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::fromMessage + */ + public function testProperlyDealsWithDuplicateHeaders() + { + $parser = new MessageParser(); + + $message = "POST / http/1.1\r\n" + . "DATE:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "host:host.foo.com\r\n" + . "ZOO:abc\r\n" + . "ZOO:123\r\n" + . "ZOO:HI\r\n" + . "zoo:456\r\n\r\n"; + + $parts = $parser->parseRequest($message); + $this->assertEquals(array ( + 'DATE' => 'Mon, 09 Sep 2011 23:36:00 GMT', + 'host' => 'host.foo.com', + 'ZOO' => array('abc', '123', 'HI'), + 'zoo' => '456', + ), $parts['headers']); + + $request = RequestFactory::getInstance()->fromMessage($message); + + $this->assertEquals(array( + 'ZOO' => array('abc', '123', 'HI'), + 'zoo' => array('456') + ), $request->getHeader('zoo')->raw()); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::fromMessage + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testCreatesHttpMessagesWithBodiesAndNormalizesLineEndings() + { + $message = "POST / http/1.1\r\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\r\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\r\n" + . "Host:host.foo.com\r\n\r\n" + . "foo=bar"; + + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('application/x-www-form-urlencoded; charset=utf8', (string) $request->getHeader('Content-Type')); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "POST / http/1.1\n" + . "Content-Type:application/x-www-form-urlencoded; charset=utf8\n" + . "Date:Mon, 09 Sep 2011 23:36:00 GMT\n" + . "Host:host.foo.com\n\n" + . "foo=bar"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertEquals('foo=bar', (string) $request->getBody()); + + $message = "PUT / HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; + $request = RequestFactory::getInstance()->fromMessage($message); + $this->assertTrue($request->hasHeader('Content-Length')); + $this->assertEquals(0, (string) $request->getHeader('Content-Length')); + } + + /** + * @covers Guzzle\Http\Message\RequestFactory::create + */ + public function testHandlesChunkedTransferEncoding() + { + $request = RequestFactory::getInstance()->create('PUT', 'http://www.foo.com/', array( + 'Transfer-Encoding' => 'chunked' + ), 'Test'); + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + + $request = RequestFactory::getInstance()->create('POST', 'http://www.foo.com/', array( + 'transfer-encoding' => 'chunked' + ), array( + 'foo' => 'bar' + )); + + $this->assertFalse($request->hasHeader('Content-Length')); + $this->assertEquals('chunked', $request->getHeader('Transfer-Encoding')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php new file mode 100644 index 0000000..483081b --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php @@ -0,0 +1,788 @@ +client = new Client($this->getServer()->getUrl()); + $this->request = $this->client->get(); + } + + public function tearDown() + { + unset($this->request); + unset($this->client); + } + + /** + * @covers Guzzle\Http\Message\Request::__construct + */ + public function testConstructorBuildsRequestWithArrayHeaders() + { + // Test passing an array of headers + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'foo' => 'bar' + )); + + $this->assertEquals('GET', $request->getMethod()); + $this->assertEquals('http://www.guzzle-project.com/', $request->getUrl()); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + /** + * @covers Guzzle\Http\Message\Request::getAllEvents + */ + public function testDescribesEvents() + { + $this->assertInternalType('array', Request::getAllEvents()); + } + + /** + * @covers Guzzle\Http\Message\Request::__construct + */ + public function testConstructorBuildsRequestWithCollectionHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', new Collection(array( + 'foo' => 'bar' + ))); + $this->assertEquals('bar', $request->getHeader('foo')); + } + + /** + * @covers Guzzle\Http\Message\Request::__construct + */ + public function testConstructorBuildsRequestWithNoHeaders() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', null); + $this->assertFalse($request->hasHeader('foo')); + } + + /** + * @covers Guzzle\Http\Message\Request::__construct + */ + public function testConstructorHandlesBasicAuth() + { + $auth = base64_encode('michael:foo'); + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'Authorization' => 'Basic ' . $auth + )); + $this->assertEquals('michael', $request->getUserName()); + $this->assertEquals('foo', $request->getPassword()); + $this->assertEquals('Basic ' . $auth, (string) $request->getHeader('Authorization')); + } + + /** + * @covers Guzzle\Http\Message\Request::__construct + */ + public function testConstructorHandlesNonBasicAuth() + { + $request = new Request('GET', 'http://www.guzzle-project.com/', array( + 'Authorization' => 'Foo bar' + )); + $this->assertNull($request->getUserName()); + $this->assertNull($request->getPassword()); + $this->assertEquals('Foo bar', (string) $request->getHeader('Authorization')); + } + + /** + * @covers Guzzle\Http\Message\Request::__toString + * @covers Guzzle\Http\Message\Request::getRawHeaders + * @covers Guzzle\Http\Message\AbstractMessage::getHeaderLines + */ + public function testRequestsCanBeConvertedToRawMessageStrings() + { + $auth = base64_encode('michael:123'); + $message = "PUT /path?q=1&v=2 HTTP/1.1\r\n" + . "Host: www.google.com\r\n" + . "Authorization: Basic {$auth}\r\n" + . "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" + . "Expect: 100-Continue\r\n" + . "Content-Length: 4\r\n\r\nData"; + + $request = RequestFactory::getInstance()->create('PUT', 'http://www.google.com/path?q=1&v=2', array( + 'Authorization' => 'Basic ' . $auth + ), 'Data'); + + $this->assertEquals($message, $request->__toString()); + } + + /** + * Add authorization after the fact and see that it was put in the message + * + * @covers Guzzle\Http\Message\Request::__toString + * @covers Guzzle\Http\Message\Request::getRawHeaders + */ + public function testRequestStringsIncludeAuth() + { + $auth = base64_encode('michael:123'); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl(), null, 'Data') + ->setClient($this->client) + ->setAuth('michael', '123', CURLAUTH_BASIC); + $request->send(); + + $this->assertContains('Authorization: Basic ' . $auth, (string) $request); + } + + /** + * @covers Guzzle\Http\Message\Request::getEventDispatcher + */ + public function testGetEventDispatcher() + { + $d = $this->request->getEventDispatcher(); + $this->assertInstanceOf('Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', $d); + $this->assertEquals($d, $this->request->getEventDispatcher()); + } + + /** + * @covers Guzzle\Http\Message\Request::getClient + * @covers Guzzle\Http\Message\Request::setClient + */ + public function testRequestsManageClients() + { + $request = new Request('GET', 'http://test.com'); + $this->assertNull($request->getClient()); + $request->setClient($this->client); + $this->assertSame($this->client, $request->getClient()); + } + + /** + * @covers Guzzle\Http\Message\Request::send + * @expectedException RuntimeException + * @expectedExceptionMessage A client must be set on the request + */ + public function testRequestsRequireClients() + { + $request = new Request('GET', 'http://test.com'); + $request->send(); + } + + /** + * @covers Guzzle\Http\Message\Request::send + * @covers Guzzle\Http\Message\Request::getResponse + * @covers Guzzle\Http\Message\Request::setResponse + * @covers Guzzle\Http\Message\Request::processResponse + * @covers Guzzle\Http\Message\Request::getResponseBody + */ + public function testSend() + { + $response = new Response(200, array( + 'Content-Length' => 3 + ), 'abc'); + $this->request->setResponse($response, true); + $r = $this->request->send(); + + $this->assertSame($response, $r); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $this->request->getResponse()); + $this->assertSame($r, $this->request->getResponse()); + $this->assertEquals('complete', $this->request->getState()); + } + + /** + * @covers Guzzle\Http\Message\Request::getResponse + * @covers Guzzle\Http\Message\Request::setResponse + * @covers Guzzle\Http\Message\Request::processResponse + * @covers Guzzle\Http\Message\Request::getResponseBody + */ + public function testGetResponse() + { + $this->assertNull($this->request->getResponse()); + + $response = new Response(200, array( + 'Content-Length' => 3 + ), 'abc'); + + $this->request->setResponse($response); + $this->assertEquals($response, $this->request->getResponse()); + + $request = new Request('GET', 'http://www.google.com/'); + $request->setResponse($response, true); + $request->setState(RequestInterface::STATE_COMPLETE); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + + // Try again, making sure it's still the same response + $this->assertSame($requestResponse, $request->getResponse()); + + $response = new Response(204); + $request = new Request('GET', 'http://www.google.com/'); + $request->setResponse($response, true); + $request->setState('complete'); + $requestResponse = $request->getResponse(); + $this->assertSame($response, $requestResponse); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + } + + /** + * @covers Guzzle\Http\Message\Request::getResponse + * @covers Guzzle\Http\Message\Request::processResponse + * @covers Guzzle\Http\Message\Request::getResponseBody + */ + public function testRequestThrowsExceptionOnBadResponse() + { + $response = new Response(404, array( + 'Content-Length' => 3 + ), 'abc'); + + $request = new Request('GET', 'http://www.google.com/'); + $request->setClient($this->client); + try { + $request->setResponse($response, true); + $request->send(); + $this->fail('Expected exception not thrown'); + } catch (BadResponseException $e) { + $this->assertInstanceOf('Guzzle\\Http\\Message\\RequestInterface', $e->getRequest()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $e->getResponse()); + $this->assertContains('Client error response', $e->getMessage()); + } + } + + /** + * @covers Guzzle\Http\Message\Request::getQuery + */ + public function testManagesQuery() + { + $this->assertInstanceOf('Guzzle\\Http\\QueryString', $this->request->getQuery()); + $this->request->getQuery()->set('test', '123'); + $this->assertEquals('?test=123', $this->request->getQuery(true)); + } + + /** + * @covers Guzzle\Http\Message\Request::getMethod + */ + public function testRequestHasMethod() + { + $this->assertEquals('GET', $this->request->getMethod()); + } + + /** + * @covers Guzzle\Http\Message\Request::getScheme + * @covers Guzzle\Http\Message\Request::setScheme + */ + public function testRequestHasScheme() + { + $this->assertEquals('http', $this->request->getScheme()); + $this->assertEquals($this->request, $this->request->setScheme('https')); + $this->assertEquals('https', $this->request->getScheme()); + } + + /** + * @covers Guzzle\Http\Message\Request::getHost + * @covers Guzzle\Http\Message\Request::setHost + */ + public function testRequestHasHost() + { + $this->assertEquals('127.0.0.1', $this->request->getHost()); + $this->assertEquals('127.0.0.1:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www2.google.com')); + $this->assertEquals('www2.google.com', $this->request->getHost()); + $this->assertEquals('www2.google.com:8124', (string) $this->request->getHeader('Host')); + + $this->assertSame($this->request, $this->request->setHost('www.test.com:8081')); + $this->assertEquals('www.test.com', $this->request->getHost()); + $this->assertEquals(8081, $this->request->getPort()); + } + + /** + * @covers Guzzle\Http\Message\Request::getProtocolVersion + * @covers Guzzle\Http\Message\Request::setProtocolVersion + */ + public function testRequestHasProtocol() + { + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.1')); + $this->assertEquals('1.1', $this->request->getProtocolVersion()); + $this->assertEquals($this->request, $this->request->setProtocolVersion('1.0')); + $this->assertEquals('1.0', $this->request->getProtocolVersion()); + } + + /** + * @covers Guzzle\Http\Message\Request::getPath + * @covers Guzzle\Http\Message\Request::setPath + */ + public function testRequestHasPath() + { + $this->assertEquals('/', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('/index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + $this->assertEquals($this->request, $this->request->setPath('index.html')); + $this->assertEquals('/index.html', $this->request->getPath()); + } + + /** + * @covers Guzzle\Http\Message\Request::getPort + * @covers Guzzle\Http\Message\Request::setPort + */ + public function testRequestHasPort() + { + $this->assertEquals(8124, $this->request->getPort()); + $this->assertEquals('127.0.0.1:8124', $this->request->getHeader('Host')); + + $this->assertEquals($this->request, $this->request->setPort('8080')); + $this->assertEquals('8080', $this->request->getPort()); + $this->assertEquals('127.0.0.1:8080', $this->request->getHeader('Host')); + + $this->request->setPort(80); + $this->assertEquals('127.0.0.1', $this->request->getHeader('Host')); + } + + /** + * @covers Guzzle\Http\Message\Request::getUsername + * @covers Guzzle\Http\Message\Request::getPassword + * @covers Guzzle\Http\Message\Request::setAuth + */ + public function testRequestHandlesAuthorization() + { + // Uninitialized auth + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Set an auth + $this->assertSame($this->request, $this->request->setAuth('michael', '123')); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('123', $this->request->getPassword()); + + // Remove the auth + $this->request->setAuth(false); + $this->assertEquals(null, $this->request->getUsername()); + $this->assertEquals(null, $this->request->getPassword()); + + // Make sure that the cURL based auth works too + $request = new Request('GET', $this->getServer()->getUrl()); + $request->setAuth('michael', 'password', CURLAUTH_DIGEST); + $this->assertEquals('michael:password', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + } + + /** + * @covers Guzzle\Http\Message\Request::getResource + */ + public function testGetResourceUri() + { + $this->assertEquals('/', $this->request->getResource()); + $this->request->setPath('/index.html'); + $this->assertEquals('/index.html', $this->request->getResource()); + $this->request->getQuery()->add('v', '1'); + $this->assertEquals('/index.html?v=1', $this->request->getResource()); + } + + /** + * @covers Guzzle\Http\Message\Request::getUrl + * @covers Guzzle\Http\Message\Request::setUrl + */ + public function testRequestHasMutableUrl() + { + $url = 'http://www.test.com:8081/path?q=123#fragment'; + $u = Url::factory($url); + $this->assertSame($this->request, $this->request->setUrl($url)); + $this->assertEquals($url, $this->request->getUrl()); + + $this->assertSame($this->request, $this->request->setUrl($u)); + $this->assertEquals($url, $this->request->getUrl()); + } + + /** + * @covers Guzzle\Http\Message\Request::setState + * @covers Guzzle\Http\Message\Request::getState + */ + public function testRequestHasState() + { + $this->assertEquals(RequestInterface::STATE_NEW, $this->request->getState()); + $this->request->setState(RequestInterface::STATE_TRANSFER); + $this->assertEquals(RequestInterface::STATE_TRANSFER, $this->request->getState()); + } + + /** + * @covers Guzzle\Http\Message\Request::setResponse + * @covers Guzzle\Http\Message\Request::getResponse + * @covers Guzzle\Http\Message\Request::getState + */ + public function testSetManualResponse() + { + $response = new Response(200, array( + 'Date' => 'Sat, 16 Oct 2010 17:27:14 GMT', + 'Expires' => '-1', + 'Cache-Control' => 'private, max-age=0', + 'Content-Type' => 'text/html; charset=ISO-8859-1', + ), 'response body'); + + $this->assertSame($this->request, $this->request->setResponse($response), '-> setResponse() must use a fluent interface'); + $this->assertEquals('complete', $this->request->getState(), '-> setResponse() must change the state of the request to complete'); + $this->assertSame($response, $this->request->getResponse(), '-> setResponse() must set the exact same response that was passed in to it'); + } + + /** + * @covers Guzzle\Http\Message\Request::setResponseBody + */ + public function testRequestCanHaveManuallySetResponseBody() + { + $file = __DIR__ . '/../../TestData/temp.out'; + if (file_exists($file)) { + unlink($file); + } + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $request->setResponseBody(EntityBody::factory(fopen($file, 'w+'))); + $request->send(); + + $this->assertTrue(file_exists($file)); + unlink($file); + + $this->assertEquals('data', $request->getResponse()->getBody(true)); + } + + /** + * @covers Guzzle\Http\Message\Request::isResponseBodyRepeatable + */ + public function testDeterminesIfResponseBodyRepeatable() + { + // The default stream created for responses is seekable + $request = RequestFactory::getInstance()->create('GET', 'http://localhost:' . $this->getServer()->getPort()); + $this->assertTrue($request->isResponseBodyRepeatable()); + + // This should return false because an HTTP stream is not seekable + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + $request->setResponseBody(EntityBody::factory(fopen($this->getServer()->getUrl(), true))); + $this->assertFalse($request->isResponseBodyRepeatable()); + } + + /** + * @covers Guzzle\Http\Message\Request::canCache + */ + public function testDeterminesIfCanCacheRequest() + { + $this->assertTrue(RequestFactory::getInstance()->fromMessage( + "GET / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120\r\n\r\n" + )->canCache()); + + $this->assertTrue(RequestFactory::getInstance()->fromMessage( + "HEAD / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120\r\n\r\n" + )->canCache()); + + $this->assertFalse(RequestFactory::getInstance()->fromMessage( + "HEAD / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120, no-store\r\n\r\n" + )->canCache()); + + $this->assertFalse(RequestFactory::getInstance()->fromMessage( + "POST / HTTP/1.1\r\nHost: www.test.com\r\n\r\n" + )->canCache()); + + $this->assertFalse(RequestFactory::getInstance()->fromMessage( + "PUT / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, max-age=120\r\n\r\n" + )->canCache()); + } + + /** + * @covers Guzzle\Http\Message\Request + */ + public function testHoldsCookies() + { + $this->assertNull($this->request->getCookie('test')); + + // Set a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + + // Unset the cookies by setting the Cookie header to null + $this->request->setHeader('Cookie', null); + $this->assertNull($this->request->getCookie('test')); + + // Set and remove a cookie + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->assertEquals('abc', $this->request->getCookie('test')); + $this->assertSame($this->request, $this->request->removeCookie('test')); + $this->assertNull($this->request->getCookie('test')); + + // Remove the cookie header + $this->assertSame($this->request, $this->request->addCookie('test', 'abc')); + $this->request->removeHeader('Cookie'); + $this->assertEquals('', (string) $this->request->getHeader('Cookie')); + + // Remove a cookie value + $this->request->addCookie('foo', 'bar')->addCookie('baz', 'boo'); + $this->request->removeCookie('foo'); + $this->assertEquals(array( + 'baz' => 'boo' + ), $this->request->getCookies()); + } + + /** + * @covers Guzzle\Http\Message\Request::processResponse + * @expectedException Guzzle\Http\Exception\RequestException + * @expectedExceptionMessage Error completing request + */ + public function testRequestThrowsExceptionWhenSetToCompleteWithNoResponse() + { + $this->request->setState(RequestInterface::STATE_COMPLETE); + } + + /** + * @covers Guzzle\Http\Message\Request::__clone + */ + public function testClonedRequestsUseNewInternalState() + { + $p = new ExponentialBackoffPlugin(); + $this->request->getEventDispatcher()->addSubscriber($p); + $h = $this->request->getHeader('Host'); + + $r = clone $this->request; + $this->assertEquals(RequestInterface::STATE_NEW, $r->getState()); + $this->assertNotSame($r->getQuery(), $this->request->getQuery()); + $this->assertNotSame($r->getCurlOptions(), $this->request->getCurlOptions()); + $this->assertNotSame($r->getEventDispatcher(), $this->request->getEventDispatcher()); + $this->assertEquals($r->getHeaders(), $this->request->getHeaders()); + $this->assertNotSame($h, $r->getHeader('Host')); + $this->assertNotSame($r->getParams(), $this->request->getParams()); + $this->assertNull($r->getParams()->get('queued_response')); + + $this->assertTrue($this->request->getEventDispatcher()->hasListeners('request.sent')); + } + + /** + * @covers Guzzle\Http\Message\Request::changedHeader + * @covers Guzzle\Http\Message\Request::setHeader + */ + public function testCatchesAllHostHeaderChanges() + { + // Tests setting using headers + $this->request->setHeader('Host', 'www.abc.com'); + $this->assertEquals('www.abc.com', $this->request->getHost()); + $this->assertEquals('www.abc.com:8124', $this->request->getHeader('Host')); + $this->assertEquals(8124, $this->request->getPort()); + + // Tests setting using setHost() + $this->request->setHost('abc.com'); + $this->assertEquals('abc.com', $this->request->getHost()); + $this->assertEquals('abc.com:8124', $this->request->getHeader('Host')); + $this->assertEquals(8124, $this->request->getPort()); + + // Tests setting with a port + $this->request->setHost('abc.com:8081'); + $this->assertEquals('abc.com', $this->request->getHost()); + $this->assertEquals('abc.com:8081', $this->request->getHeader('Host')); + $this->assertEquals(8081, $this->request->getPort()); + + // Tests setting with a port using the Host header + $this->request->setHeader('Host', 'solr.com:8983'); + $this->assertEquals('solr.com', $this->request->getHost()); + $this->assertEquals('solr.com:8983', (string) $this->request->getHeader('Host')); + $this->assertEquals(8983, $this->request->getPort()); + + // Tests setting with an inferred 443 port using the Host header + $this->request->setScheme('https'); + $this->request->setHeader('Host', 'solr.com'); + $this->assertEquals('solr.com', $this->request->getHost()); + $this->assertEquals('solr.com:8983', (string) $this->request->getHeader('Host')); + $this->assertEquals(8983, $this->request->getPort()); + } + + /** + * @covers Guzzle\Http\Message\Request::setUrl + */ + public function testRecognizesBasicAuthCredentialsInUrls() + { + $this->request->setUrl('http://michael:test@test.com/'); + $this->assertEquals('michael', $this->request->getUsername()); + $this->assertEquals('test', $this->request->getPassword()); + } + + /** + * This test launches a dummy Guzzle\Http\Server\Server object that listens + * for incoming requests. The server allows us to test how cURL sends + * requests and receives responses. We can validate the request structure + * and whether or not the response was interpreted correctly. + * + * @covers Guzzle\Http\Message\Request + */ + public function testRequestCanBeSentUsingCurl() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\r\nConnection: close\r\n\r\ndata", + "HTTP/1.1 404 Not Found\r\nContent-Encoding: application/xml\r\nContent-Length: 48\r\n\r\nFile not found" + )); + + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl()); + $request->setClient($this->client); + $response = $request->send(); + + $this->assertEquals('data', $response->getBody(true)); + $this->assertEquals(200, (int) $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, $response->getContentLength()); + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $response->getExpires()); + + // Test that the same handle can be sent twice without setting state to new + $response2 = $request->send(); + $this->assertNotSame($response, $response2); + + try { + $request = RequestFactory::getInstance()->create('GET', $this->getServer()->getUrl() . 'index.html'); + $request->setClient($this->client); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + } + + $requests = $this->getServer()->getReceivedRequests(true); + $messages = $this->getServer()->getReceivedRequests(false); + $port = $this->getServer()->getPort(); + + $userAgent = Utils::getDefaultUserAgent(); + + $this->assertEquals('127.0.0.1:' . $port, $requests[0]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[1]->getHeader('Host')); + $this->assertEquals('127.0.0.1:' . $port, $requests[2]->getHeader('Host')); + + $this->assertEquals('/', $requests[0]->getPath()); + $this->assertEquals('/', $requests[1]->getPath()); + $this->assertEquals('/index.html', $requests[2]->getPath()); + + $parts = explode("\r\n", $messages[0]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[1]); + $this->assertEquals('GET / HTTP/1.1', $parts[0]); + + $parts = explode("\r\n", $messages[2]); + $this->assertEquals('GET /index.html HTTP/1.1', $parts[0]); + } + + /** + * @covers Guzzle\Http\Message\Request::onRequestError + */ + public function testThrowsExceptionsWhenUnsuccessfulResponseIsReceivedByDefault() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"); + + try { + $request = $this->client->get('/index.html'); + $response = $request->send(); + $this->fail('Request did not receive a 404 response'); + } catch (BadResponseException $e) { + $this->assertContains('Client error response', $e->getMessage()); + $this->assertContains('[status code] 404', $e->getMessage()); + $this->assertContains('[reason phrase] Not found', $e->getMessage()); + } + } + + /** + * @covers Guzzle\Http\Message\Request::onRequestError + */ + public function testCanShortCircuitErrorHandling() + { + $request = $this->request; + $response = new Response(404); + $request->setResponse($response, true); + $out = ''; + $that = $this; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$out, $that) { + $out .= $event['request'] . "\n" . $event['response'] . "\n"; + $event->stopPropagation(); + }); + $request->send(); + $this->assertContains((string) $request, $out); + $this->assertContains((string) $request->getResponse(), $out); + $this->assertSame($response, $request->getResponse()); + } + + /** + * @covers Guzzle\Http\Message\Request::processResponse + * @covers Guzzle\Http\Message\Request::onRequestError + */ + public function testCanOverrideUnsuccessfulResponses() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 404 NOT FOUND\r\n" . + "Content-Length: 0\r\n" . + "\r\n", + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n" . + "\r\n" + )); + + $newResponse = null; + + $request = $this->request; + $request->getEventDispatcher()->addListener('request.error', function($event) use (&$newResponse) { + if ($event['response']->getStatusCode() == 404) { + $newRequest = clone $event['request']; + $newResponse = $newRequest->send(); + // Override the original response and bypass additional response processing + $event['response'] = $newResponse; + // Call $event['request']->setResponse($newResponse); to re-apply events + $event->stopPropagation(); + } + }); + + $request->send(); + + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertSame($newResponse, $request->getResponse()); + $this->assertEquals(2, count($this->getServer()->getReceivedRequests())); + } + + /** + * @covers Guzzle\Http\Message\Request + */ + public function testCanRetrieveUrlObject() + { + $request = new Request('GET', 'http://www.example.com/foo?abc=d'); + $this->assertInstanceOf('Guzzle\Http\Url', $request->getUrl(true)); + $this->assertEquals('http://www.example.com/foo?abc=d', $request->getUrl()); + $this->assertEquals('http://www.example.com/foo?abc=d', (string) $request->getUrl(true)); + } + + /** + * Users sometimes want to use a custom stream when receiving a response body. + * Because of the various potential for retrying failed requests, the stream + * specified by the user should only be written to in the event that a + * successful response was received. Otherwise, a new temp stream is created + * to store the body of the failed request. + * + * @covers Guzzle\Http\Message\Request::receiveResponseHeader + */ + public function testReceivingUnsuccessfulResponseUsesOtherResponseBody() + { + $request = new Request('GET', $this->getServer()->getUrl()); + $body = EntityBody::factory(); + $request->setResponseBody($body); + $request->receiveResponseHeader('HTTP/1.1 503 Service Unavailable'); + $this->assertNotSame($body, $request->getResponse()->getBody()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php new file mode 100644 index 0000000..e9e770a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php @@ -0,0 +1,787 @@ +response = new Response(200, new Collection(array( + 'Accept-Ranges' => 'bytes', + 'Age' => '12', + 'Allow' => 'GET, HEAD', + 'Cache-Control' => 'no-cache', + 'Content-Encoding' => 'gzip', + 'Content-Language' => 'da', + 'Content-Length' => '348', + 'Content-Location' => '/index.htm', + 'Content-Disposition' => 'attachment; filename=fname.ext', + 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==', + 'Content-Range' => 'bytes 21010-47021/47022', + 'Content-Type' => 'text/html; charset=utf-8', + 'Date' => 'Tue, 15 Nov 1994 08:12:31 GMT', + 'ETag' => '737060cd8c284d8af7ad3082f209582d', + 'Expires' => 'Thu, 01 Dec 1994 16:00:00 GMT', + 'Last-Modified' => 'Tue, 15 Nov 1994 12:45:26 GMT', + 'Location' => 'http://www.w3.org/pub/WWW/People.html', + 'Pragma' => 'no-cache', + 'Proxy-Authenticate' => 'Basic', + 'Retry-After' => '120', + 'Server' => 'Apache/1.3.27 (Unix) (Red-Hat/Linux)', + 'Set-Cookie' => 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'Trailer' => 'Max-Forwards', + 'Transfer-Encoding' => 'chunked', + 'Vary' => '*', + 'Via' => '1.0 fred, 1.1 nowhere.com (Apache/1.1)', + 'Warning' => '199 Miscellaneous warning', + 'WWW-Authenticate' => 'Basic' + )), 'body'); + } + + /** + * @covers Guzzle\Http\Message\Response::__construct + */ + public function testConstructor() + { + $params = new Collection(); + $body = EntityBody::factory(''); + $response = new Response(200, $params, $body); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals($body, $response->getBody()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Make sure Content-Length is set automatically + $response = new Response(200, $params); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", $response->getRawHeaders()); + + // Pass bodies to the response + $response = new Response(200, null, 'data'); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $response = new Response(200, null, EntityBody::factory('data')); + $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); + $this->assertEquals('data', $response->getBody(true)); + + // Make sure the proper exception is thrown + try { + //$response = new Response(200, null, array('foo' => 'bar')); + //$this->fail('Response did not throw exception when passing invalid body'); + } catch (HttpException $e) { + } + + // Ensure custom codes can be set + $response = new Response(2); + $this->assertEquals(2, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + // Make sure the proper exception is thrown when sending invalid headers + try { + $response = new Response(200, 'adidas'); + $this->fail('Response did not throw exception when passing invalid $headers'); + } catch (BadResponseException $e) { + } + } + + /** + * @covers Guzzle\Http\Message\Response::__toString + */ + public function test__toString() + { + $response = new Response(200); + $this->assertEquals("HTTP/1.1 200 OK\r\n\r\n", (string) $response); + + // Add another header + $response = new Response(200, array( + 'X-Test' => 'Guzzle' + )); + $this->assertEquals("HTTP/1.1 200 OK\r\nX-Test: Guzzle\r\n\r\n", (string) $response); + + $response = new Response(200, array( + 'Content-Length' => 4 + ), 'test'); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", (string) $response); + } + + /** + * @covers Guzzle\Http\Message\Response::fromMessage + */ + public function testFactory() + { + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('OK', $response->getReasonPhrase()); + $this->assertEquals(4, $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + + // Make sure that automatic Content-Length works + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: x\r\n\r\ntest"); + $this->assertEquals(4, $response->getContentLength()); + $this->assertEquals('test', $response->getBody(true)); + } + + /** + * @covers Guzzle\Http\Message\Response::fromMessage + */ + public function testFactoryRequiresMessage() + { + $this->assertFalse(Response::fromMessage('')); + } + + /** + * @covers Guzzle\Http\Message\Response::getBody + */ + public function testGetBody() + { + $body = EntityBody::factory(''); + $response = new Response(403, new Collection(), $body); + $this->assertEquals($body, $response->getBody()); + } + + /** + * @covers Guzzle\Http\Message\Response::getStatusCode + */ + public function testManagesStatusCode() + { + $response = new Response(403); + $this->assertEquals(403, $response->getStatusCode()); + } + + /** + * @covers Guzzle\Http\Message\Response::getMessage + */ + public function testGetMessage() + { + $response = new Response(200, new Collection(array( + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nbody", $response->getMessage()); + } + + /** + * @covers Guzzle\Http\Message\Response::getRawHeaders + */ + public function testGetRawHeaders() + { + $response = new Response(200, new Collection(array( + 'Keep-Alive' => 155, + 'User-Agent' => 'Guzzle', + 'Content-Length' => 4 + )), 'body'); + + $this->assertEquals("HTTP/1.1 200 OK\r\nKeep-Alive: 155\r\nUser-Agent: Guzzle\r\nContent-Length: 4\r\n\r\n", $response->getRawHeaders()); + } + + /** + * @covers Guzzle\Http\Message\Response::getRequest + */ + public function testGetRequest() + { + $response = new Response(200, new Collection(), 'body'); + $this->assertNull($response->getRequest()); + $request = new \Guzzle\Http\Message\Request('GET', 'http://www.guzzle-project.com/'); + $response->setRequest($request); + $this->assertEquals($request, $response->getRequest()); + } + + /** + * @covers Guzzle\Http\Message\Response::getReasonPhrase + * @covers Guzzle\Http\Message\Response::setStatus + */ + public function testHandlesStatusAndStatusCodes() + { + $response = new Response(200, new Collection(), 'body'); + $this->assertEquals('OK', $response->getReasonPhrase()); + + $this->assertSame($response, $response->setStatus(204)); + $this->assertEquals('No Content', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $this->assertSame($response, $response->setStatus(204, 'Testing!')); + $this->assertEquals('Testing!', $response->getReasonPhrase()); + $this->assertEquals(204, $response->getStatusCode()); + + $response->setStatus(2000); + $this->assertEquals(2000, $response->getStatusCode()); + $this->assertEquals('', $response->getReasonPhrase()); + + $response->setStatus(200, 'Foo'); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals('Foo', $response->getReasonPhrase()); + } + + /** + * @covers Guzzle\Http\Message\Response::isClientError + */ + public function testIsClientError() + { + $response = new Response(403); + $this->assertTrue($response->isClientError()); + $response = new Response(200); + $this->assertFalse($response->isClientError()); + } + + /** + * @covers Guzzle\Http\Message\Response::isError + */ + public function testIsError() + { + $response = new Response(403); + $this->assertTrue($response->isError()); + $response = new Response(200); + $this->assertFalse($response->isError()); + $response = new Response(500); + $this->assertTrue($response->isError()); + } + + /** + * @covers Guzzle\Http\Message\Response::isInformational + */ + public function testIsInformational() + { + $response = new Response(100); + $this->assertTrue($response->isInformational()); + $response = new Response(200); + $this->assertFalse($response->isInformational()); + } + + /** + * @covers Guzzle\Http\Message\Response::isRedirect + */ + public function testIsRedirect() + { + $response = new Response(301); + $this->assertTrue($response->isRedirect()); + $response = new Response(200); + $this->assertFalse($response->isRedirect()); + } + + /** + * @covers Guzzle\Http\Message\Response::isServerError + */ + public function testIsServerError() + { + $response = new Response(500); + $this->assertTrue($response->isServerError()); + $response = new Response(400); + $this->assertFalse($response->isServerError()); + } + + /** + * @covers Guzzle\Http\Message\Response::isSuccessful + */ + public function testIsSuccessful() + { + $response = new Response(200); + $this->assertTrue($response->isSuccessful()); + $response = new Response(403); + $this->assertFalse($response->isSuccessful()); + } + + /** + * @covers Guzzle\Http\Message\Response::setRequest + */ + public function testSetRequest() + { + $response = new Response(200); + $this->assertNull($response->getRequest()); + $r = new \Guzzle\Http\Message\Request('GET', 'http://www.guzzle-project.com/'); + $response->setRequest($r); + $this->assertEquals($r, $response->getRequest()); + } + + /** + * @covers Guzzle\Http\Message\Response::getAcceptRanges + */ + public function testGetAcceptRanges() + { + $this->assertEquals('bytes', $this->response->getAcceptRanges()); + } + + /** + * @covers Guzzle\Http\Message\Response::getAge + */ + public function testGetAge() + { + $this->assertEquals(12, $this->response->getAge(false)); + $this->assertEquals(12, $this->response->getAge(true)); + + $this->response->removeHeader('Age'); + $this->response->removeHeader('Date'); + $this->assertNull($this->response->getAge()); + + $this->response->setHeader('Date', Utils::getHttpDate(strtotime('-1 minute'))); + // If the test runs slowly, still pass with a +5 second allowance + $this->assertTrue($this->response->getAge() - 60 <= 5); + $this->assertNull($this->response->getAge(true)); + } + + /** + * @covers Guzzle\Http\Message\Response::getAllow + */ + public function testGetAllow() + { + $this->assertEquals('GET, HEAD', $this->response->getAllow()); + } + + /** + * @covers Guzzle\Http\Message\Response::getCacheControl + */ + public function testGetCacheControl() + { + $this->assertEquals('no-cache', $this->response->getCacheControl()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentEncoding + */ + public function testGetContentEncoding() + { + $this->assertEquals('gzip', $this->response->getContentEncoding()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentLanguage + */ + public function testGetContentLanguage() + { + $this->assertEquals('da', $this->response->getContentLanguage()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentLength + */ + public function testGetContentLength() + { + $this->assertEquals('348', $this->response->getContentLength()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentLocation + */ + public function testGetContentLocation() + { + $this->assertEquals('/index.htm', $this->response->getContentLocation()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentDisposition + */ + public function testGetContentDisposition() + { + $this->assertEquals('attachment; filename=fname.ext', $this->response->getContentDisposition()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentMd5 + */ + public function testGetContentMd5() + { + $this->assertEquals('Q2hlY2sgSW50ZWdyaXR5IQ==', $this->response->getContentMd5()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentRange + */ + public function testGetContentRange() + { + $this->assertEquals('bytes 21010-47021/47022', $this->response->getContentRange()); + } + + /** + * @covers Guzzle\Http\Message\Response::getContentType + */ + public function testGetContentType() + { + $this->assertEquals('text/html; charset=utf-8', $this->response->getContentType()); + } + + /** + * @covers Guzzle\Http\Message\Response::getDate + */ + public function testGetDate() + { + $this->assertEquals('Tue, 15 Nov 1994 08:12:31 GMT', $this->response->getDate()); + } + + /** + * @covers Guzzle\Http\Message\Response::getEtag + */ + public function testGetEtag() + { + $this->assertEquals('737060cd8c284d8af7ad3082f209582d', $this->response->getEtag()); + } + + /** + * @covers Guzzle\Http\Message\Response::getExpires + */ + public function testGetExpires() + { + $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $this->response->getExpires()); + } + + /** + * @covers Guzzle\Http\Message\Response::getLastModified + */ + public function testGetLastModified() + { + $this->assertEquals('Tue, 15 Nov 1994 12:45:26 GMT', $this->response->getLastModified()); + } + + /** + * @covers Guzzle\Http\Message\Response::getLocation + */ + public function testGetLocation() + { + $this->assertEquals('http://www.w3.org/pub/WWW/People.html', $this->response->getLocation()); + } + + /** + * @covers Guzzle\Http\Message\Response::getPragma + */ + public function testGetPragma() + { + $this->assertEquals('no-cache', $this->response->getPragma()); + } + + /** + * @covers Guzzle\Http\Message\Response::getProxyAuthenticate + */ + public function testGetProxyAuthenticate() + { + $this->assertEquals('Basic', $this->response->getProxyAuthenticate()); + } + + /** + * @covers Guzzle\Http\Message\Response::getRetryAfter + */ + public function testGetRetryAfter() + { + $this->assertEquals('120', $this->response->getRetryAfter()); + $t = time() + 1000; + $d = $t - time(); + $this->response->setHeader('Retry-After', date('r', $t)); + $this->assertEquals($d, $this->response->getRetryAfter()); + $this->response->removeHeader('Retry-After'); + $this->assertNull($this->response->getRetryAfter()); + } + + /** + * @covers Guzzle\Http\Message\Response::getServer + */ + public function testGetServer() + { + $this->assertEquals('Apache/1.3.27 (Unix) (Red-Hat/Linux)', $this->response->getServer()); + } + + /** + * @covers Guzzle\Http\Message\Response::getSetCookie + */ + public function testGetSetCookie() + { + $this->assertEquals('UserID=JohnDoe; Max-Age=3600; Version=1', $this->response->getSetCookie()); + } + + /** + * @covers Guzzle\Http\Message\Response::getSetCookie + */ + public function testGetSetCookieNormalizesHeaders() + { + $this->response->addHeaders(array( + 'Set-Cooke' => 'boo', + 'set-cookie' => 'foo' + )); + + $this->assertEquals(array( + 'UserID=JohnDoe; Max-Age=3600; Version=1', + 'foo' + ), $this->response->getSetCookie()->toArray()); + + $this->response->addHeaders(array( + 'set-cookie' => 'fubu' + )); + $this->assertEquals(array('UserID=JohnDoe; Max-Age=3600; Version=1', 'foo', 'fubu'), $this->response->getSetCookie()->toArray()); + } + + /** + * @covers Guzzle\Http\Message\Response::getTrailer + */ + public function testGetTrailer() + { + $this->assertEquals('Max-Forwards', $this->response->getTrailer()); + } + + /** + * @covers Guzzle\Http\Message\Response::getTransferEncoding + */ + public function testGetTransferEncoding() + { + $this->assertEquals('chunked', $this->response->getTransferEncoding()); + } + + /** + * @covers Guzzle\Http\Message\Response::getVary + */ + public function testGetVary() + { + $this->assertEquals('*', $this->response->getVary()); + } + + /** + * @covers Guzzle\Http\Message\Response::getVia + */ + public function testReturnsViaHeader() + { + $this->assertEquals('1.0 fred, 1.1 nowhere.com (Apache/1.1)', $this->response->getVia()); + } + /** + * @covers Guzzle\Http\Message\Response::getWarning + */ + public function testGetWarning() + { + $this->assertEquals('199 Miscellaneous warning', $this->response->getWarning()); + } + + /** + * @covers Guzzle\Http\Message\Response::getWwwAuthenticate + */ + public function testReturnsWwwAuthenticateHeader() + { + $this->assertEquals('Basic', $this->response->getWwwAuthenticate()); + } + + /** + * @covers Guzzle\Http\Message\Response::getConnection + */ + public function testReturnsConnectionHeader() + { + $this->assertEquals(null, $this->response->getConnection()); + $this->response->setHeader('Connection', 'close'); + $this->assertEquals('close', $this->response->getConnection()); + } + + /** + * @covers Guzzle\Http\Message\Response::getHeader + */ + public function testReturnsHeaders() + { + $this->assertEquals('Basic', $this->response->getHeader('WWW-Authenticate', null, true)); + $this->assertEquals('chunked', $this->response->getHeader('Transfer-Encoding', null, false)); + } + + /** + * @covers Guzzle\Http\Message\Response::setInfo + * @covers Guzzle\Http\Message\Response::getInfo + */ + public function testHasTransferInfo() + { + $stats = array ( + 'url' => 'http://www.google.com/', + 'content_type' => 'text/html; charset=ISO-8859-1', + 'http_code' => 200, + 'header_size' => 606, + 'request_size' => 53, + 'filetime' => -1, + 'ssl_verify_result' => 0, + 'redirect_count' => 0, + 'total_time' => 0.093284, + 'namelookup_time' => 0.001349, + 'connect_time' => 0.01635, + 'pretransfer_time' => 0.016358, + 'size_upload' => 0, + 'size_download' => 10330, + 'speed_download' => 110737, + 'speed_upload' => 0, + 'download_content_length' => -1, + 'upload_content_length' => 0, + 'starttransfer_time' => 0.07066, + 'redirect_time' => 0, + ); + + // Uninitialized state + $this->assertNull($this->response->getInfo('url')); + $this->assertEquals(array(), $this->response->getInfo()); + + // Set the stats + $this->response->setInfo($stats); + $this->assertEquals($stats, $this->response->getInfo()); + $this->assertEquals(606, $this->response->getInfo('header_size')); + $this->assertNull($this->response->getInfo('does_not_exist')); + } + + /** + * @return Response + */ + private function getResponse($code, array $headers = null, EntityBody $body = null) + { + return new Response($code, $headers, $body); + } + + /** + * @covers Guzzle\Http\Message\Response::canCache + */ + public function testDeterminesIfItCanBeCached() + { + $this->assertTrue($this->getResponse(200)->canCache()); + $this->assertTrue($this->getResponse(410)->canCache()); + $this->assertFalse($this->getResponse(404)->canCache()); + $this->assertTrue($this->getResponse(200, array( + 'Cache-Control' => 'public' + ))->canCache()); + + // This has the no-store directive + $this->assertFalse($this->getResponse(200, array( + 'Cache-Control' => 'private, no-store' + ))->canCache()); + + // The body cannot be read, so it cannot be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertFalse($this->getResponse(200, array( + 'Transfer-Encoding' => 'chunked' + ), EntityBody::factory($resource, 10))->canCache()); + unlink($tmp); + + // The body is 0 length, cannot be read, so it can be cached + $tmp = tempnam('/tmp', 'not-readable'); + $resource = fopen($tmp, 'w'); + $this->assertTrue($this->getResponse(200, array(array( + 'Content-Length' => 0 + )), EntityBody::factory($resource, 0))->canCache()); + unlink($tmp); + } + + /** + * @covers Guzzle\Http\Message\Response::getMaxAge + */ + public function testDeterminesResponseMaxAge() + { + $this->assertEquals(null, $this->getResponse(200)->getMaxAge()); + + // Uses the response's s-maxage + $this->assertEquals(140, $this->getResponse(200, array( + 'Cache-Control' => 's-maxage=140' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120' + ))->getMaxAge()); + + // Uses the response's max-age + $this->assertEquals(120, $this->getResponse(200, array( + 'Cache-Control' => 'max-age=120', + 'Expires' => Utils::getHttpDate('+1 day') + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => Utils::getHttpDate('+1 day') + ))->getMaxAge()); + + // Uses the Expires date + $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( + 'Expires' => Utils::getHttpDate('+1 day') + ))->getMaxAge()); + } + + /** + * @covers Guzzle\Http\Message\Response::canValidate + */ + public function testDeterminesIfItCanValidate() + { + $response = new Response(200); + $this->assertFalse($response->canValidate()); + $response->setHeader('ETag', '123'); + $this->assertTrue($response->canValidate()); + $response->removeHeader('ETag'); + $this->assertFalse($response->canValidate()); + $response->setHeader('Last-Modified', '123'); + $this->assertTrue($response->canValidate()); + } + + /** + * @covers Guzzle\Http\Message\Response::getFreshness + * @covers Guzzle\Http\Message\Response::isFresh + */ + public function testCalculatesFreshness() + { + $response = new Response(200); + $this->assertNull($response->isFresh()); + $this->assertNull($response->getFreshness()); + + $response->addCacheControlDirective('max-age', 120); + $response->setHeader('Age', 100); + $this->assertEquals(20, $response->getFreshness()); + $this->assertTrue($response->isFresh()); + + $response->setHeader('Age', 150); + $this->assertEquals(-30, $response->getFreshness()); + $this->assertFalse($response->isFresh()); + } + + /** + * @covers Guzzle\Http\Message\Response::setProtocol + * @covers Guzzle\Http\Message\Response::getProtocol + * @covers Guzzle\Http\Message\Response::getProtocolVersion + */ + public function testHandlesProtocols() + { + $this->assertSame($this->response, $this->response->setProtocol('HTTP', '1.0')); + $this->assertEquals('HTTP', $this->response->getProtocol()); + $this->assertEquals('1.0', $this->response->getProtocolVersion()); + } + + /** + * @covers Guzzle\Http\Message\Response::isContentType + */ + public function testComparesContentType() + { + $response = new Response(200, array( + 'Content-Type' => 'text/html; charset=ISO-8859-4' + )); + + $this->assertTrue($response->isContentType('text/html')); + $this->assertTrue($response->isContentType('TExT/html')); + $this->assertTrue($response->isContentType('charset=ISO-8859-4')); + $this->assertFalse($response->isContentType('application/xml')); + } + + /** + * @covers Guzzle\Http\Message\Response::isMethodAllowed + */ + public function testResponseDeterminesIfMethodIsAllowedBaseOnAllowHeader() + { + $response = new Response(200, array( + 'Allow' => 'OPTIONS, POST, deletE,GET' + )); + + $this->assertTrue($response->isMethodAllowed('get')); + $this->assertTrue($response->isMethodAllowed('GET')); + $this->assertTrue($response->isMethodAllowed('options')); + $this->assertTrue($response->isMethodAllowed('post')); + $this->assertTrue($response->isMethodAllowed('Delete')); + $this->assertFalse($response->isMethodAllowed('put')); + $this->assertFalse($response->isMethodAllowed('PUT')); + + $response = new Response(200); + $this->assertFalse($response->isMethodAllowed('get')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/AsyncPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/AsyncPluginTest.php new file mode 100644 index 0000000..d5d9432 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/AsyncPluginTest.php @@ -0,0 +1,105 @@ +assertArrayHasKey('request.before_send', $events); + $this->assertArrayHasKey('request.exception', $events); + $this->assertArrayHasKey('curl.callback.progress', $events); + } + + /** + * @covers Guzzle\Http\Plugin\AsyncPlugin::onBeforeSend + */ + public function testEnablesProgressCallbacks() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $event = new Event(array( + 'request' => $request + )); + $p->onBeforeSend($event); + $this->assertEquals(true, $request->getCurlOptions()->get('progress')); + } + + /** + * @covers Guzzle\Http\Plugin\AsyncPlugin::onCurlProgress + */ + public function testAddsTimesOutAfterSending() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $handle = CurlHandle::factory($request); + $event = new Event(array( + 'request' => $request, + 'handle' => $handle, + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + $this->assertEquals(1, $handle->getOptions()->get(CURLOPT_TIMEOUT_MS)); + $this->assertEquals(true, $handle->getOptions()->get(CURLOPT_NOBODY)); + } + + /** + * @covers Guzzle\Http\Plugin\AsyncPlugin::onCurlProgress + */ + public function testEnsuresRequestIsSet() + { + $p = new AsyncPlugin(); + $event = new Event(array( + 'uploaded' => 10, + 'upload_size' => 10, + 'downloaded' => 0 + )); + $p->onCurlProgress($event); + } + + /** + * @covers Guzzle\Http\Plugin\AsyncPlugin::onRequestTimeout + */ + public function testMasksCurlExceptions() + { + $p = new AsyncPlugin(); + $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com'); + $e = new CurlException('Error'); + $event = new Event(array( + 'request' => $request, + 'exception' => $e + )); + $p->onRequestTimeout($event); + $this->assertEquals(RequestInterface::STATE_COMPLETE, $request->getState()); + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + } + + public function testEnsuresIntegration() + { + $this->getServer()->enqueue("HTTP/1.1 204 FOO\r\nContent-Length: 4\r\n\r\ntest"); + $client = new Client($this->getServer()->getUrl()); + $request = $client->post('/', null, array( + 'foo' => 'bar' + )); + $request->getEventDispatcher()->addSubscriber(new AsyncPlugin()); + $request->send(); + $this->assertEquals('', $request->getResponse()->getBody(true)); + $this->assertTrue($request->getResponse()->hasHeader('X-Guzzle-Async')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CachePluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CachePluginTest.php new file mode 100644 index 0000000..be16ec9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CachePluginTest.php @@ -0,0 +1,470 @@ +cache = new ArrayCache(); + $this->adapter = new DoctrineCacheAdapter($this->cache); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::__construct + */ + public function testConstructorSetsValues() + { + $plugin = new CachePlugin($this->adapter, 1200); + + $this->assertEquals($this->adapter, $this->readAttribute($plugin, 'adapter')); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestSent + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestBeforeSend + * @covers Guzzle\Http\Plugin\CachePlugin::saveCache + * @covers Guzzle\Http\Plugin\CachePlugin::getCacheKey + * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest + */ + public function testSavesResponsesInCache() + { + // Send a 200 OK script to the testing server + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest" + )); + + // Create a new Cache plugin + $plugin = new CachePlugin($this->adapter); + $client = new Client($this->getServer()->getUrl()); + $client->setCurlMulti(new \Guzzle\Http\Curl\CurlMulti()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get(); + $request->send(); + + // Calculate the cache key like the cache plugin does + $key = $plugin->getCacheKey($request); + // Make sure that the cache plugin set the request in cache + $this->assertNotNull($this->adapter->fetch($key)); + + // Clear out the requests stored on the server to make sure we didn't send a new request + $this->getServer()->flush(); + + // Test that the request is set manually + // The test server has no more script data, so if it actually sends a + // request it will fail the test. + $this->assertEquals($key, $plugin->getCacheKey($request)); + $request->setState('new'); + $request->send(); + $this->assertEquals('data', $request->getResponse()->getBody(true)); + + // Make sure a request wasn't sent + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestSent + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestBeforeSend + * @covers Guzzle\Http\Plugin\CachePlugin::saveCache + */ + public function testSkipsNonReadableResponseBodies() + { + // Send a 200 OK script to the testing server + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + + // Create a new Cache plugin + $plugin = new CachePlugin($this->adapter); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + // Create a new request using the Cache plugin + $request = $client->get(); + + // Create a temp file that is not readable + $tempFile = tempnam('/tmp', 'temp_stream_data'); + // Set the non-readable stream as the response body so that it can't be cached + $request->setResponseBody(EntityBody::factory( + fopen($tempFile, 'w') + )); + + $request->send(); + + // Calculate the cache key like the cache plugin does + $key = $plugin->getCacheKey($request); + // Make sure that the cache plugin set the request in cache + $this->assertFalse($this->adapter->fetch($key)); + } + + public function cacheKeyDataProvider() + { + $r = array( + array('', 'gz_get_http_www.test.com/path?q=abc_host=www.test.com&date=123', 'http://www.test.com/path?q=abc', "Host: www.test.com\r\nDate: 123"), + array('query = q', 'gz_get_http_www.test.com/path_host=www.test.com&date=123', 'http://www.test.com/path?q=abc', "Host: www.test.com\r\nDate: 123"), + array('query=q; header=Date;', 'gz_get_http_www.test.com/path_host=www.test.com', 'http://www.test.com/path?q=abc', "Host: www.test.com\r\nDate: 123"), + array('query=a, q; header=Date, Host;', 'gz_get_http_www.test.com/path_', 'http://www.test.com/path?q=abc&a=123', "Host: www.test.com\r\nDate: 123"), + ); + + return $r; + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::getCacheKey + * @dataProvider cacheKeyDataProvider + */ + public function testCreatesCacheKeysUsingFilters($filter, $key, $url, $headers = null) + { + // Create a new Cache plugin + $plugin = new CachePlugin($this->adapter); + + // Generate the header array + $h = null; + if ($headers) { + $h = array(); + foreach (explode("\r\n", $headers) as $header) { + list($k, $v) = explode(': ', $header); + $h[$k] = $v; + } + } + + // Create the request + $request = RequestFactory::getInstance()->create('GET', $url, $h); + $request->getParams()->set('cache.key_filter', $filter); + $request->removeHeader('User-Agent'); + + $this->assertEquals($key, $plugin->getCacheKey($request, true)); + + // Make sure that the encoded request is returned when $raw is false + $this->assertNotEquals($key, $plugin->getCacheKey($request)); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::getCacheKey + */ + public function testCreatesEncodedKeys() + { + $plugin = new CachePlugin($this->adapter); + $request = RequestFactory::getInstance()->fromMessage( + "GET / HTTP/1.1\r\nHost: www.test.com\r\nCache-Control: no-cache, no-store, max-age=120" + ); + + $key = $plugin->getCacheKey($request); + + $this->assertEquals(1, preg_match('/^gz_[a-z0-9]{32}$/', $key)); + + // Make sure that the same value is returned in a subsequent call + $this->assertEquals($key, $plugin->getCacheKey($request)); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestSent + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestBeforeSend + * @covers Guzzle\Http\Plugin\CachePlugin::saveCache + */ + public function testRequestsCanOverrideTtlUsingCacheParam() + { + $plugin = new CachePlugin($this->adapter); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get('http://www.test.com/'); + $request->getParams()->set('cache.override_ttl', 1000); + $request->setResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nCache-Control: max-age=100\r\nContent-Length: 4\r\n\r\nData"), true); + $request->send(); + + $request2 = $client->get('http://www.test.com/'); + $response = $request2->send(); + + $token = $response->getTokenizedHeader('X-Guzzle-Cache', ', '); + $this->assertEquals(1000, $token['ttl']); + $this->assertEquals(true, $token->hasKey('key')); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestSent + * @covers Guzzle\Http\Plugin\CachePlugin::onRequestBeforeSend + * @covers Guzzle\Http\Plugin\CachePlugin::saveCache + */ + public function testRequestsCanAcceptStaleResponses() + { + $server = $this->getServer(); + + $client = new Client($this->getServer()->getUrl()); + $plugin = new CachePlugin($this->adapter); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get('test'); + // Cache this response for 1000 seconds if it is cacheable + $request->getParams()->set('cache.override_ttl', 1000); + $request->setResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nExpires: " . Utils::getHttpDate('-1 second') . "\r\nContent-Length: 4\r\n\r\nData"), true); + $request->send(); + + sleep(1); + + // Accept responses that are up to 100 seconds expired + $request2 = $client->get('test'); + $request2->addCacheControlDirective('max-stale', 100); + $response = $request2->send(); + $token = $response->getTokenizedHeader('X-Guzzle-Cache', ', '); + $this->assertEquals(1000, $token['ttl']); + + // Accepts any stale response + $request3 = $client->get('test'); + $request3->addCacheControlDirective('max-stale'); + $response = $request3->send(); + $token = $response->getTokenizedHeader('X-Guzzle-Cache', ', '); + $this->assertEquals(1000, $token['ttl']); + + // Will not accept the stale cached entry + $server->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nData"); + $request4 = $client->get('test'); + $request4->addCacheControlDirective('max-stale', 0); + $response = $request4->send(); + $this->assertEquals("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nData", $this->removeKeepAlive((string) $response)); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest + */ + public function testChecksIfResponseCanSatisfyRequest() + { + $plugin = new CachePlugin($this->adapter); + + // Send some responses to the test server for cache validation + $server = $this->getServer(); + + // No restrictions + $request = RequestFactory::getInstance()->create('GET', $server->getUrl()); + $response = new Response(200, array('Date' => Utils::getHttpDate('now'))); + $this->assertTrue($plugin->canResponseSatisfyRequest($request, $response)); + + // Request max-age is less than response age + $request = RequestFactory::getInstance()->create('GET', $server->getUrl()); + $request->addCacheControlDirective('max-age', 100); + $response = new Response(200, array('Age' => 10)); + $this->assertTrue($plugin->canResponseSatisfyRequest($request, $response)); + + // Request must have something fresher than 200 seconds + $response->setHeader('Date', Utils::getHttpDate('-200 days')); + $response->removeHeader('Age'); + $request->setHeader('Cache-Control', 'max-age=200'); + $this->assertFalse($plugin->canResponseSatisfyRequest($request, $response)); + + // Response says it's too old + $request->removeHeader('Cache-Control'); + $response->setHeader('Cache-Control', 'max-age=86400'); + $this->assertFalse($plugin->canResponseSatisfyRequest($request, $response)); + + // Response is OK + $response->setHeader('Date', Utils::getHttpDate('-1 hour')); + $this->assertTrue($plugin->canResponseSatisfyRequest($request, $response)); + } + + /** + * Data provider to test cache revalidation + * + * @return array + */ + public function cacheRevalidationDataProvider() + { + return array( + // Forces revalidation that passes + array( + true, + "Pragma: no-cache\r\n\r\n", + "HTTP/1.1 200 OK\r\nDate: " . Utils::getHttpDate('-100 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nCache-Control: max-age=2000000\r\nContent-Length: 0\r\n\r\n", + ), + // Forces revalidation that overwrites what is in cache + array( + false, + "\r\n\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: must-revalidate, no-cache\r\nDate: " . Utils::getHttpDate('-10 hours') . "\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nDatas", + "HTTP/1.1 200 OK\r\nContent-Length: 5\r\nDate: " . Utils::getHttpDate('now') . "\r\n\r\nDatas" + ), + // Must get a fresh copy because the request is declining revalidation + array( + false, + "\r\n\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . Utils::getHttpDate('-3 hours') . "\r\nContent-Length: 4\r\n\r\nData", + null, + null, + 'never' + ), + // Skips revalidation because the request is accepting the cached copy + array( + true, + "\r\n\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . Utils::getHttpDate('-3 hours') . "\r\nContent-Length: 4\r\n\r\nData", + null, + null, + 'skip' + ), + // Throws an exception during revalidation + array( + false, + "\r\n\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . Utils::getHttpDate('-3 hours') . "\r\n\r\nData", + "HTTP/1.1 500 INTERNAL SERVER ERROR\r\nContent-Length: 0\r\n\r\n" + ), + // ETag mismatch + array( + false, + "\r\n\r\n", + "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nETag: \"123\"\r\nDate: " . Utils::getHttpDate('-10 hours') . "\r\n\r\nData", + "HTTP/1.1 304 NOT MODIFIED\r\nETag: \"123456\"\r\n\r\n", + ), + ); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::canResponseSatisfyRequest + * @covers Guzzle\Http\Plugin\CachePlugin::revalidate + * @dataProvider cacheRevalidationDataProvider + */ + public function testRevalidatesResponsesAgainstOriginServer($can, $request, $response, $validate = null, $result = null, $param = null) + { + // Send some responses to the test server for cache validation + $server = $this->getServer(); + $server->flush(); + + if ($validate) { + $server->enqueue($validate); + } + + $request = RequestFactory::getInstance()->fromMessage("GET / HTTP/1.1\r\nHost: 127.0.0.1:" . $server->getPort() . "\r\n" . $request); + $response = Response::fromMessage($response); + $request->setClient(new Client()); + + if ($param) { + $request->getParams()->set('cache.revalidate', $param); + } + + $plugin = new CachePlugin($this->adapter); + $this->assertEquals($can, $plugin->canResponseSatisfyRequest($request, $response), '-> ' . $request . "\n" . $response); + + if ($result) { + // Get rid of dates + $this->assertEquals( + preg_replace('/(Date:\s)(.*)(\r\n)/', '$1$3', (string) $result), + preg_replace('/(Date:\s)(.*)(\r\n)/', '$1$3', (string) $request->getResponse()) + ); + } + + if ($validate) { + $this->assertEquals(1, count($server->getReceivedRequests())); + } + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin + */ + public function testCachesResponsesAndHijacksRequestsWhenApplicable() + { + $server = $this->getServer(); + $server->flush(); + $server->enqueue("HTTP/1.1 200 OK\r\nCache-Control: max-age=1000\r\nContent-Length: 4\r\n\r\nData"); + + $plugin = new CachePlugin($this->adapter); + $client = new Client($server->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get(); + $request->getCurlOptions()->set(CURLOPT_TIMEOUT, 2); + $request2 = $client->get(); + $request2->getCurlOptions()->set(CURLOPT_TIMEOUT, 2); + $request->send(); + $request2->send(); + + $this->assertEquals(1, count($server->getReceivedRequests())); + $this->assertEquals(true, $request2->getResponse()->hasHeader('X-Guzzle-Cache')); + } + + /** + * @covers Guzzle\Http\Plugin\CachePlugin::revalidate + * @expectedException Guzzle\Http\Exception\BadResponseException + */ + public function testRemovesMissingEntitesFromCacheWhenRevalidating() + { + $server = $this->getServer(); + $server->enqueue(array( + "HTTP/1.1 200 OK\r\nCache-Control: max-age=1000, no-cache\r\nContent-Length: 4\r\n\r\nData", + "HTTP/1.1 404 NOT FOUND\r\nContent-Length: 0\r\n\r\n" + )); + + $plugin = new CachePlugin($this->adapter); + $client = new Client($server->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request1 = $client->get('/'); + $request1->send(); + $this->assertTrue($this->cache->contains($plugin->getCacheKey($request1))); + $client->get('/')->send(); + } + + public function testOnlyCachesCacheableRequests() + { + $plugin = new CachePlugin($this->adapter); + $client = new Client($this->getServer()->getUrl()); + $plugin->onRequestBeforeSend(new Event(array('request' => $client->post('/')))); + $this->assertEquals(0, count($this->readAttribute($plugin, 'cached'))); + } + + public function testAllowsCustomCacheFilterStrategies() + { + $plugin = new CachePlugin($this->adapter); + $client = new Client($this->getServer()->getUrl(), array( + 'params.cache.filter_strategy' => function ($request) { + return true; + } + )); + $plugin->onRequestBeforeSend(new Event(array('request' => $client->post('/')))); + $this->assertEquals(1, count($this->readAttribute($plugin, 'cached'))); + + $client->getConfig()->set('params.cache.filter_strategy', function ($request) { + return false; + }); + $plugin->onRequestBeforeSend(new Event(array('request' => $client->get('/')))); + $this->assertEquals(1, count($this->readAttribute($plugin, 'cached'))); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CookiePluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CookiePluginTest.php new file mode 100644 index 0000000..61aa4f6 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CookiePluginTest.php @@ -0,0 +1,126 @@ +getMockBuilder('Guzzle\\Http\\CookieJar\\ArrayCookieJar') + ->setMethods(array('addCookiesFromResponse')) + ->getMock(); + + $mock->expects($this->exactly(1)) + ->method('addCookiesFromResponse') + ->with($response); + + $plugin = new CookiePlugin($mock); + $plugin->onRequestSent(new Event(array( + 'response' => $response + ))); + } + + public function testAddsCookiesToRequests() + { + $cookie = new Cookie(array( + 'name' => 'foo', + 'value' => 'bar' + )); + + $mock = $this->getMockBuilder('Guzzle\\Http\\CookieJar\\ArrayCookieJar') + ->setMethods(array('getMatchingCookies')) + ->getMock(); + + $mock->expects($this->once()) + ->method('getMatchingCookies') + ->will($this->returnValue(array($cookie))); + + $plugin = new CookiePlugin($mock); + + $client = new Client(); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get('http://www.example.com'); + $plugin->onRequestBeforeSend(new Event(array( + 'request' => $request + ))); + + $this->assertEquals('bar', $request->getCookie('foo')); + } + + public function testCookiesAreExtractedFromRedirectResponses() + { + $plugin = new CookiePlugin(new ArrayCookieJar()); + $this->getServer()->enqueue(array( + "HTTP/1.1 302 Moved Temporarily\r\n" . + "Set-Cookie: test=583551; expires=Wednesday, 23-Mar-2050 19:49:45 GMT; path=/\r\n" . + "Location: /redirect\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n", + + "HTTP/1.1 200 OK\r\n" . + "Content-Length: 0\r\n\r\n" + )); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + + $request = $client->get(); + $request->send(); + + $request = $client->get(); + $request->send(); + + $this->assertEquals('test=583551', $request->getHeader('Cookie')); + } + + public function testCookiesAreNotAddedWhenParamIsSet() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + + $jar->add(new Cookie(array( + 'domain' => 'example.com', + 'path' => '/', + 'name' => 'test', + 'value' => 'hi', + 'expires' => time() + 3600 + ))); + + $client = new Client('http://example.com'); + $client->getEventDispatcher()->addSubscriber($plugin); + + // Ensure that it is normally added + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertEquals('hi', $request->getCookie('test')); + + // Now ensure that it is not added + $request = $client->get(); + $request->getParams()->set('cookies.disable', true); + $request->setResponse(new Response(200), true); + $request->send(); + $this->assertNull($request->getCookie('test')); + } + + public function testProvidesCookieJar() + { + $jar = new ArrayCookieJar(); + $plugin = new CookiePlugin($jar); + $this->assertSame($jar, $plugin->getCookieJar()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CurlAuthPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CurlAuthPluginTest.php new file mode 100644 index 0000000..fff1f80 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/CurlAuthPluginTest.php @@ -0,0 +1,34 @@ +getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('michael', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + } + + public function testAddsDigestAuthentication() + { + $plugin = new CurlAuthPlugin('julian', 'test', CURLAUTH_DIGEST); + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get('/'); + $this->assertEquals('julian', $request->getUsername()); + $this->assertEquals('test', $request->getPassword()); + $this->assertEquals('julian:test', $request->getCurlOptions()->get(CURLOPT_USERPWD)); + $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/ExponentialBackoffTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/ExponentialBackoffTest.php new file mode 100644 index 0000000..2a0ba60 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/ExponentialBackoffTest.php @@ -0,0 +1,310 @@ +assertEquals(2, $plugin->getMaxRetries()); + $this->assertEquals(array(500, 503, 502), $plugin->getFailureCodes()); + + // You can specify any codes you want... Probably not a good idea though + $plugin->setFailureCodes(array(200, 204)); + $this->assertEquals(array(200, 204), $plugin->getFailureCodes()); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::calculateWait + */ + public function testCalculateWait() + { + $plugin = new ExponentialBackoffPlugin(2); + $this->assertEquals(1, $plugin->calculateWait(0)); + $this->assertEquals(2, $plugin->calculateWait(1)); + $this->assertEquals(4, $plugin->calculateWait(2)); + $this->assertEquals(8, $plugin->calculateWait(3)); + $this->assertEquals(16, $plugin->calculateWait(4)); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin + */ + public function testRetriesRequests() + { + // Create a script to return several 500 and 503 response codes + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new ExponentialBackoffPlugin(2, null, array($this, 'delayClosure')); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + + // Make sure it eventually completed successfully + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('OK', $request->getResponse()->getReasonPhrase()); + $this->assertEquals('data', $request->getResponse()->getBody(true)); + + // Check that three requests were made to retry this request + $this->assertEquals(3, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin + */ + public function testRetriesRequestsUsingReasonPhraseMatch() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 400 FooError\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 400 FooError\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $plugin = new ExponentialBackoffPlugin(2, array('FooError'), array($this, 'delayClosure')); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + + // Make sure it eventually completed successfully + $this->assertEquals(200, $request->getResponse()->getStatusCode()); + $this->assertEquals('data', $request->getResponse()->getBody(true)); + + // Check that three requests were made to retry this request + $this->assertEquals(3, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestSent + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestPoll + * @covers Guzzle\Http\Message\Request + * @expectedException Guzzle\Http\Exception\BadResponseException + */ + public function testAllowsFailureOnMaxRetries() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n" + )); + + $plugin = new ExponentialBackoffPlugin(2, null, array($this, 'delayClosure')); + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + // This will fail because the plugin isn't retrying the request because + // the max number of retries is exceeded (1 > 0) + $request->send(); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestSent + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestPoll + * @covers Guzzle\Http\Curl\CurlMulti + */ + public function testRetriesPooledRequestsUsingDelayAndPollingEvent() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + // Need to sleep for one second to make sure that the polling works + // correctly in the observer + $plugin = new ExponentialBackoffPlugin(1, null, function($r) { + return 1; + }); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + + // Make sure it eventually completed successfully + $this->assertEquals('data', $request->getResponse()->getBody(true)); + + // Check that two requests were made to retry this request + $this->assertEquals(2, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::getDefaultFailureCodes + */ + public function testReturnsDefaultFailureCodes() + { + $this->assertNotEmpty(ExponentialBackoffPlugin::getDefaultFailureCodes()); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::__construct + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::getDefaultFailureCodes + */ + public function testUsesDefaultFailureCodesByDefault() + { + $p = new ExponentialBackoffPlugin(); + $this->assertEquals($p->getFailureCodes(), ExponentialBackoffPlugin::getDefaultFailureCodes()); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestSent + */ + public function testAllowsCallableFailureCodes() + { + $a = 0; + $plugin = new ExponentialBackoffPlugin(1, function($request, $response) use (&$a) { + // Look for a Foo header + if ($response->hasHeader('Foo')) { + $a = 1; + return true; + } + }, array($this, 'delayClosure')); + + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 0\r\n\r\n", + "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata" + )); + + $client = new Client($this->getServer()->getUrl()); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + $request->send(); + + // Make sure it eventually completed successfully + $this->assertEquals('data', $request->getResponse()->getBody(true)); + // Check that the callback saw the request and header + $this->assertEquals(1, $a); + // Check that two requests were made to retry this request + $this->assertEquals(2, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestSent + */ + public function testExponentiallyBacksOffCurlErrors() + { + $plugin = $this->getMock('Guzzle\Http\Plugin\ExponentialBackoffPlugin', array('retryRequest')); + + // Mock the retryRequest method so that it does nothing, but ensure + // that it is called exactly once + $plugin->expects($this->once()) + ->method('retryRequest') + ->will($this->returnValue(null)); + + // Create an exception that is found in the default curl exception list + $exception = new CurlException('Curl'); + $exception->setError('foo', CURLE_OPERATION_TIMEOUTED); + + // Create a dummy event to send to the plugin + $event = new Event(array( + 'request' => new Request('GET', 'http://test.com'), + 'response' => null, + 'exception' => $exception + )); + // Ensure the it uses the name we're looking for + $event->setName('request.exception'); + + // Trigger the event + $plugin->onRequestSent($event); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestSent + */ + public function testAllowsCustomFailureMethodsToPuntToDefaultMethod() + { + $count = 0; + + $plugin = $this->getMockBuilder('Guzzle\Http\Plugin\ExponentialBackoffPlugin') + ->setMethods(array('retryRequest')) + ->setConstructorArgs(array(2, function() use (&$count) { + $count++; + }, array($this, 'delayClosure'))) + ->getMock(); + + $plugin->expects($this->once()) + ->method('retryRequest') + ->will($this->returnValue(null)); + + $event = new Event(array( + 'request' => new Request('GET', 'http://test.com'), + 'response' => new Response(500) + )); + $event->setName('request.exception'); + + $plugin->onRequestSent($event); + $this->assertEquals(1, $count); + } + + /** + * @covers Guzzle\Http\Plugin\ExponentialBackoffPlugin::onRequestPoll + */ + public function testSeeksToBeginningOfRequestBodyWhenRetrying() + { + // Create a mock curl multi object + $multi = $this->getMockBuilder('Guzzle\Http\Curl\CurlMulti') + ->setMethods(array('remove', 'add')) + ->getMock(); + + // Create a request with a body + $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); + $request->setBody('abc'); + // Set the retry time to be something that will be retried always + $request->getParams()->set('plugins.exponential_backoff.retry_time', 2); + // Seek to the end of the stream + $request->getBody()->seek(3); + $this->assertEquals('', $request->getBody()->read(1)); + + // Create a plugin that does not delay when retrying + $plugin = new ExponentialBackoffPlugin(2, null, array($this, 'delayClosure')); + + // Create an event that is expected for the Poll event + $event = new Event(array( + 'request' => $request, + 'curl_multi' => $multi + )); + $event->setName(CurlMultiInterface::POLLING_REQUEST); + + $plugin->onRequestPoll($event); + + // Ensure that the stream was seeked to 0 + $this->assertEquals('a', $request->getBody()->read(1)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/HistoryPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/HistoryPluginTest.php new file mode 100644 index 0000000..bce9aa1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/HistoryPluginTest.php @@ -0,0 +1,146 @@ +get(); + $requests[$i]->setResponse(new Response(200), true); + $requests[$i]->send(); + $h->add($requests[$i]); + } + + return $requests; + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::getSubscribedEvents + */ + public function testDescribesSubscribedEvents() + { + $this->assertInternalType('array', HistoryPlugin::getSubscribedEvents()); + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::getLimit + * @covers Guzzle\Http\Plugin\HistoryPlugin::setLimit + */ + public function testMaintainsLimitValue() + { + $h = new HistoryPlugin(); + $this->assertSame($h, $h->setLimit(10)); + $this->assertEquals(10, $h->getLimit()); + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::add + * @covers Guzzle\Http\Plugin\HistoryPlugin::count + * @covers Guzzle\Http\Plugin\HistoryPlugin::getIterator + */ + public function testAddsRequests() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 1); + $this->assertEquals(1, count($h)); + $i = $h->getIterator(); + $this->assertEquals(1, count($i)); + $this->assertEquals($requests[0], $i[0]); + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::add + */ + public function testIgnoresUnsentRequests() + { + $h = new HistoryPlugin(); + $request = RequestFactory::getInstance()->create('GET', 'http://localhost/'); + $h->add($request); + $this->assertEquals(0, count($h)); + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::add + * @depends testAddsRequests + */ + public function testMaintainsLimit() + { + $h = new HistoryPlugin(); + $h->setLimit(2); + $requests = $this->addRequests($h, 3); + $this->assertEquals(2, count($h)); + $i = 0; + foreach ($h as $request) { + if ($i > 0) { + $this->assertSame($requests[$i], $request); + } + } + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::getLastRequest + */ + public function testReturnsLastRequest() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests), $h->getLastRequest()); + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::getLastResponse + */ + public function testReturnsLastResponse() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertSame(end($requests)->getResponse(), $h->getLastResponse()); + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::clear + */ + public function testClearsHistory() + { + $h = new HistoryPlugin(); + $requests = $this->addRequests($h, 5); + $this->assertEquals(5, count($h)); + $h->clear(); + $this->assertEquals(0, count($h)); + } + + /** + * @covers Guzzle\Http\Plugin\HistoryPlugin::onRequestComplete + * @depends testAddsRequests + */ + public function testUpdatesAddRequests() + { + $h = new HistoryPlugin(); + $client = new Client('http://localhost/'); + $client->getEventDispatcher()->addSubscriber($h); + + $request = $client->get(); + $request->setResponse(new Response(200), true); + $request->send(); + + $this->assertSame($request, $h->getLastRequest()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/LogPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/LogPluginTest.php new file mode 100644 index 0000000..df27ee9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/LogPluginTest.php @@ -0,0 +1,292 @@ +logAdapter = new ClosureLogAdapter( + function($message, $priority, $extras = null) { + echo $message . ' - ' . $priority . ' ' . implode(', ', (array) $extras) . "\n"; + } + ); + + $this->plugin = new LogPlugin($this->logAdapter); + $this->client = new Client($this->getServer()->getUrl()); + $this->client->getEventDispatcher()->addSubscriber($this->plugin); + } + + /** + * Parse a log message into parts + * + * @param string $message Message to parse + * + * @return array + */ + private function parseMessage($message) + { + $p = explode(' - ', $message, 4); + + $parts['host'] = trim($p[0]); + $parts['request'] = str_replace('"', '', $p[1]); + list($parts['code'], $parts['size']) = explode(' ', $p[2]); + list($parts['time'], $parts['up'], $parts['down']) = explode(' ', $p[3]); + $parts['extra'] = isset($p[4]) ? $p[4] : null; + + return $parts; + } + + /** + * @covers Guzzle\Http\Plugin\LogPlugin::__construct + * @covers Guzzle\Http\Plugin\LogPlugin::getLogAdapter + */ + public function testPluginHasLogAdapter() + { + $plugin = new LogPlugin($this->logAdapter); + $this->assertEquals($this->logAdapter, $plugin->getLogAdapter()); + } + + public function testLogsRequestAndResponseContext() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $request = $this->client->get(); + ob_start(); + $request->send(); + $message = ob_get_clean(); + $parts = $this->parseMessage($message); + + $this->assertEquals('127.0.0.1', $parts['host']); + $this->assertEquals('GET / HTTP/1.1', $parts['request']); + $this->assertEquals(200, $parts['code']); + $this->assertEquals(0, $parts['size']); + + $this->assertContains('127.0.0.1 - "GET / HTTP/1.1" - 200 0 - ', $message); + $this->assertContains('7 guzzle.request', $message); + } + + public function testLogsRequestAndResponseWireHeaders() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + + $client = new Client($this->getServer()->getUrl()); + $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_CONTEXT | LogPlugin::LOG_HEADERS); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + + ob_start(); + $request->send(); + $message = ob_get_clean(); + + // Make sure the context was logged + $this->assertContains('127.0.0.1 - "GET / HTTP/1.1" - 200 4 - ', $message); + $this->assertContains('7 guzzle.request', $message); + + // Check that the headers were logged + $this->assertContainsIns("GET / HTTP/1.1\r\n", $message); + $this->assertContainsIns("User-Agent: " . Utils::getDefaultUserAgent(), $message); + $this->assertContainsIns("Accept: */*\r\n", $message); + $this->assertContainsIns("Accept-Encoding: deflate, gzip", $message); + $this->assertContainsIns("Host: 127.0.0.1:", $message); + + // Make sure the response headers are present with a line between the request and response + $this->assertContainsIns("\n< HTTP/1.1 200 OK\r\n< Content-Length: 4", $message); + } + + public function testLogsRequestAndResponseWireContentAndHeaders() + { + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); + + $client = new Client($this->getServer()->getUrl()); + $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_CONTEXT | LogPlugin::LOG_HEADERS | LogPlugin::LOG_BODY); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->put('', null, EntityBody::factory('send')); + + ob_start(); + $request->send(); + $message = ob_get_clean(); + + // Make sure the context was logged + $this->assertContains('127.0.0.1 - "PUT / HTTP/1.1" - 200 4 - ', $message); + + // Check that the headers were logged + $this->assertContains("PUT / HTTP/1.1\r\n", $message); + $this->assertContainsIns("User-Agent: " . Utils::getDefaultUserAgent(), $message); + $this->assertContainsIns("Content-Length: 4", $message); + + // Make sure the response headers are present with a line between the request and response + $this->assertContains("\n< HTTP/1.1 200 OK\r\n< Content-Length: 4", $message); + + // Request payload + $this->assertContains("\r\nsend", $message); + + // Response body + $this->assertContains("data", $message); + + // Ensure that the debug option was set + $this->assertTrue($request->getCurlOptions()->get('debug')); + } + + public function testLogsRequestAndResponseWireContentAndHeadersNonStreamable() + { + $client = new Client($this->getServer()->getUrl()); + $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_CONTEXT | LogPlugin::LOG_HEADERS | LogPlugin::LOG_BODY); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->put(); + + // Send the response from the dummy server as the request body + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nsend"); + $stream = fopen($this->getServer()->getUrl(), 'r'); + $request->setBody(EntityBody::factory($stream, 4)); + + $tmpFile = tempnam('/tmp', 'testLogsRequestAndResponseWireContentAndHeadersNonStreamable'); + $request->setResponseBody(EntityBody::factory(fopen($tmpFile, 'w'))); + + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse"); + ob_start(); + $request->send(); + $message = ob_get_clean(); + + // Make sure the context was logged + $this->assertContains('127.0.0.1 - "PUT / HTTP/1.1" - 200 8 - ', $message); + + // Check that the headers were logged + $this->assertContains("PUT / HTTP/1.1\r\n", $message); + $this->assertContainsIns("User-Agent: " . Utils::getDefaultUserAgent(), $message); + $this->assertContainsIns("Content-Length: 4", $message); + // Request payload + $this->assertContains("\r\nsend", $message); + + // Make sure the response headers are present with a line between the request and response + $this->assertContains("\n< HTTP/1.1 200 OK\r\n< Content-Length: 8", $message); + // Response body + $this->assertContains("\nresponse", $message); + + unlink($tmpFile); + } + + public function testLogsWhenExceptionsAreThrown() + { + $client = new Client($this->getServer()->getUrl()); + $plugin = new LogPlugin($this->logAdapter, LogPlugin::LOG_VERBOSE); + $client->getEventDispatcher()->addSubscriber($plugin); + $request = $client->get(); + + $this->getServer()->enqueue("HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"); + + ob_start(); + try { + $request->send(); + $this->fail('Exception for 404 was not thrown'); + } catch (\Exception $e) {} + $message = ob_get_clean(); + + $this->assertContains('127.0.0.1 - "GET / HTTP/1.1" - 404 0 - ', $message); + $this->assertContains("GET / HTTP/1.1\r\n", $message); + $this->assertContainsIns("User-Agent: " . Utils::getDefaultUserAgent(), $message); + $this->assertContains("\n< HTTP/1.1 404 Not Found\r\n< Content-Length: 0", $message); + } + + /** + * Data provider to test log verbosity + * + * @return array + */ + public function verbosityProvider() + { + $u = str_replace('http://', '', substr($this->getServer()->getUrl(), 0, -1)); + + return array( + array(LogPlugin::LOG_CONTEXT, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), + array(LogPlugin::LOG_CONTEXT | LogPlugin::LOG_DEBUG, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), + array(LogPlugin::LOG_CONTEXT | LogPlugin::LOG_DEBUG | LogPlugin::LOG_HEADERS, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), + array(LogPlugin::LOG_CONTEXT | LogPlugin::LOG_DEBUG | LogPlugin::LOG_HEADERS | LogPlugin::LOG_BODY, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), + array(LogPlugin::LOG_VERBOSE, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), + array(LogPlugin::LOG_HEADERS, "GET /abc HTTP/1.1\r\nHost: $u\r\n\r\n"), + array(LogPlugin::LOG_CONTEXT, "PUT /abc HTTP/1.1\r\nHost: $u\r\nContent-Length: 4\r\n\r\ndata"), + array(LogPlugin::LOG_VERBOSE, "PUT /abc HTTP/1.1\r\nHost: $u\r\nContent-Length: 4\r\n\r\ndata"), + ); + } + + /** + * @dataProvider verbosityProvider + */ + public function testLogsTransactionsAtDifferentLevels($level, $request) + { + $client = new Client(); + $request = RequestFactory::getInstance()->fromMessage($request); + $request->setClient($client); + + $plugin = new LogPlugin(new ClosureLogAdapter( + function($message, $priority, $extras = null) { + echo $message . "\n"; + } + ), $level); + $request->getEventDispatcher()->addSubscriber($plugin); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\nresp"); + + ob_start(); + $request->send(); + $gen = ob_get_clean(); + + $parts = explode("\n", trim($gen), 2); + + // Check if the context was properly logged + if ($level & LogPlugin::LOG_CONTEXT) { + $this->assertContains('127.0.0.1 - "' . $request->getMethod() . ' /', $gen); + } + + // Check if the line count is 1 when just logging the context + if ($level == LogPlugin::LOG_CONTEXT) { + $this->assertEquals(1, count($parts)); + return; + } + + // Check if debug information is being logged + if ($level & LogPlugin::LOG_DEBUG) { + $this->assertContains("\n* Connected to ", $gen); + } + + // Check if the headers are being properly logged + if ($level & LogPlugin::LOG_HEADERS) { + $this->assertContains("> " . $request->getMethod() . " /abc HTTP/1.1", $gen); + // Ensure headers following the request line are logged + $this->assertContains("Accept: */*", $gen); + // Ensure the response headers are being logged + $this->assertContains("< HTTP/1.1 200 OK\r\n< Content-Length: 4", $gen); + } + + // Check if the body of the request and response are being logged + if ($level & LogPlugin::LOG_BODY) { + if ($request instanceof EntityEnclosingRequest) { + $this->assertContains("\r\n\r\n" . $request->getBody(true), $gen); + } + $this->assertContains("\nresp", $gen); + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/Md5ValidatorPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/Md5ValidatorPluginTest.php new file mode 100644 index 0000000..5b44ca7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/Md5ValidatorPluginTest.php @@ -0,0 +1,127 @@ +create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $body = 'abc'; + $hash = md5($body); + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Length' => 3 + ), 'abc'); + + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with no Content-MD5 + $response->removeHeader('Content-MD5'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } + + /** + * @covers Guzzle\Http\Plugin\Md5ValidatorPlugin + * @expectedException UnexpectedValueException + */ + public function testThrowsExceptionOnInvalidMd5() + { + $plugin = new Md5ValidatorPlugin(); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + /** + * @covers Guzzle\Http\Plugin\Md5ValidatorPlugin + */ + public function testSkipsWhenContentLengthIsTooLarge() + { + $plugin = new Md5ValidatorPlugin(false, 1); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + $request->dispatch('request.complete', array( + 'response' => new Response(200, array( + 'Content-MD5' => 'foobar', + 'Content-Length' => 3 + ), 'abc') + )); + } + + /** + * @covers Guzzle\Http\Plugin\Md5ValidatorPlugin + */ + public function testProperlyValidatesWhenUsingContentEncoding() + { + $plugin = new Md5ValidatorPlugin(true); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + $request->getEventDispatcher()->addSubscriber($plugin); + + // Content-MD5 is the MD5 hash of the canonical content after all + // content-encoding has been applied. Because cURL will automatically + // decompress entity bodies, we need to re-compress it to calculate. + $body = EntityBody::factory('abc'); + $body->compress(); + $hash = $body->getContentMd5(); + $body->uncompress(); + + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'gzip' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + $this->assertEquals('abc', $response->getBody(true)); + + // Try again with an unknown encoding + $response = new Response(200, array( + 'Content-MD5' => $hash, + 'Content-Encoding' => 'foobar' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with compress + $body->compress('bzip2.compress'); + $response = new Response(200, array( + 'Content-MD5' => $body->getContentMd5(), + 'Content-Encoding' => 'compress' + ), 'abc'); + $request->dispatch('request.complete', array( + 'response' => $response + )); + + // Try again with encoding and disabled content-encoding checks + $request->getEventDispatcher()->removeSubscriber($plugin); + $plugin = new Md5ValidatorPlugin(false); + $request->getEventDispatcher()->addSubscriber($plugin); + $request->dispatch('request.complete', array( + 'response' => $response + )); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/MockPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/MockPluginTest.php new file mode 100644 index 0000000..631099a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/MockPluginTest.php @@ -0,0 +1,205 @@ +assertInternalType('array', MockPlugin::getSubscribedEvents()); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::getAllEvents + */ + public function testDescribesEvents() + { + $this->assertInternalType('array', MockPlugin::getAllEvents()); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::__construct + * @covers Guzzle\Http\Plugin\MockPlugin::isTemporary + */ + public function testCanBeTemporary() + { + $plugin = new MockPlugin(); + $this->assertFalse($plugin->isTemporary()); + $plugin = new MockPlugin(null, true); + $this->assertTrue($plugin->isTemporary()); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::count + */ + public function testIsCountable() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $this->assertEquals(1, count($plugin)); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::clearQueue + * @depends testIsCountable + */ + public function testCanClearQueue() + { + $plugin = new MockPlugin(); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $plugin->clearQueue(); + $this->assertEquals(0, count($plugin)); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::getQueue + */ + public function testCanInspectQueue() + { + $plugin = new MockPlugin(); + $this->assertInternalType('array', $plugin->getQueue()); + $plugin->addResponse(Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")); + $queue = $plugin->getQueue(); + $this->assertInternalType('array', $queue); + $this->assertEquals(1, count($queue)); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::getMockFile + */ + public function testRetrievesResponsesFromFiles() + { + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response); + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::getMockFile + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenResponseFileIsNotFound() + { + MockPlugin::getMockFile('missing/filename'); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::addResponse + * @expectedException InvalidArgumentException + */ + public function testInvalidResponsesThrowAnException() + { + $p = new MockPlugin(); + $p->addResponse($this); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::addResponse + */ + public function testAddsResponseObjectsToQueue() + { + $p = new MockPlugin(); + $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + $p->addResponse($response); + $this->assertEquals(array($response), $p->getQueue()); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::addResponse + */ + public function testAddsResponseFilesToQueue() + { + $p = new MockPlugin(); + $p->addResponse(__DIR__ . '/../../TestData/mock_response'); + $this->assertEquals(1, count($p)); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::onRequestCreate + * @covers Guzzle\Http\Plugin\MockPlugin::addResponse + * @covers Guzzle\Http\Plugin\MockPlugin::dequeue + * @depends testAddsResponseFilesToQueue + */ + public function testAddsMockResponseToRequestFromClient() + { + $p = new MockPlugin(); + $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); + $p->addResponse($response); + + $client = new Client('http://localhost:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertSame($response, $request->getResponse()); + $this->assertEquals(0, count($p)); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::onRequestCreate + * @depends testAddsResponseFilesToQueue + */ + public function testUpdateIgnoresWhenEmpty() + { + $p = new MockPlugin(); + $p->onRequestCreate(new Event()); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::onRequestCreate + * @covers Guzzle\Http\Plugin\MockPlugin::dequeue + * @depends testAddsMockResponseToRequestFromClient + */ + public function testDetachesTemporaryWhenEmpty() + { + $p = new MockPlugin(null, true); + $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response')); + $client = new Client('http://localhost:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + $request = $client->get(); + $request->send(); + + $this->assertFalse($this->hasSubscriber($client, $p)); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::__construct + */ + public function testLoadsResponsesFromConstructor() + { + $p = new MockPlugin(array(new Response(200))); + $this->assertEquals(1, $p->count()); + } + + /** + * @covers Guzzle\Http\Plugin\MockPlugin::getReceivedRequests + * @covers Guzzle\Http\Plugin\MockPlugin::flush + */ + public function testStoresMockedRequests() + { + $p = new MockPlugin(array(new Response(200), new Response(200))); + $client = new Client('http://localhost:123/'); + $client->getEventDispatcher()->addSubscriber($p, 9999); + + $request1 = $client->get(); + $request1->send(); + $this->assertEquals(array($request1), $p->getReceivedRequests()); + + $request2 = $client->get(); + $request2->send(); + $this->assertEquals(array($request1, $request2), $p->getReceivedRequests()); + + $p->flush(); + $this->assertEquals(array(), $p->getReceivedRequests()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/OauthPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/OauthPluginTest.php new file mode 100644 index 0000000..a9c6d8f --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Plugin/OauthPluginTest.php @@ -0,0 +1,171 @@ + 'foo', + 'consumer_secret' => 'bar', + 'token' => 'count', + 'token_secret' => 'dracula' + ); + + protected function getRequest() + { + return RequestFactory::getInstance()->create('POST', 'http://www.test.com/path?a=b&c=d', null, array( + 'e' => 'f' + )); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::getSubscribedEvents + */ + public function testSubscribesToEvents() + { + $events = OauthPlugin::getSubscribedEvents(); + $this->assertArrayHasKey('request.before_send', $events); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::__construct + */ + public function testAcceptsConfigurationData() + { + $p = new OauthPlugin($this->config); + + // Access the config object + $class = new \ReflectionClass($p); + $property = $class->getProperty('config'); + $property->setAccessible(true); + $config = $property->getValue($p); + + $this->assertEquals('foo', $config['consumer_key']); + $this->assertEquals('bar', $config['consumer_secret']); + $this->assertEquals('count', $config['token']); + $this->assertEquals('dracula', $config['token_secret']); + $this->assertEquals('1.0', $config['version']); + $this->assertEquals('HMAC-SHA1', $config['signature_method']); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::getStringToSign + */ + public function testCreatesStringToSignFromPostRequest() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $this->assertEquals( + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D22c3b010c30c17043c3d2dd3a7aa3ae6c5549b32%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0', + $p->getStringToSign($request, self::TIMESTAMP) + ); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::getStringToSign + */ + public function testCreatesStringToSignFromPostRequestWithCustomContentType() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->setHeader('Content-Type', 'Foo'); + $this->assertEquals( + // Method and URL + 'POST&http%3A%2F%2Fwww.test.com%2Fpath' . + // Sorted parameters from query string and body + '&a%3Db%26c%3Dd%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D22c3b010c30c17043c3d2dd3a7aa3ae6c5549b32%26' . + 'oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0', + $p->getStringToSign($request, self::TIMESTAMP) + ); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::getStringToSign + * @depends testCreatesStringToSignFromPostRequest + */ + public function testConvertsBooleansToStrings() + { + $p = new OauthPlugin($this->config); + $request = $this->getRequest(); + $request->getQuery()->set('a', true); + $request->getQuery()->set('c', false); + $this->assertContains('&a%3Dtrue%26c%3Dfalse', $p->getStringToSign($request, self::TIMESTAMP)); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::getSignature + * @depends testCreatesStringToSignFromPostRequest + */ + public function testSignsStrings() + { + $p = new OauthPlugin(array_merge($this->config, array( + 'signature_callback' => function($string, $key) { + return "_{$string}|{$key}_"; + } + ))); + $request = $this->getRequest(); + $sig = $p->getSignature($request, self::TIMESTAMP); + $this->assertEquals( + '_POST&http%3A%2F%2Fwww.test.com%2Fpath&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . + '%26oauth_nonce%3D22c3b010c30c17043c3d2dd3a7aa3ae6c5549b32%26oauth_signature_method%3DHMAC-SHA1' . + '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0|' . + 'bar&dracula_', + base64_decode($sig) + ); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::onRequestBeforeSend + * @covers Guzzle\Http\Plugin\OauthPlugin::__construct + */ + public function testSignsOauthRequests() + { + $p = new OauthPlugin($this->config); + $event = new Event(array( + 'request' => $this->getRequest(), + 'timestamp' => self::TIMESTAMP + )); + $p->onRequestBeforeSend($event); + + $this->assertTrue($event['request']->hasHeader('Authorization')); + $this->assertEquals('OAuth oauth_consumer_key="foo", ' + . 'oauth_nonce="22c3b010c30c17043c3d2dd3a7aa3ae6c5549b32", ' + . 'oauth_signature="BqUAsVHc1cYJ3FA9%2BtLMkJnizJk%3D", ' + . 'oauth_signature_method="HMAC-SHA1", ' + . 'oauth_timestamp="' . self::TIMESTAMP . '", ' + . 'oauth_token="count", ' + . 'oauth_version="1.0"', + (string) $event['request']->getHeader('Authorization') + ); + } + + /** + * @covers Guzzle\Http\Plugin\OauthPlugin::generateNonce + */ + public function testGeneratesUniqueNonce() + { + $p = new OauthPlugin($this->config); + $method = new \ReflectionMethod('Guzzle\Http\Plugin\OauthPlugin', 'generateNonce'); + $method->setAccessible(true); + $request = RequestFactory::getInstance()->create('GET', 'http://www.example.com'); + $result = $method->invoke($p, $request, 1335936584); + $this->assertEquals('29f72fa5fc2893972060b28a0df8623c41cbb5d2', $result); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php new file mode 100644 index 0000000..1664cc3 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php @@ -0,0 +1,254 @@ +q = new QueryString(); + } + + /** + * @covers \Guzzle\Http\QueryString::getFieldSeparator + */ + public function testGetFieldSeparator() + { + $this->assertEquals('&', $this->q->getFieldSeparator()); + } + + /** + * @covers \Guzzle\Http\QueryString::getPrefix + */ + public function testGetPrefix() + { + $this->assertEquals('?', $this->q->getPrefix()); + } + + /** + * @covers \Guzzle\Http\QueryString::getValueSeparator + */ + public function testGetValueSeparator() + { + $this->assertEquals('=', $this->q->getValueSeparator()); + } + + /** + * @covers \Guzzle\Http\QueryString::isEncodingFields + * @covers \Guzzle\Http\QueryString::setEncodeFields + */ + public function testIsEncodingFields() + { + $this->assertTrue($this->q->isEncodingFields()); + $this->assertEquals($this->q, $this->q->setEncodeFields(false)); + $this->assertFalse($this->q->isEncodingFields()); + } + + /** + * @covers \Guzzle\Http\QueryString::isEncodingValues + * @covers \Guzzle\Http\QueryString::setEncodeValues + */ + public function testIsEncodingValues() + { + $this->assertTrue($this->q->isEncodingValues()); + $this->assertEquals($this->q, $this->q->setEncodeValues(false)); + $this->assertFalse($this->q->isEncodingValues()); + } + + /** + * @covers \Guzzle\Http\QueryString::setFieldSeparator + * @covers \Guzzle\Http\QueryString::setFieldSeparator + */ + public function testSetFieldSeparator() + { + $this->assertEquals($this->q, $this->q->setFieldSeparator('/')); + $this->assertEquals('/', $this->q->getFieldSeparator()); + } + + /** + * @covers \Guzzle\Http\QueryString::setPrefix + * @covers \Guzzle\Http\QueryString::getPrefix + */ + public function testSetPrefix() + { + $this->assertEquals($this->q, $this->q->setPrefix('')); + $this->assertEquals('', $this->q->getPrefix()); + } + + /** + * @covers \Guzzle\Http\QueryString::setValueSeparator + * @covers \Guzzle\Http\QueryString::getValueSeparator + */ + public function testSetValueSeparator() + { + $this->assertEquals($this->q, $this->q->setValueSeparator('/')); + $this->assertEquals('/', $this->q->getValueSeparator()); + } + + /** + * @covers \Guzzle\Http\QueryString::urlEncode + * @covers \Guzzle\Http\QueryString::encodeData + * @covers \Guzzle\Http\QueryString::replace + */ + public function testUrlEncode() + { + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array('v1', 'v2', 'v3'), + 'ሴ' => 'bar' + ); + $encoded = array( + 'test' => 'value', + 'test%202' => rawurlencode('this is a test?'), + 'test3%5B0%5D' => 'v1', + 'test3%5B1%5D' => 'v2', + 'test3%5B2%5D' => 'v3', + '%E1%88%B4' => 'bar' + ); + $this->q->replace($params); + $this->assertEquals($encoded, $this->q->urlEncode()); + + // Disable field encoding + $testData = array( + 'test 2' => 'this is a test' + ); + $this->q->replace($testData); + $this->q->setEncodeFields(false); + $this->assertEquals(array( + 'test 2' => rawurlencode('this is a test') + ), $this->q->urlEncode()); + + // Disable encoding of both fields and values + $this->q->setEncodeValues(false); + $this->assertEquals($testData, $this->q->urlEncode()); + } + + /** + * @covers \Guzzle\Http\QueryString + * @covers \Guzzle\Http\QueryString::__toString + * @covers \Guzzle\Http\QueryString::setEncodeFields + * @covers \Guzzle\Http\QueryString::replace + * @covers \Guzzle\Http\QueryString::setAggregateFunction + * @covers \Guzzle\Http\QueryString::encodeData + */ + public function testToString() + { + // Check with no parameters + $this->assertEquals('', $this->q->__toString()); + + $params = array( + 'test' => 'value', + 'test 2' => 'this is a test?', + 'test3' => array( + 'v1', + 'v2', + 'v3' + ) + ); + $this->q->replace($params); + $this->assertEquals('?test=value&test%202=this%20is%20a%20test%3F&test3%5B0%5D=v1&test3%5B1%5D=v2&test3%5B2%5D=v3', $this->q->__toString()); + $this->q->setEncodeFields(false); + $this->q->setEncodeValues(false); + $this->assertEquals('?test=value&test 2=this is a test?&test3[0]=v1&test3[1]=v2&test3[2]=v3', $this->q->__toString()); + + // Use an alternative aggregator + $this->q->setAggregateFunction(array($this->q, 'aggregateUsingComma')); + $this->assertEquals('?test=value&test 2=this is a test?&test3=v1,v2,v3', $this->q->__toString()); + } + + /** + * @covers \Guzzle\Http\QueryString::__toString + * @covers \Guzzle\Http\QueryString::aggregateUsingDuplicates + */ + public function testAllowsMultipleValuesPerKey() + { + $q = new QueryString(); + $q->add('facet', 'size'); + $q->add('facet', 'width'); + $q->add('facet.field', 'foo'); + // Use the duplicate aggregator + $q->setAggregateFunction(array($this->q, 'aggregateUsingDuplicates')); + $this->assertEquals('?facet=size&facet=width&facet.field=foo', $q->__toString()); + } + + /** + * @covers \Guzzle\Http\QueryString::__toString + * @covers \Guzzle\Http\QueryString::encodeData + * @covers \Guzzle\Http\QueryString::aggregateUsingPhp + */ + public function testAllowsNestedQueryData() + { + $this->q->replace(array( + 'test' => 'value', + 't' => array( + 'v1' => 'a', + 'v2' => 'b', + 'v3' => array( + 'v4' => 'c', + 'v5' => 'd', + ) + ) + )); + + $this->q->setEncodeFields(false); + $this->q->setEncodeValues(false); + $this->assertEquals('?test=value&t[v1]=a&t[v2]=b&t[v3][v4]=c&t[v3][v5]=d', $this->q->__toString()); + } + + public function parseQueryProvider() + { + return array( + // Ensure that multiple query string values are allowed per value + array('q=a&q=b', array( + 'q' => array('a', 'b') + )), + // Ensure that PHP array style query string values are parsed + array('q[]=a&q[]=b', array( + 'q' => array('a', 'b') + )), + // Ensure that decimals are allowed in query strings + array('q.a=a&q.b=b', array( + 'q.a' => 'a', + 'q.b' => 'b' + )), + // Ensure that query string values are percent decoded + array('q%20a=a%20b', array( + 'q a' => 'a b' + )), + // Ensure that values can be set without have a value + array('q', array( + 'q' => null + )), + ); + } + + /** + * @covers Guzzle\Http\QueryString::fromString + * @dataProvider parseQueryProvider + */ + public function testParsesQueryStrings($query, $data) + { + $query = QueryString::fromString($query); + $this->assertEquals($data, $query->getAll()); + } + + /** + * @covers Guzzle\Http\QueryString::fromString + */ + public function testProperlyDealsWithDuplicateQueryStringValues() + { + $query = QueryString::fromString('foo=a&foo=b&?µ=c'); + $this->assertEquals(array('a', 'b'), $query->get('foo')); + $this->assertEquals('c', $query->get('?µ')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php new file mode 100644 index 0000000..1ae3273 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/Server.php @@ -0,0 +1,233 @@ +port = $port ?: self::DEFAULT_PORT; + $this->client = new Client($this->getUrl()); + } + + /** + * Destructor to safely shutdown the node.js server if it is still running + */ + public function __destruct() + { + // Disabled for now + if (false && $this->running) { + try { + $this->stop(); + } catch (\Exception $e) {} + } + } + + /** + * Flush the received requests from the server + * + * @return bool Returns TRUE on success or FALSE on failure + * @throws RuntimeException + */ + public function flush() + { + if (!$this->isRunning()) { + return false; + } + + return $this->client->delete('guzzle-server/requests') + ->send()->getStatusCode() == 200; + } + + /** + * Queue an array of responses or a single response on the server. + * + * Any currently queued responses will be overwritten. Subsequent requests + * on the server will return queued responses in FIFO order. + * + * @param array|Response $responses A single or array of Responses to queue + * + * @return bool Returns TRUE on success or FALSE on failure + * @throws BadResponseException + */ + public function enqueue($responses) + { + $data = array(); + foreach ((array) $responses as $response) { + + // Create the response object from a string + if (is_string($response)) { + $response = Response::fromMessage($response); + } elseif (!($response instanceof Response)) { + throw new BadResponseException( + 'Responses must be strings or implement Response' + ); + } + + $data[] = array( + 'statusCode' => $response->getStatusCode(), + 'reasonPhrase' => $response->getReasonPhrase(), + 'headers' => $response->getHeaders()->getAll(), + 'body' => $response->getBody(true) + ); + } + + $request = $this->client->put('guzzle-server/responses', null, json_encode($data)); + $request->removeHeader('Expect'); + $response = $request->send(); + + return $response->getStatusCode() == 200; + } + + /** + * Check if the server is running + * + * @return bool + */ + public function isRunning() + { + if ($this->running) { + return true; + } else { + $fp = @fsockopen('127.0.0.1', $this->port, $errno, $errstr, 1); + if (!$fp) { + return false; + } else { + fclose($fp); + return true; + } + } + } + + /** + * Get the URL to the server + * + * @return string + */ + public function getUrl() + { + return 'http://127.0.0.1:' . $this->getPort() . '/'; + } + + /** + * Get the port that the server is listening on + * + * @return int + */ + public function getPort() + { + return $this->port; + } + + /** + * Get all of the received requests + * + * @param bool $hydrate Set to TRUE to turn the messages into + * actual {@see RequestInterface} objects. If $hydrate is FALSE, + * requests will be returned as strings. + * + * @return array + * @throws RuntimeException + */ + public function getReceivedRequests($hydrate = false) + { + $data = array(); + + if ($this->isRunning()) { + $response = $this->client->get('guzzle-server/requests')->send(); + $data = array_filter(explode(self::REQUEST_DELIMITER, $response->getBody(true))); + if ($hydrate) { + $data = array_map(function($message) { + return RequestFactory::getInstance()->fromMessage($message); + }, $data); + } + } + + return $data; + } + + /** + * Start running the node.js server in the background + */ + public function start() + { + if (!$this->isRunning()) { + exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js ' . $this->port . ' >> /tmp/server.log 2>&1 &'); + // Wait at most 5 seconds for the server the setup before proceeding + $start = time(); + while (!$this->isRunning() && time() - $start < 5); + if (!$this->isRunning()) { + throw new RuntimeException( + 'Unable to contact server.js. Have you installed node.js ' + . 'v0.5.0+? The node.js executable, node, must also be in ' + . 'your path.' + ); + } + } + + $this->running = true; + } + + /** + * Stop running the node.js server + * + * @return bool Returns TRUE on success or FALSE on failure + * @throws RuntimeException + */ + public function stop() + { + if (!$this->isRunning()) { + return false; + } + + $this->running = false; + + return $this->client->delete('guzzle-server')->send() + ->getStatusCode() == 200; + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php new file mode 100644 index 0000000..f5e1a6b --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UrlTest.php @@ -0,0 +1,234 @@ +assertEquals(80, Url::factory('http://www.test.com/')->getPort()); + $this->assertEquals(443, Url::factory('https://www.test.com/')->getPort()); + $this->assertEquals(null, Url::factory('ftp://www.test.com/')->getPort()); + $this->assertEquals(8192, Url::factory('http://www.test.com:8192/')->getPort()); + } + + /** + * @covers Guzzle\Http\Url::__clone + */ + public function testCloneCreatesNewInternalObjects() + { + $u1 = Url::factory('http://www.test.com/'); + $u2 = clone $u1; + $this->assertNotSame($u1->getQuery(), $u2->getQuery()); + } + + /** + * @covers Guzzle\Http\Url::__construct + * @covers Guzzle\Http\Url::factory + * @covers Guzzle\Http\Url::__toString + * @covers Guzzle\Http\Url::isAbsolute + */ + public function testValidatesUrlPartsInFactory() + { + $url = Url::factory('/index.php'); + $this->assertEquals('/index.php', (string) $url); + $this->assertFalse($url->isAbsolute()); + + $url = 'http://michael:test@test.com:80/path/123?q=abc#test'; + $u = Url::factory($url); + $this->assertEquals('http://michael:test@test.com/path/123?q=abc#test', (string) $u); + $this->assertTrue($u->isAbsolute()); + } + + /** + * @covers Guzzle\Http\Url + */ + public function testUrlStoresParts() + { + $url = Url::factory('http://test:pass@www.test.com:8081/path/path2/?a=1&b=2#fragment'); + $this->assertEquals('http', $url->getScheme()); + $this->assertEquals('test', $url->getUsername()); + $this->assertEquals('pass', $url->getPassword()); + $this->assertEquals('www.test.com', $url->getHost()); + $this->assertEquals(8081, $url->getPort()); + $this->assertEquals('/path/path2/', $url->getPath()); + $this->assertEquals('fragment', $url->getFragment()); + $this->assertEquals('?a=1&b=2', (string) $url->getQuery()); + + $this->assertEquals(array( + 'fragment' => 'fragment', + 'host' => 'www.test.com', + 'pass' => 'pass', + 'path' => '/path/path2/', + 'port' => 8081, + 'query' => '?a=1&b=2', + 'query_prefix' => '?', + 'scheme' => 'http', + 'user' => 'test' + ), $url->getParts()); + } + + /** + * @covers Guzzle\Http\Url::setPath + * @covers Guzzle\Http\Url::getPath + * @covers Guzzle\Http\Url::getPathSegments + * @covers Guzzle\Http\Url::buildUrl + */ + public function testHandlesPathsCorrectly() + { + $url = Url::factory('http://www.test.com'); + $this->assertEquals('/', $url->getPath()); + $url->setPath('test'); + $this->assertEquals('/test', $url->getPath()); + + $url->setPath('/test/123/abc'); + $this->assertEquals(array('test', '123', 'abc'), $url->getPathSegments()); + + $parts = parse_url('http://www.test.com/test'); + $parts['path'] = ''; + $this->assertEquals('http://www.test.com/', Url::buildUrl($parts)); + $parts['path'] = 'test'; + $this->assertEquals('http://www.test.com/test', Url::buildUrl($parts)); + } + + /** + * @covers Guzzle\Http\Url::buildUrl + */ + public function testAddsQueryStringIfPresent() + { + $this->assertEquals('/?foo=bar', Url::buildUrl(array( + 'query' => 'foo=bar' + ))); + } + + /** + * @covers Guzzle\Http\Url::addPath + */ + public function testAddsToPath() + { + // Does nothing here + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(false)); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('')); + $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/')); + + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('relative')); + $this->assertEquals('http://e.com/base/relative?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath('/relative')); + } + + /** + * URL combination data provider + * + * @return array + */ + public function urlCombineDataProvider() + { + return array( + array('http://www.example.com/', 'http://www.example.com/', 'http://www.example.com/'), + array('http://www.example.com/path', '/absolute', 'http://www.example.com/absolute'), + array('http://www.example.com/path', '/absolute?q=2', 'http://www.example.com/absolute?q=2'), + array('http://www.example.com/path', 'more', 'http://www.example.com/path/more'), + array('http://www.example.com/path', 'more?q=1', 'http://www.example.com/path/more?q=1'), + array('http://www.example.com/', '?q=1', 'http://www.example.com/?q=1'), + array('http://www.example.com/path', 'http://test.com', 'http://test.com/path'), + array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com/path'), + array('http://www.example.com:8080/path', '?q=2#abc', 'http://www.example.com:8080/path?q=2#abc'), + array('http://u:a@www.example.com/path', 'test', 'http://u:a@www.example.com/path/test'), + array('http://www.example.com/path', 'http://u:a@www.example.com/', 'http://u:a@www.example.com/path'), + array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'), + ); + } + + /** + * @covers Guzzle\Http\Url::combine + * @dataProvider urlCombineDataProvider + */ + public function testCombinesUrls($a, $b, $c) + { + $this->assertEquals($c, (string) Url::factory($a)->combine($b)); + } + + /** + * @covers Guzzle\Http\Url + */ + public function testHasGettersAndSetters() + { + $url = Url::factory('http://www.test.com/'); + $this->assertEquals('example.com', $url->setHost('example.com')->getHost()); + $this->assertEquals('8080', $url->setPort(8080)->getPort()); + $this->assertEquals('/foo/bar', $url->setPath(array('foo', 'bar'))->getPath()); + $this->assertEquals('a', $url->setPassword('a')->getPassword()); + $this->assertEquals('b', $url->setUsername('b')->getUsername()); + $this->assertEquals('abc', $url->setFragment('abc')->getFragment()); + $this->assertEquals('https', $url->setScheme('https')->getScheme()); + $this->assertEquals('?a=123', (string) $url->setQuery('a=123')->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?a=123#abc', (string)$url); + $this->assertEquals('?b=boo', (string) $url->setQuery(new QueryString(array( + 'b' => 'boo' + )))->getQuery()); + $this->assertEquals('https://b:a@example.com:8080/foo/bar?b=boo#abc', (string)$url); + } + + /** + * @covers Guzzle\Http\Url::setQuery + */ + public function testSetQueryAcceptsArray() + { + $url = Url::factory('http://www.test.com'); + $url->setQuery(array('a' => 'b')); + $this->assertEquals('http://www.test.com/?a=b', (string) $url); + } + + function urlProvider() + { + return array( + array('/foo/..', '/'), + array('//foo//..', '/'), + array('/foo/../..', '/'), + array('/foo/../.', '/'), + array('/./foo/..', '/'), + array('/./foo', '/foo'), + array('/./foo/', '/foo/'), + array('/./foo/bar/baz/pho/../..', '/foo/bar'), + array('*', '*') + ); + } + + /** + * @covers Guzzle\Http\Url::normalizePath + * @dataProvider urlProvider + */ + public function testNormalizesPaths($path, $result) + { + $url = Url::factory('http://www.example.com/'); + $url->setPath($path)->normalizePath(); + $this->assertEquals($result, $url->getPath()); + } + + /** + * @covers Guzzle\Http\Url::setHost + */ + public function testSettingHostWithPortModifiesPort() + { + $url = Url::factory('http://www.example.com'); + $url->setHost('foo:8983'); + $this->assertEquals('foo', $url->getHost()); + $this->assertEquals(8983, $url->getPort()); + } + + /** + * @covers Guzzle\Http\Url::buildUrl + */ + public function testUrlOnlyContainsHashWhenHashIsNotEmpty() + { + $url = Url::factory('http://www.example.com/'); + $url->setFragment(''); + $this->assertEquals('http://www.example.com/', (string) $url); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UtilsTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UtilsTest.php new file mode 100644 index 0000000..ab9af08 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/UtilsTest.php @@ -0,0 +1,38 @@ +assertEquals(gmdate($fmt), Utils::getHttpDate('now')); + $this->assertEquals(gmdate($fmt), Utils::getHttpDate(strtotime('now'))); + $this->assertEquals(gmdate($fmt, strtotime('+1 day')), Utils::getHttpDate('+1 day')); + } + + /** + * @covers Guzzle\Http\Utils::getDefaultUserAgent + */ + public function testGetDefaultUserAgent() + { + // Clear out the user agent cache + $refObject = new \ReflectionClass('Guzzle\Http\Utils'); + $refProperty = $refObject->getProperty('userAgent'); + $refProperty->setAccessible(true); + $refProperty->setValue(null, null); + + $version = curl_version(); + $agent = sprintf('Guzzle/%s curl/%s PHP/%s openssl/%s', Version::VERSION, $version['version'], PHP_VERSION, $version['ssl_version']); + $this->assertEquals($agent, Utils::getDefaultUserAgent()); + // Get it from cache this time + $this->assertEquals($agent, Utils::getDefaultUserAgent()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js new file mode 100644 index 0000000..eb05778 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Http/server.js @@ -0,0 +1,164 @@ +/** + * Guzzle node.js test server to return queued responses to HTTP requests and + * expose a RESTful API for enqueueing responses and retrieving the requests + * that have been received. + * + * - Delete all requests that have been received: + * DELETE /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Enqueue responses + * PUT /guzzle-server/responses + * Host: 127.0.0.1:8124 + * + * [{ "statusCode": 200, "reasonPhrase": "OK", "headers": {}, "body": "" }] + * + * - Get the received requests + * GET /guzzle-server/requests + * Host: 127.0.0.1:8124 + * + * - Shutdown the server + * DELETE /guzzle-server + * Host: 127.0.0.1:8124 + * + * @package Guzzle PHP + * @license See the LICENSE file that was distributed with this source code. + */ + +var http = require("http"); + +/** + * Guzzle node.js server + * @class + */ +var GuzzleServer = function(port) { + + this.port = port; + this.log = log; + this.responses = []; + this.requests = []; + var that = this; + + /** + * Handle a Guzzle Server control request + * @param (String) request HTTP request as a string + * @param (ServerRequest) req Received server request + * @param (ServerResponse) res Outgoing server response + */ + var controlRequest = function(request, req, res) { + if (req.method == "DELETE") { + if (req.url == "/guzzle-server/requests") { + // Clear the received requests + that.requests = []; + res.writeHead(200, "OK", { "Content-Length": 0 }); + res.end(); + if (this.log) { + console.log("Flushing requests"); + } + } else if (req.url == "/guzzle-server") { + // Shutdown the server + res.writeHead(200, "OK", { "Content-Length": 0, "Connection": "close" }); + res.end(); + if (this.log) { + console.log("Shutting down"); + } + that.server.close(); + } + } else if (req.method == "GET") { + if (req.url === "/guzzle-server/requests") { + // Get received requests + var data = that.requests.join("\n----[request]\n"); + res.writeHead(200, "OK", { "Content-Length": data.length }); + res.end(data); + if (that.log) { + console.log("Sending receiving requests"); + } + } + } else if (req.method == "PUT") { + if (req.url == "/guzzle-server/responses") { + if (that.log) { + console.log("Adding responses..."); + } + // Received response to queue + var data = request.split("\r\n\r\n")[1]; + if (!data) { + if (that.log) { + console.log("No response data was provided"); + } + res.writeHead(500, "NO RESPONSES IN REQUEST", { "Content-Length": 0 }); + } else { + that.responses = eval("(" + data + ")"); + if (that.log) { + console.log(that.responses); + } + res.writeHead(200, "OK", { "Content-Length": 0 }); + } + res.end(); + } + } + }; + + /** + * Received a complete request + * @param (String) request HTTP request as a string + * @param (ServerRequest) req Received server request + * @param (ServerResponse) res Outgoing server response + */ + var receivedRequest = function(request, req, res) { + if (req.url.indexOf("/guzzle-server") === 0) { + controlRequest(request, req, res); + } else { + var response = that.responses.shift(); + res.writeHead(response.statusCode, response.reasonPhrase, response.headers); + res.end(response.body); + that.requests.push(request); + } + }; + + /** + * Start the node.js Guzzle server + */ + this.start = function() { + + that.server = http.createServer(function(req, res) { + + // If this is not a control request and no responses are in queue, return 500 response + if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) { + res.writeHead(500); + res.end("No responses in queue"); + return; + } + + // Begin building the request message as a string + var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n"; + // Add the request headers + for (var i in req.headers) { + request += i + ": " + req.headers[i] + "\r\n"; + } + request += "\r\n"; + + // Receive each chunk of the request body + req.addListener("data", function(chunk) { + request += chunk; + }); + + // Called when the request completes + req.addListener("end", function() { + receivedRequest(request, req, res); + }); + }); + that.server.listen(port, "127.0.0.1"); + + if (this.log) { + console.log("Server running at http://127.0.0.1:8124/"); + } + }; +}; + +// Get the port from the arguments +port = process.argv.length >= 3 ? process.argv[2] : 8124; +log = process.argv.length >= 4 ? process.argv[3] : false; + +// Start the server +server = new GuzzleServer(port, log); +server.start(); diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php new file mode 100644 index 0000000..97a1974 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php @@ -0,0 +1,11 @@ +multiHandle; + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php new file mode 100644 index 0000000..c4365ee --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockObserver.php @@ -0,0 +1,70 @@ +events as $event) { + if ($event->getName() == $eventName) { + return true; + } + } + + return false; + } + + public function getLastEvent() + { + return end($this->events); + } + + public function count() + { + return count($this->events); + } + + public function getGrouped() + { + $events = array(); + foreach ($this->events as $event) { + if (!isset($events[$event->getName()])) { + $events[$event->getName()] = array(); + } + $events[$event->getName()][] = $event; + } + + return $events; + } + + public function getData($event, $key, $occurrence = 0) + { + $grouped = $this->getGrouped(); + if (isset($grouped[$event])) { + return $grouped[$event][$occurrence][$key]; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function update(Event $event) + { + $this->events[] = $event; + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php new file mode 100644 index 0000000..e011959 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Mock/MockSubject.php @@ -0,0 +1,7 @@ + 'allseeing-i.com', + 'path' => '/', + 'data' => array( + 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c' + ), + 'max_age' => NULL, + 'expires' => 'Sat, 26-Jul-2008 17:00:42 GMT', + 'version' => NULL, + 'secure' => NULL, + 'discard' => NULL, + 'port' => NULL, + 'cookies' => array( + 'ASIHTTPRequestTestCookie' => 'This is the value' + ), + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array('', false), + array('foo', false), + // Test setting a blank value for a cookie + array(array( + 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '), + array( + 'cookies' => array( + 'foo' => '' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting a value and removing quotes + array(array( + 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;', 'foo="1"', 'foo="1";', 'foo= "1";'), + array( + 'cookies' => array( + 'foo' => '1' + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Test setting multiple values + array(array( + 'foo=1; bar=2;', 'foo =1; bar = "2"', 'foo=1; bar=2'), + array( + 'cookies' => array( + 'foo' => '1', + 'bar' => '2', + ), + 'data' => array(), + 'discard' => null, + 'domain' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + // Tests getting the domain and path from a reference request + array(array( + 'foo=1; port="80,8081"; httponly', 'foo=1; port="80,8081"; domain=www.test.com; HttpOnly;', 'foo=1; ; domain=www.test.com; path=/path/; port="80,8081"; HttpOnly;'), + array( + 'cookies' => array( + 'foo' => 1 + ), + 'data' => array(), + 'discard' => null, + 'domain' => 'www.test.com', + 'expires' => null, + 'max_age' => null, + 'path' => '/path/', + 'port' => array('80', '8081'), + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => true + ), + 'http://www.test.com/path/' + ), + // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php + array( + 'justacookie=foo; domain=example.com', + array( + 'cookies' => array( + 'justacookie' => 'foo' + ), + 'domain' => 'example.com', + 'path' => '', + 'data' => array(), + 'discard' => null, + 'expires' => null, + 'max_age' => null, + 'path' => '/', + 'port' => null, + 'secure' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com', + array( + 'cookies' => array( + 'expires' => 'tomorrow' + ), + 'domain' => '.example.com', + 'path' => '/Space Out/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'data' => array(), + 'discard' => null, + 'port' => null, + 'secure' => true, + 'version' => null, + 'max_age' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/', + array( + 'cookies' => array( + 'domain' => 'unittests' + ), + 'domain' => 'example.com', + 'path' => '/some value/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT', + array( + 'cookies' => array( + 'path' => 'indexAction' + ), + 'domain' => '.foo.com', + 'path' => '/', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => false, + 'data' => array(), + 'discard' => null, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400', + array( + 'cookies' => array( + 'secure' => 'sha1' + ), + 'domain' => 'some.really.deep.domain.com', + 'path' => '/', + 'secure' => true, + 'data' => array(), + 'discard' => null, + 'expires' => time() + 86400, + 'max_age' => 86400, + 'port' => null, + 'version' => 1, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + array( + 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;', + array( + 'cookies' => array( + 'PHPSESSID' => '123456789 abcd,ef' + ), + 'domain' => '.localdomain', + 'path' => '/foo/baz', + 'expires' => 'Tue, 21-Nov-2006 08:33:44 GMT', + 'secure' => true, + 'data' => array(), + 'discard' => true, + 'max_age' => null, + 'port' => null, + 'version' => null, + 'comment' => null, + 'comment_url' => null, + 'http_only' => false + ) + ), + ); + } + + /** + * @dataProvider cookieParserDataProvider + * @covers Guzzle\Parser\Cookie\CookieParser + */ + public function testParseCookie($cookie, $parsed, $url = null) + { + $c = $this->cookieParserClass; + $parser = new $c(); + + $request = null; + if ($url) { + $url = Url::factory($url); + $host = $url->getHost(); + $path = $url->getPath(); + } else { + $host = ''; + $path = ''; + } + + foreach ((array) $cookie as $c) { + $p = $parser->parseCookie($c, $host, $path); + + // Remove expires values from the assertion if they are relatively equal + if ($p['expires'] != $parsed['expires']) { + if (abs($p['expires'] - $parsed['expires']) < 20) { + unset($p['expires']); + unset($parsed['expires']); + } + } + + if (is_array($parsed)) { + foreach ($parsed as $key => $value) { + $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + + foreach ($p as $key => $value) { + $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true)); + } + } else { + $this->assertEquals($parsed, $p); + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php new file mode 100644 index 0000000..85e3337 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php @@ -0,0 +1,11 @@ + 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => '', + 'port' => '', + 'path' => '/', + 'query' => '' + ), + 'headers' => array(), + 'body' => '' + )), + // Path and query string, multiple header values per header and case sensitive storage + array("HEAD /path?query=foo HTTP/1.0\r\nHost: example.com\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\nX-Foo: Baz\r\n\r\n", array( + 'method' => 'HEAD', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '', + 'path' => '/path', + 'query' => 'query=foo' + ), + 'headers' => array( + 'Host' => 'example.com', + 'X-Foo' => array('foo', 'foo', 'Baz'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + // Includes a body + array("PUT / HTTP/1.0\r\nhost: example.com:443\r\nContent-Length: 4\r\n\r\ntest", array( + 'method' => 'PUT', + 'protocol' => 'HTTP', + 'version' => '1.0', + 'request_url' => array( + 'scheme' => 'https', + 'host' => 'example.com', + 'port' => '443', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'host' => 'example.com:443', + 'Content-Length' => '4' + ), + 'body' => 'test' + )), + // Includes Authorization headers + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nAuthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'Authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + // Include authorization header + array("GET / HTTP/1.1\r\nHost: example.com:8080\r\nauthorization: Basic {$auth}\r\n\r\n", array( + 'method' => 'GET', + 'protocol' => 'HTTP', + 'version' => '1.1', + 'request_url' => array( + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => '8080', + 'path' => '/', + 'query' => '' + ), + 'headers' => array( + 'Host' => 'example.com:8080', + 'authorization' => "Basic {$auth}" + ), + 'body' => '' + )), + ); + } + + public function responseProvider() + { + return array( + // Empty request + array('', false), + + array("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'OK', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 400 Bad Request\r\nContent-Length: 0\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '400', + 'reason_phrase' => 'Bad Request', + 'headers' => array( + 'Content-Length' => 0 + ), + 'body' => '' + )), + array("HTTP/1.0 100 Continue\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.0', + 'code' => '100', + 'reason_phrase' => 'Continue', + 'headers' => array(), + 'body' => '' + )), + array("HTTP/1.1 204 No Content\r\nX-Foo: foo\r\nx-foo: Bar\r\nX-Foo: foo\r\n\r\n", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '204', + 'reason_phrase' => 'No Content', + 'headers' => array( + 'X-Foo' => array('foo', 'foo'), + 'x-foo' => 'Bar' + ), + 'body' => '' + )), + array("HTTP/1.1 200 Ok that is great!\r\nContent-Length: 4\r\n\r\nTest", array( + 'protocol' => 'HTTP', + 'version' => '1.1', + 'code' => '200', + 'reason_phrase' => 'Ok that is great!', + 'headers' => array( + 'Content-Length' => 4 + ), + 'body' => 'Test' + )), + ); + } + + public function compareRequestResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['method'], $expected['method']); + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['request_url'], $expected['request_url']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + public function compareResponseResults($result, $expected) + { + if (!$result) { + $this->assertFalse($expected); + return; + } + + $this->assertEquals($result['protocol'], $expected['protocol']); + $this->assertEquals($result['version'], $expected['version']); + $this->assertEquals($result['code'], $expected['code']); + $this->assertEquals($result['reason_phrase'], $expected['reason_phrase']); + $this->assertEquals($result['body'], $expected['body']); + $this->compareHttpHeaders($result['headers'], $expected['headers']); + } + + protected function normalizeHeaders($headers) + { + $normalized = array(); + foreach ($headers as $key => $value) { + $key = strtolower($key); + if (!isset($normalized[$key])) { + $normalized[$key] = $value; + } elseif (!is_array($normalized[$key])) { + $normalized[$key] = array($value); + } else { + $normalized[$key][] = $value; + } + } + + foreach ($normalized as $key => &$value) { + if (is_array($value)) { + sort($value); + } + } + + return $normalized; + } + + public function compareHttpHeaders($result, $expected) + { + // Aggregate all headers case-insensitively + $result = $this->normalizeHeaders($result); + $expected = $this->normalizeHeaders($expected); + $this->assertEquals($result, $expected); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php new file mode 100644 index 0000000..bf0f994 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php @@ -0,0 +1,31 @@ +compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @covers Guzzle\Parser\Message\MessageParser::parseMessage + * @covers Guzzle\Parser\Message\MessageParser::parseResponse + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new MessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php new file mode 100644 index 0000000..5a8ba07 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php @@ -0,0 +1,35 @@ +markTestSkipped('pecl_http is not available.'); + } + } + + /** + * @covers Guzzle\Parser\Message\PeclHttpMessageParser::parseRequest + * @dataProvider requestProvider + */ + public function testParsesRequests($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareRequestResults($parts, $parser->parseRequest($message)); + } + + /** + * @covers Guzzle\Parser\Message\PeclHttpMessageParser::parseResponse + * @dataProvider responseProvider + */ + public function testParsesResponses($message, $parts) + { + $parser = new PeclHttpMessageParser(); + $this->compareResponseResults($parts, $parser->parseResponse($message)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php new file mode 100644 index 0000000..47c901a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php @@ -0,0 +1,36 @@ +assertSame($c, ParserRegistry::get('foo')); + } + + public function testReturnsNullWhenNotFound() + { + $this->assertNull(ParserRegistry::get('FOO')); + } + + public function testReturnsLazyLoadedDefault() + { + // Clear out what might be cached + $refObject = new \ReflectionClass('Guzzle\Parser\ParserRegistry'); + $refProperty = $refObject->getProperty('instances'); + $refProperty->setAccessible(true); + $refProperty->setValue(null, array()); + + $c = ParserRegistry::get('cookie'); + $this->assertInstanceOf('Guzzle\Parser\Cookie\CookieParser', $c); + $this->assertSame($c, ParserRegistry::get('cookie')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php new file mode 100644 index 0000000..9731db1 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php @@ -0,0 +1,202 @@ + 'value', + 'hello' => 'Hello World!', + 'empty' => '', + 'path' => '/foo/bar', + 'x' => '1024', + 'y' => '768', + 'list' => array('red', 'green', 'blue'), + 'keys' => array( + "semi" => ';', + "dot" => '.', + "comma" => ',' + ) + ); + + return array_map(function($t) use ($params) { + $t[] = $params; + return $t; + }, array( + array('foo', 'foo'), + array('{var}', 'value'), + array('{hello}', 'Hello%20World%21'), + array('{+var}', 'value'), + array('{+hello}', 'Hello%20World!'), + array('{+path}/here', '/foo/bar/here'), + array('here?ref={+path}', 'here?ref=/foo/bar'), + array('X{#var}', 'X#value'), + array('X{#hello}', 'X#Hello%20World!'), + array('map?{x,y}', 'map?1024,768'), + array('{x,hello,y}', '1024,Hello%20World%21,768'), + array('{+x,hello,y}', '1024,Hello%20World!,768'), + array('{+path,x}/here', '/foo/bar,1024/here'), + array('{#x,hello,y}', '#1024,Hello%20World!,768'), + array('{#path,x}/here', '#/foo/bar,1024/here'), + array('X{.var}', 'X.value'), + array('X{.x,y}', 'X.1024.768'), + array('{/var}', '/value'), + array('{/var,x}/here', '/value/1024/here'), + array('{;x,y}', ';x=1024;y=768'), + array('{;x,y,empty}', ';x=1024;y=768;empty'), + array('{?x,y}', '?x=1024&y=768'), + array('{?x,y,empty}', '?x=1024&y=768&empty='), + array('?fixed=yes{&x}', '?fixed=yes&x=1024'), + array('{&x,y,empty}', '&x=1024&y=768&empty='), + array('{var:3}', 'val'), + array('{var:30}', 'value'), + array('{list}', 'red,green,blue'), + array('{list*}', 'red,green,blue'), + array('{keys}', 'semi,%3B,dot,.,comma,%2C'), + array('{keys*}', 'semi=%3B,dot=.,comma=%2C'), + array('{+path:6}/here', '/foo/b/here'), + array('{+list}', 'red,green,blue'), + array('{+list*}', 'red,green,blue'), + array('{+keys}', 'semi,;,dot,.,comma,,'), + array('{+keys*}', 'semi=;,dot=.,comma=,'), + array('{#path:6}/here', '#/foo/b/here'), + array('{#list}', '#red,green,blue'), + array('{#list*}', '#red,green,blue'), + array('{#keys}', '#semi,;,dot,.,comma,,'), + array('{#keys*}', '#semi=;,dot=.,comma=,'), + array('X{.var:3}', 'X.val'), + array('X{.list}', 'X.red,green,blue'), + array('X{.list*}', 'X.red.green.blue'), + array('X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'), + array('X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'), + array('{/var:1,var}', '/v/value'), + array('{/list}', '/red,green,blue'), + array('{/list*}', '/red/green/blue'), + array('{/list*,path:4}', '/red/green/blue/%2Ffoo'), + array('{/keys}', '/semi,%3B,dot,.,comma,%2C'), + array('{/keys*}', '/semi=%3B/dot=./comma=%2C'), + array('{;hello:5}', ';hello=Hello'), + array('{;list}', ';list=red,green,blue'), + array('{;list*}', ';list=red;list=green;list=blue'), + array('{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'), + array('{;keys*}', ';semi=%3B;dot=.;comma=%2C'), + array('{?var:3}', '?var=val'), + array('{?list}', '?list=red,green,blue'), + array('{?list*}', '?list=red&list=green&list=blue'), + array('{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'), + array('{?keys*}', '?semi=%3B&dot=.&comma=%2C'), + array('{&var:3}', '&var=val'), + array('{&list}', '&list=red,green,blue'), + array('{&list*}', '&list=red&list=green&list=blue'), + array('{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'), + array('{&keys*}', '&semi=%3B&dot=.&comma=%2C'), + // Test that missing expansions are skipped + array('test{&missing*}', 'test'), + // Test that multiple expansions can be set + array('http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'), + // Test that it is backwards compatible with {{ }} syntax + array('{{var}}|{{var:3}}', 'value|val'), + // Test more complex query string stuff + array('http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C') + )); + } + + /** + * @dataProvider templateProvider + */ + public function testExpandsUriTemplates($template, $expansion, $params) + { + $uri = new UriTemplate($template); + $result = $uri->expand($template, $params); + $this->assertEquals($expansion, $result); + } + + public function expressionProvider() + { + return array( + array( + '{+var*}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'var', 'modifier' => '*') + ) + ), + ), + array( + '{?keys,var,val}', array( + 'operator' => '?', + 'values' => array( + array('value' => 'keys', 'modifier' => ''), + array('value' => 'var', 'modifier' => ''), + array('value' => 'val', 'modifier' => '') + ) + ), + ), + array( + '{+x,hello,y}', array( + 'operator' => '+', + 'values' => array( + array('value' => 'x', 'modifier' => ''), + array('value' => 'hello', 'modifier' => ''), + array('value' => 'y', 'modifier' => '') + ) + ) + ) + ); + } + + /** + * @dataProvider expressionProvider + */ + public function testParsesExpressions($exp, $data) + { + $template = new UriTemplate($exp); + + // Access the config object + $class = new \ReflectionClass($template); + $method = $class->getMethod('parseExpression'); + $method->setAccessible(true); + + $exp = substr($exp, 1, -1); + $this->assertEquals($data, $method->invokeArgs($template, array($exp))); + } + + /** + * @ticket https://github.com/guzzle/guzzle/issues/90 + */ + public function testAllowsNestedArrayExpansion() + { + $template = new UriTemplate(); + + $result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', array( + 'path' => '/foo/bar', + 'segments' => array('one', 'two'), + 'query' => 'test', + 'data' => array( + 'more' => array('fun', 'ice cream') + ), + 'foo' => array( + 'baz' => array( + 'bar' => 'fizz', + 'test' => 'buzz' + ), + 'bam' => 'boo' + ) + )); + + $this->assertEquals('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Url/UrlParserProvider.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Url/UrlParserProvider.php new file mode 100644 index 0000000..306cc41 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Parser/Url/UrlParserProvider.php @@ -0,0 +1,29 @@ +assertEquals($url, Url::buildUrl($parts)); + } + + public function testCanUseUtf8Query() + { + $url = Url::factory('http://www.example.com?µ=a'); + $this->assertEquals('a', $url->getQuery()->get('µ')); + } + + public function testParsesUtf8UrlQueryStringsWithFragment() + { + $parser = new UrlParser(); + $parser->setUtf8Support(true); + + $parts = $parser->parseUrl('http://www.example.com?ሴ=a#fragmentishere'); + $this->assertEquals('ሴ=a', $parts['query']); + $this->assertEquals('fragmentishere', $parts['fragment']); + + $parts = $parser->parseUrl('http://www.example.com?ሴ=a'); + $this->assertEquals('ሴ=a', $parts['query']); + $this->assertEquals('', $parts['fragment']); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractFactoryTest.php new file mode 100644 index 0000000..5ce645b --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/AbstractFactoryTest.php @@ -0,0 +1,67 @@ +getMockBuilder('Guzzle\Service\AbstractFactory') + ->setMethods(array('getCacheTtlKey', 'throwException', 'getClassName')) + ->getMockForAbstractClass(); + } + + public function testCachesArtifacts() + { + $jsonFile = __DIR__ . '/../TestData/test_service.json'; + + $adapter = new DoctrineCacheAdapter(new ArrayCache()); + $factory = $this->getFactory(); + + $factory->expects($this->once()) + ->method('getClassName') + ->will($this->returnValue('Guzzle\Service\Builder\JsonServiceBuilderFactory')); + + // Create a service and add it to the cache + $service = $factory->build($jsonFile, array( + 'cache.adapter' => $adapter + )); + + // Ensure the cache key was set + $this->assertTrue($adapter->contains('guzzle' . crc32($jsonFile))); + + // Grab the service from the cache + $this->assertEquals($service, $factory->build($jsonFile, array( + 'cache.adapter' => $adapter + ))); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceBuilderException + */ + public function testThrowsExceptionsWhenNoFactoryResolves() + { + $factory = $this->getFactory(); + $factory->expects($this->any()) + ->method('getClassName') + ->will($this->returnValue(false)); + + // Exceptions are mocked and disabled, so nothing happens here + $service = $factory->build('foo'); + + // Throw an exception when it's supposed to + $factory->expects($this->any()) + ->method('throwException') + ->will($this->throwException(new ServiceBuilderException())); + + $service = $factory->build('foo'); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ArrayServiceBuilderFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ArrayServiceBuilderFactoryTest.php new file mode 100644 index 0000000..beec2ba --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ArrayServiceBuilderFactoryTest.php @@ -0,0 +1,96 @@ + array( + 'abstract' => array( + 'params' => array( + 'access_key' => 'xyz', + 'secret' => 'abc', + ), + ), + 'foo' => array( + 'extends' => 'abstract', + 'params' => array( + 'baz' => 'bar', + ), + ), + 'mock' => array( + 'extends' => 'abstract', + 'params' => array( + 'username' => 'foo', + 'password' => 'baz', + 'subdomain' => 'bar', + ) + ) + ) + ); + + $builder = $arrayFactory->build($data); + + // Ensure that services were parsed + $this->assertTrue(isset($builder['mock'])); + $this->assertTrue(isset($builder['abstract'])); + $this->assertTrue(isset($builder['foo'])); + $this->assertFalse(isset($builder['jimmy'])); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage foo is trying to extend a non-existent service: abstract + */ + public function testThrowsExceptionWhenExtendingNonExistentService() + { + $arrayFactory = new ArrayServiceBuilderFactory(); + + $data = array( + 'services' => array( + 'foo' => array( + 'extends' => 'abstract' + ) + ) + ); + + $builder = $arrayFactory->build($data); + } + + public function testAllowsGlobalParameterOverrides() + { + $arrayFactory = new ArrayServiceBuilderFactory(); + + $data = array( + 'services' => array( + 'foo' => array( + 'params' => array( + 'foo' => 'baz', + 'bar' => 'boo' + ) + ) + ) + ); + + $builder = $arrayFactory->build($data, array( + 'bar' => 'jar', + 'far' => 'car' + )); + + $compiled = json_decode($builder->serialize(), true); + $this->assertEquals(array( + 'foo' => 'baz', + 'bar' => 'jar', + 'far' => 'car' + ), $compiled['foo']['params']); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/JsonServiceBuilderFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/JsonServiceBuilderFactoryTest.php new file mode 100644 index 0000000..d542d71 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/JsonServiceBuilderFactoryTest.php @@ -0,0 +1,29 @@ +build($file); + // Build it again, get a similar result using the same JsonLoader + $this->assertEquals($builder, $j->build($file)); + + // Ensure that services were parsed + $this->assertTrue(isset($builder['mock'])); + $this->assertTrue(isset($builder['abstract'])); + $this->assertTrue(isset($builder['foo'])); + $this->assertFalse(isset($builder['jimmy'])); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderAbstractFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderAbstractFactoryTest.php new file mode 100644 index 0000000..41ab52b --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderAbstractFactoryTest.php @@ -0,0 +1,51 @@ +xmlFile = __DIR__ . '/../../TestData/services/new_style.xml'; + $this->jsonFile = __DIR__ . '/../../TestData/services/json1.json'; + } + + public function testFactoryDelegatesToConcreteFactories() + { + $factory = new ServiceBuilderAbstractFactory(); + $this->assertInstanceOf('Guzzle\Service\Builder\ServiceBuilder', $factory->build($this->xmlFile)); + $this->assertInstanceOf('Guzzle\Service\Builder\ServiceBuilder', $factory->build($this->jsonFile)); + + $xml = new \SimpleXMLElement(file_get_contents($this->xmlFile)); + $xml->includes = null; + $this->assertInstanceOf('Guzzle\Service\Builder\ServiceBuilder', $factory->build($xml)); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceBuilderException + * @expectedExceptionMessage Unable to determine which factory to use based on the file extension of jarJarBinks. Valid file extensions are: .js, .json, .xml + */ + public function testFactoryEnsuresItCanHandleTheTypeOfFileOrArray() + { + $factory = new ServiceBuilderAbstractFactory(); + $factory->build('jarJarBinks'); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceBuilderException + * @expectedExceptionMessage Must pass a file name, array, or SimpleXMLElement + */ + public function testThrowsExceptionWhenUnknownTypeIsPassed() + { + $factory = new ServiceBuilderAbstractFactory(); + $factory->build(new \stdClass()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php new file mode 100644 index 0000000..7272426 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php @@ -0,0 +1,332 @@ + array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'username' => 'michael', + 'password' => 'testing123', + 'subdomain' => 'michael', + ), + ), + 'billy.mock' => array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'username' => 'billy', + 'password' => 'passw0rd', + 'subdomain' => 'billy', + ), + ), + 'billy.testing' => array( + 'extends' => 'billy.mock', + 'params' => array( + 'subdomain' => 'test.billy', + ), + ), + 'missing_params' => array( + 'extends' => 'billy.mock' + ) + ); + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::serialize + * @covers Guzzle\Service\Builder\ServiceBuilder::unserialize + */ + public function testAllowsSerialization() + { + $builder = ServiceBuilder::factory($this->arrayData); + $cached = unserialize(serialize($builder)); + $this->assertEquals($cached, $builder); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::factory + */ + public function testDelegatesFactoryMethodToAbstractFactory() + { + $builder = ServiceBuilder::factory($this->arrayData); + $c = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $c); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::get + * @expectedException Guzzle\Service\Exception\ServiceNotFoundException + * @expectedExceptionMessage No service is registered as foobar + */ + public function testThrowsExceptionWhenGettingInvalidClient() + { + ServiceBuilder::factory($this->arrayData)->get('foobar'); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::get + */ + public function testStoresClientCopy() + { + $builder = ServiceBuilder::factory($this->arrayData); + $client = $builder->get('michael.mock'); + $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $client); + $this->assertEquals('http://127.0.0.1:8124/v1/michael', $client->getBaseUrl()); + $this->assertEquals($client, $builder->get('michael.mock')); + + // Get another client but throw this one away + $client2 = $builder->get('billy.mock', true); + $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $client2); + $this->assertEquals('http://127.0.0.1:8124/v1/billy', $client2->getBaseUrl()); + + // Make sure the original client is still there and set + $this->assertTrue($client === $builder->get('michael.mock')); + + // Create a new billy.mock client that is stored + $client3 = $builder->get('billy.mock'); + + // Make sure that the stored billy.mock client is equal to the other stored client + $this->assertTrue($client3 === $builder->get('billy.mock')); + + // Make sure that this client is not equal to the previous throwaway client + $this->assertFalse($client2 === $builder->get('billy.mock')); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder + */ + public function testBuildersPassOptionsThroughToClients() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertEquals(8080, $c->getConfig('curl.curlopt_proxyport')); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder + */ + public function testUsesTheDefaultBuilderWhenNoBuilderIsSpecified() + { + $s = new ServiceBuilder(array( + 'michael.mock' => array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + 'curl.curlopt_proxyport' => 8080 + ) + ) + )); + + $c = $s->get('michael.mock'); + $this->assertInstanceOf('Guzzle\\Tests\\Service\\Mock\\MockClient', $c); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::set + * @covers Guzzle\Service\Builder\ServiceBuilder::offsetSet + * @covers Guzzle\Service\Builder\ServiceBuilder::offsetGet + * @covers Guzzle\Service\Builder\ServiceBuilder::offsetUnset + * @covers Guzzle\Service\Builder\ServiceBuilder::offsetExists + */ + public function testUsedAsArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $this->assertTrue($b->offsetExists('michael.mock')); + $this->assertFalse($b->offsetExists('not_there')); + $this->assertInstanceOf('Guzzle\\Service\\Client', $b['michael.mock']); + + unset($b['michael.mock']); + $this->assertFalse($b->offsetExists('michael.mock')); + + $b['michael.mock'] = new Client('http://www.test.com/'); + $this->assertInstanceOf('Guzzle\\Service\\Client', $b['michael.mock']); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::factory + */ + public function testFactoryCanCreateFromJson() + { + $tmp = sys_get_temp_dir() . '/test.js'; + file_put_contents($tmp, json_encode($this->arrayData)); + $b = ServiceBuilder::factory($tmp); + unlink($tmp); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::factory + */ + public function testFactoryCanCreateFromArray() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('billy.testing'); + $this->assertEquals('test.billy', $s->getConfig('subdomain')); + $this->assertEquals('billy', $s->getConfig('username')); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::factory + * @expectedException Guzzle\Service\Exception\ServiceBuilderException + * @expectedExceptionMessage Unable to determine which factory to use based on the file extension of + */ + public function testFactoryValidatesFileExtension() + { + $tmp = sys_get_temp_dir() . '/test.abc'; + file_put_contents($tmp, 'data'); + try { + ServiceBuilder::factory($tmp); + } catch (\RuntimeException $e) { + unlink($tmp); + throw $e; + } + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::factory + * @expectedException Guzzle\Service\Exception\ServiceBuilderException + * @expectedExceptionMessage Must pass a file name, array, or SimpleXMLElement + */ + public function testFactoryValidatesObjectTypes() + { + ServiceBuilder::factory(new \stdClass()); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::factory + */ + public function testFactoryDoesNotRequireParams() + { + $b = ServiceBuilder::factory($this->arrayData); + $s = $b->get('missing_params'); + $this->assertEquals('billy', $s->getConfig('username')); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder + */ + public function testBuilderAllowsReferencesBetweenClients() + { + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'other_client' => '{{ b }}', + 'username' => 'x', + 'password' => 'y', + 'subdomain' => 'z' + ) + ), + 'b' => array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'username' => '1', + 'password' => '2', + 'subdomain' => '3' + ) + ) + )); + + $client = $builder['a']; + $this->assertEquals('x', $client->getConfig('username')); + $this->assertSame($builder['b'], $client->getConfig('other_client')); + $this->assertEquals('1', $builder['b']->getConfig('username')); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::getAllEvents + * @covers Guzzle\Service\Builder\ServiceBuilder::get + */ + public function testEmitsEventsWhenClientsAreCreated() + { + // Ensure that the client signals that it emits an event + $this->assertEquals(array('service_builder.create_client'), ServiceBuilder::getAllEvents()); + + // Create a test service builder + $builder = ServiceBuilder::factory(array( + 'a' => array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'username' => 'test', + 'password' => '123', + 'subdomain' => 'z' + ) + ) + )); + + $emits = 0; + $emitted = null; + + // Add an event listener to pick up client creation events + $builder->getEventDispatcher()->addListener('service_builder.create_client', function($event) use (&$emits, &$emitted) { + $emits++; + $emitted = $event['client']; + }); + + // Get the 'a' client by name + $client = $builder->get('a'); + + // Ensure that the event was emitted once, and that the client was present + $this->assertEquals(1, $emits); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $client); + } + + /** + * @covers Guzzle\Service\Builder\ServiceBuilder::factory + */ + public function testCanAddGlobalParametersToServicesOnLoad() + { + $builder = ServiceBuilder::factory($this->arrayData, array( + 'username' => 'fred', + 'new_value' => 'test' + )); + + $data = json_decode($builder->serialize(), true); + + foreach ($data as $service) { + $this->assertEquals('fred', $service['params']['username']); + $this->assertEquals('test', $service['params']['new_value']); + } + } + + public function testDescriptionIsCacheable() + { + $jsonFile = __DIR__ . '/../../TestData/test_service.json'; + $adapter = new DoctrineCacheAdapter(new ArrayCache()); + + $builder = ServiceBuilder::factory($jsonFile, array( + 'cache.adapter' => $adapter + )); + + // Ensure the cache key was set + $this->assertTrue($adapter->contains('guzzle' . crc32($jsonFile))); + + // Grab the service from the cache + $this->assertEquals($builder, ServiceBuilder::factory($jsonFile, array( + 'cache.adapter' => $adapter + ))); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/XmlServiceBuilderFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/XmlServiceBuilderFactoryTest.php new file mode 100644 index 0000000..e0b7225 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Builder/XmlServiceBuilderFactoryTest.php @@ -0,0 +1,46 @@ +build($file); + + // Ensure that services were parsed + $this->assertTrue(isset($builder['mock'])); + $this->assertTrue(isset($builder['abstract'])); + $this->assertTrue(isset($builder['foo'])); + $this->assertFalse(isset($builder['jimmy'])); + } + + public function testBuildsServiceBuildersUsingSimpleXmlElement() + { + $xmlFactory = new XmlServiceBuilderFactory(); + $file = __DIR__ . '/../../TestData/services/new_style.xml'; + $xml = new \SimpleXMLElement(file_get_contents($file)); + $xml->includes = null; + $this->assertInstanceOf('Guzzle\Service\Builder\ServiceBuilder', $xmlFactory->build($xml)); + } + + /** + * @expectedException Guzzle\Service\Exception\ServiceBuilderException + */ + public function testCannotExtendWhenUsingSimpleXMLElement() + { + $xmlFactory = new XmlServiceBuilderFactory(); + $file = __DIR__ . '/../../TestData/services/new_style.xml'; + $xml = new \SimpleXMLElement(file_get_contents($file)); + $xmlFactory->build($xml); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php new file mode 100644 index 0000000..89c232a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/ClientTest.php @@ -0,0 +1,370 @@ +serviceTest = new ServiceDescription(array( + 'test_command' => new ApiCommand(array( + 'doc' => 'documentationForCommand', + 'method' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'args' => array( + 'bucket' => array( + 'required' => true + ), + 'key' => array( + 'required' => true + ) + ) + )) + )); + + $this->service = ServiceDescription::factory(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'); + } + + /** + * @covers Guzzle\Service\Client::factory + */ + public function testFactoryCreatesClient() + { + $client = Client::factory(array( + 'base_url' => 'http://www.test.com/', + 'test' => '123' + )); + + $this->assertEquals('http://www.test.com/', $client->getBaseUrl()); + $this->assertEquals('123', $client->getConfig('test')); + } + + /** + * @covers Guzzle\Service\Client::getAllEvents + */ + public function testDescribesEvents() + { + $this->assertInternalType('array', Client::getAllEvents()); + } + + /** + * @covers Guzzle\Service\Client::execute + */ + public function testExecutesCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); + + $client = new Client($this->getServer()->getUrl()); + $cmd = new MockCommand(); + $client->execute($cmd); + + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $cmd->getResult()); + $this->assertEquals(1, count($this->getServer()->getReceivedRequests(false))); + } + + /** + * @covers Guzzle\Service\Client::execute + */ + public function testExecutesCommandsWithArray() + { + $client = new Client('http://www.test.com/'); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200), + new Response(200) + ))); + + // Create a command set and a command + $set = array(new MockCommand(), new MockCommand()); + $client->execute($set); + + // Make sure it sent + $this->assertTrue($set[0]->isExecuted()); + $this->assertTrue($set[1]->isExecuted()); + } + + /** + * @covers Guzzle\Service\Client::execute + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testThrowsExceptionWhenInvalidCommandIsExecuted() + { + $client = new Client(); + $client->execute(new \stdClass()); + } + + /** + * @covers Guzzle\Service\Client::getCommand + * @expectedException InvalidArgumentException + */ + public function testThrowsExceptionWhenMissingCommand() + { + $client = new Client(); + + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('test')) + ->will($this->returnValue(null)); + + $client->setCommandFactory($mock); + $client->getCommand('test'); + } + + /** + * @covers Guzzle\Service\Client::getCommand + */ + public function testCreatesCommandsUsingCommandFactory() + { + $mockCommand = new MockCommand(); + + $client = new Mock\MockClient(); + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\FactoryInterface'); + $mock->expects($this->any()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand)); + + $client->setCommandFactory($mock); + + $command = $client->getCommand('foo', array( + 'acl' => '123' + )); + + $this->assertSame($mockCommand, $command); + $this->assertSame($client, $command->getClient()); + } + + /** + * @covers Guzzle\Service\Client::getDescription + * @covers Guzzle\Service\Client::setDescription + */ + public function testOwnsServiceDescription() + { + $client = new Mock\MockClient(); + $this->assertNull($client->getDescription()); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $this->assertSame($client, $client->setDescription($description)); + $this->assertSame($description, $client->getDescription()); + } + + /** + * @covers Guzzle\Service\Client::setDescription + */ + public function testSettingServiceDescriptionUpdatesFactories() + { + $client = new Mock\MockClient(); + $factory = $this->getMockBuilder('Guzzle\\Service\\Command\\Factory\\MapFactory') + ->disableOriginalConstructor() + ->getMock(); + $client->setCommandFactory($factory); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $client->setDescription($description); + + $cf = $this->readAttribute($client, 'commandFactory'); + $this->assertNotSame($factory, $cf); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\CompositeFactory', $cf); + + $array = $cf->getIterator()->getArrayCopy(); + $this->assertSame($array[0], $factory); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory', $array[1]); + $this->assertSame($description, $array[1]->getServiceDescription()); + + $description2 = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $client->setDescription($description2); + + $cf = $this->readAttribute($client, 'commandFactory'); + $array = $cf->getIterator()->getArrayCopy(); + $this->assertSame($array[0], $factory); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory', $array[1]); + $this->assertSame($description2, $array[1]->getServiceDescription()); + } + + /** + * @covers Guzzle\Service\Client::__call + * @expectedException BadMethodCallException + */ + public function testMagicCallBehaviorIsDisabledByDefault() + { + $client = new Client(); + $client->foo(); + } + + /** + * @covers Guzzle\Service\Client::__call + * @covers Guzzle\Service\Client::setMagicCallBehavior + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Command was not found matching foo + */ + public function testMagicCallBehaviorEnsuresCommandExists() + { + $client = new Mock\MockClient(); + $client->setDescription($this->service); + $client->setMagicCallBehavior(Client::MAGIC_CALL_RETURN); + $client->foo(); + } + + /** + * @covers Guzzle\Service\Client::__call + */ + public function testMagicCallBehaviorReturnReturnsCommands() + { + $client = new Mock\MockClient(); + $client->setMagicCallBehavior(Client::MAGIC_CALL_RETURN); + $client->setDescription($this->service); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Command\MockCommand', $client->mockCommand()); + } + + /** + * @covers Guzzle\Service\Client::__call + */ + public function testMagicCallBehaviorExecuteExecutesCommands() + { + $client = new Mock\MockClient(); + $client->setMagicCallBehavior(Client::MAGIC_CALL_EXECUTE); + $client->setDescription($this->service); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array(new Response(200)))); + $this->assertInstanceOf('Guzzle\Http\Message\Response', $client->mockCommand()); + } + + /** + * @covers Guzzle\Service\Client::getCommandFactory + * @covers Guzzle\Service\Client::setCommandFactory + */ + public function testOwnsCommandFactory() + { + $client = new Mock\MockClient(); + $method = new \ReflectionMethod($client, 'getCommandFactory'); + $method->setAccessible(TRUE); + $cf1 = $method->invoke($client); + + $cf = $this->readAttribute($client, 'commandFactory'); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\CompositeFactory', $cf); + $this->assertSame($method->invoke($client), $cf1); + + $mock = $this->getMock('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $client->setCommandFactory($mock); + $this->assertSame($mock, $this->readAttribute($client, 'commandFactory')); + } + + /** + * @covers Guzzle\Service\Client::getResourceIteratorFactory + * @covers Guzzle\Service\Client::setResourceIteratorFactory + */ + public function testOwnsResourceIteratorFactory() + { + $client = new Mock\MockClient(); + + $method = new \ReflectionMethod($client, 'getResourceIteratorFactory'); + $method->setAccessible(TRUE); + $rf1 = $method->invoke($client); + + $rf = $this->readAttribute($client, 'resourceIteratorFactory'); + $this->assertInstanceOf('Guzzle\\Service\\Resource\\ResourceIteratorClassFactory', $rf); + $this->assertSame($rf1, $rf); + + $rf = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock'); + $client->setResourceIteratorFactory($rf); + $this->assertNotSame($rf1, $rf); + } + + /** + * @covers Guzzle\Service\Client::execute + */ + public function testClientResetsRequestsBeforeExecutingCommands() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHi", + "HTTP/1.1 200 OK\r\nContent-Length: 1\r\n\r\nI" + )); + + $client = new Mock\MockClient($this->getServer()->getUrl()); + + $command = $client->getCommand('mock_command'); + $client->execute($command); + $client->execute($command); + $this->assertEquals('I', $command->getResponse()->getBody(true)); + } + + /** + * @covers Guzzle\Service\Client::getIterator + */ + public function testClientCreatesIterators() + { + $client = new Mock\MockClient(); + + $iterator = $client->getIterator('mock_command', array( + 'foo' => 'bar' + ), array( + 'limit' => 10 + )); + + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $this->assertEquals(10, $this->readAttribute($iterator, 'limit')); + + $command = $this->readAttribute($iterator, 'originalCommand'); + $this->assertEquals('bar', $command->get('foo')); + } + + /** + * @covers Guzzle\Service\Client::getIterator + */ + public function testClientCreatesIteratorsWithNoOptions() + { + $client = new Mock\MockClient(); + $iterator = $client->getIterator('mock_command'); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } + + /** + * @covers Guzzle\Service\Client::getIterator + */ + public function testClientCreatesIteratorsWithCommands() + { + $client = new Mock\MockClient(); + $command = new MockCommand(); + $iterator = $client->getIterator($command); + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + $iteratorCommand = $this->readAttribute($iterator, 'originalCommand'); + $this->assertSame($command, $iteratorCommand); + } + + /** + * @covers Guzzle\Service\Client::getInflector + * @covers Guzzle\Service\Client::setInflector + */ + public function testClientHoldsInflector() + { + $client = new Mock\MockClient(); + $this->assertInstanceOf('Guzzle\Common\Inflection\MemoizingInflector', $client->getInflector()); + + $inflector = new Inflector(); + $client->setInflector($inflector); + $this->assertSame($inflector, $client->getInflector()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php new file mode 100644 index 0000000..02122db --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php @@ -0,0 +1,18 @@ +setDescription($service); + + return $client; + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/BatchCommandTransferTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/BatchCommandTransferTest.php new file mode 100644 index 0000000..ccca212 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/BatchCommandTransferTest.php @@ -0,0 +1,83 @@ + $command) { + if ($i % 2) { + $command->setClient($client1); + } else { + $command->setClient($client2); + } + $queue[] = $command; + } + + $batch = new BatchCommandTransfer(2); + $this->assertEquals(array( + array($commands[0], $commands[2]), + array($commands[4]), + array($commands[1], $commands[3]) + ), $batch->createBatches($queue)); + } + + /** + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testEnsuresAllItemsAreCommands() + { + $queue = new \SplQueue(); + $queue[] = 'foo'; + $batch = new BatchCommandTransfer(2); + $batch->createBatches($queue); + } + + public function testTransfersBatches() + { + $client = $this->getMockBuilder('Guzzle\Service\Client') + ->setMethods(array('send')) + ->getMock(); + $client->expects($this->once()) + ->method('send'); + $command = new Mc(); + $command->setClient($client); + $batch = new BatchCommandTransfer(2); + $batch->transfer(array($command)); + } + + public function testDoesNotTransfersEmptyBatches() + { + $batch = new BatchCommandTransfer(2); + $batch->transfer(array()); + } + + /** + * @expectedException Guzzle\Service\Exception\InconsistentClientTransferException + */ + public function testEnsuresAllCommandsUseTheSameClient() + { + $batch = new BatchCommandTransfer(2); + $client1 = new Client('http://www.example.com'); + $client2 = new Client('http://www.example.com'); + $command1 = new Mc(); + $command1->setClient($client1); + $command2 = new Mc(); + $command2->setClient($client2); + $batch->transfer(array($command1, $command2)); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php new file mode 100644 index 0000000..a0a28bc --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php @@ -0,0 +1,57 @@ + function($command, $api) { + $command->set('testing', '123'); + $request = RequestFactory::getInstance()->create('GET', 'http://www.test.com/'); + return $request; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + $this->assertEquals('123', $c->get('testing')); + $this->assertEquals('http://www.test.com/', $c->getRequest()->getUrl()); + } + + /** + * @covers Guzzle\Service\Command\ClosureCommand + * @expectedException UnexpectedValueException + * @expectedExceptionMessage Closure command did not return a RequestInterface object + */ + public function testMustReturnRequest() + { + $c = new ClosureCommand(array( + 'closure' => function($command, $api) { + return false; + } + )); + + $client = $this->getServiceBuilder()->get('mock'); + $c->setClient($client)->prepare(); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php new file mode 100644 index 0000000..d7346b8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php @@ -0,0 +1,387 @@ +assertEquals('123', $command->get('test')); + $this->assertFalse($command->isPrepared()); + $this->assertFalse($command->isExecuted()); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::getName + */ + public function testDeterminesShortName() + { + $api = new ApiCommand(array( + 'name' => 'foobar' + )); + $command = new MockCommand(array(), $api); + $this->assertEquals('foobar', $command->getName()); + + $command = new MockCommand(); + $this->assertEquals('mock_command', $command->getName()); + + $command = new Sub(); + $this->assertEquals('sub.sub', $command->getName()); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::getRequest + * @expectedException RuntimeException + */ + public function testGetRequestThrowsExceptionBeforePreparation() + { + $command = new MockCommand(); + $command->getRequest(); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::getResponse + * @expectedException RuntimeException + */ + public function testGetResponseThrowsExceptionBeforePreparation() + { + $command = new MockCommand(); + $command->getResponse(); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::getResult + * @expectedException RuntimeException + */ + public function testGetResultThrowsExceptionBeforePreparation() + { + $command = new MockCommand(); + $command->getResult(); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::setClient + * @covers Guzzle\Service\Command\AbstractCommand::getClient + * @covers Guzzle\Service\Command\AbstractCommand::prepare + * @covers Guzzle\Service\Command\AbstractCommand::isPrepared + */ + public function testSetClient() + { + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client); + $this->assertEquals($client, $command->getClient()); + + unset($client); + unset($command); + + $command = new MockCommand(); + $client = $this->getClient(); + + $command->setClient($client)->prepare(); + $this->assertEquals($client, $command->getClient()); + $this->assertTrue($command->isPrepared()); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::execute + * @covers Guzzle\Service\Command\AbstractCommand::setClient + * @covers Guzzle\Service\Command\AbstractCommand::getRequest + * @covers Guzzle\Service\Command\AbstractCommand::getResponse + * @covers Guzzle\Service\Command\AbstractCommand::getResult + * @covers Guzzle\Service\Command\AbstractCommand::prepare + * @covers Guzzle\Service\Command\AbstractCommand::process + * @covers Guzzle\Service\Command\AbstractCommand::prepare + * @covers Guzzle\Service\Client::execute + */ + public function testExecute() + { + $client = $this->getClient(); + + $response = new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/xml' + ), '123'); + + // Set a mock response + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + $response + ))); + + $command = new MockCommand(); + + $this->assertSame($command, $command->setClient($client)); + + // Returns the result of the command + $this->assertInstanceOf('SimpleXMLElement', $command->execute()); + + $this->assertTrue($command->isPrepared()); + $this->assertTrue($command->isExecuted()); + $this->assertSame($response, $command->getResponse()); + $this->assertInstanceOf('Guzzle\\Http\\Message\\Request', $command->getRequest()); + // Make sure that the result was automatically set to a SimpleXMLElement + $this->assertInstanceOf('SimpleXMLElement', $command->getResult()); + $this->assertEquals('123', (string)$command->getResult()->data); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::process + */ + public function testConvertsJsonResponsesToArray() + { + $client = $this->getClient(); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), '{ "key": "Hi!" }' + ) + ))); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + $this->assertEquals(array( + 'key' => 'Hi!' + ), $command->getResult()); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::process + * @expectedException Guzzle\Service\Exception\JsonException + */ + public function testConvertsInvalidJsonResponsesToArray() + { + $client = $this->getClient(); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new \Guzzle\Http\Message\Response(200, array( + 'Content-Type' => 'application/json' + ), '{ "key": "Hi!" }invalid' + ) + ))); + $command = new MockCommand(); + $command->setClient($client); + $command->execute(); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::process + */ + public function testProcessResponseIsNotXml() + { + $client = $this->getClient(); + + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200, array( + 'Content-Type' => 'application/octet-stream' + ), 'abc,def,ghi') + ))); + + $command = new MockCommand(); + $client->execute($command); + + // Make sure that the result was not converted to XML + $this->assertFalse($command->getResult() instanceof \SimpleXMLElement); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::execute + * @expectedException RuntimeException + */ + public function testExecuteThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->execute(); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::prepare + * @expectedException RuntimeException + */ + public function testPrepareThrowsExceptionWhenNoClientIsSet() + { + $command = new MockCommand(); + $command->prepare(); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::prepare + * @covers Guzzle\Service\Command\AbstractCommand::getRequestHeaders + */ + public function testCommandsAllowsCustomRequestHeaders() + { + $command = new MockCommand(); + $command->getRequestHeaders()->set('test', '123'); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('123', $command->getRequestHeaders()->get('test')); + + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', (string) $command->getRequest()->getHeader('test')); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::__construct + */ + public function testCommandsAllowsCustomRequestHeadersAsArray() + { + $command = new MockCommand(array( + 'headers' => array( + 'Foo' => 'Bar' + ) + )); + $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); + $this->assertEquals('Bar', $command->getRequestHeaders()->get('Foo')); + } + + private function getApiCommand() + { + return new ApiCommand(array( + 'name' => 'foobar', + 'method' => 'POST', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'params' => array( + 'test' => array( + 'default' => '123', + 'type' => 'string' + ) + ))); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand + */ + public function testCommandsUsesApiCommand() + { + $api = $this->getApiCommand(); + $command = new MockCommand(array(), $api); + $this->assertSame($api, $command->getApiCommand()); + $command->setClient($this->getClient())->prepare(); + $this->assertEquals('123', $command->get('test')); + $this->assertSame($api, $command->getApiCommand($api)); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::__clone + */ + public function testCloneMakesNewRequest() + { + $client = $this->getClient(); + $command = new MockCommand(array(), $this->getApiCommand()); + $command->setClient($client); + + $command->prepare(); + $this->assertTrue($command->isPrepared()); + + $command2 = clone $command; + $this->assertFalse($command2->isPrepared()); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::setOnComplete + * @covers Guzzle\Service\Command\AbstractCommand::__construct + * @covers Guzzle\Service\Command\AbstractCommand::getResult + */ + public function testHasOnCompleteMethod() + { + $that = $this; + $called = 0; + + $testFunction = function($command) use (&$called, $that) { + $called++; + $that->assertInstanceOf('Guzzle\Service\Command\CommandInterface', $command); + }; + + $client = $this->getClient(); + $command = new MockCommand(array( + 'command.on_complete' => $testFunction + ), $this->getApiCommand()); + $command->setClient($client); + + $command->prepare()->setResponse(new Response(200), true); + $command->execute(); + $this->assertEquals(1, $called); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::setOnComplete + * @expectedException Guzzle\Common\Exception\InvalidArgumentException + */ + public function testOnCompleteMustBeCallable() + { + $client = $this->getClient(); + $command = new MockCommand(); + $command->setOnComplete('foo'); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::setInspector + * @covers Guzzle\Service\Command\AbstractCommand::getInspector + */ + public function testInspectorCanBeInjected() + { + $instance = Inspector::getInstance(); + $command = new MockCommand(); + + $refObject = new \ReflectionObject($command); + $method = $refObject->getMethod('getInspector'); + $method->setAccessible(true); + + $this->assertSame($instance, $method->invoke($command)); + + $newInspector = new Inspector(); + $command->setInspector($newInspector); + $this->assertSame($newInspector, $method->invoke($command)); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::setResult + */ + public function testCanSetResultManually() + { + $client = $this->getClient(); + $client->getEventDispatcher()->addSubscriber(new MockPlugin(array( + new Response(200) + ))); + $command = new MockCommand(); + $client->execute($command); + $command->setResult('foo!'); + $this->assertEquals('foo!', $command->getResult()); + } + + /** + * @covers Guzzle\Service\Command\AbstractCommand::initConfig + */ + public function testCanInitConfig() + { + $command = $this->getMockBuilder('Guzzle\\Service\\Command\\AbstractCommand') + ->setConstructorArgs(array(array( + 'foo' => 'bar' + ), new ApiCommand(array( + 'params' => array( + 'baz' => new ApiParam(array( + 'default' => 'baaar' + )) + ) + )))) + ->getMockForAbstractClass(); + + $this->assertEquals('bar', $command['foo']); + $this->assertEquals('baaar', $command['baz']); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DynamicCommandTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DynamicCommandTest.php new file mode 100644 index 0000000..7c260e9 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/DynamicCommandTest.php @@ -0,0 +1,271 @@ +service = new ServiceDescription( + array( + 'test_command' => new ApiCommand(array( + 'doc' => 'documentationForCommand', + 'method' => 'HEAD', + 'uri' => '{/key}', + 'params' => array( + 'bucket' => array( + 'required' => true, + 'append' => '.' + ), + 'key' => array( + 'prepend' => 'hi_' + ), + 'acl' => array( + 'location' => 'query' + ), + 'meta' => array( + 'location' => 'header:X-Amz-Meta', + 'append' => ':meta' + ) + ) + )), + 'body' => new ApiCommand(array( + 'doc' => 'doc', + 'method' => 'PUT', + 'params' => array( + 'b' => array( + 'required' => true, + 'prepend' => 'begin_body::', + 'append' => '::end_body', + 'location' => 'body' + ), + 'q' => array( + 'location' => 'query:test' + ), + 'h' => array( + 'location' => 'header:X-Custom' + ), + 'i' => array( + 'static' => 'test', + 'location' => 'query' + ), + // Data locations means the argument is just a placeholder for data + // that can be referenced by other arguments + 'data' => array( + 'location' => 'data' + ) + ) + )), + 'concrete' => new ApiCommand(array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'params' => array() + )) + ) + ); + $this->factory = new ServiceDescriptionFactory($this->service); + } + + /** + * @covers Guzzle\Service\Command\DynamicCommand + */ + public function testBuildsUsingPathParametersAndAppendSlashPrepend() + { + $client = new Client('http://www.example.com/'); + $client->setDescription($this->service); + + $command = $this->factory->factory('test_command', array( + 'bucket' => 'test', + 'key' => 'key' + )); + $request = $command->setClient($client)->prepare(); + + // Ensure that the path values were injected into the path and base_url + $this->assertEquals('/hi_key', $request->getPath()); + $this->assertEquals('www.example.com', $request->getHost()); + + // Check the complete request + $this->assertEquals( + "HEAD /hi_key HTTP/1.1\r\n" . + "Host: www.example.com\r\n" . + "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" . + "\r\n", (string) $request); + } + + /** + * @covers Guzzle\Service\Command\DynamicCommand + * @expectedException Guzzle\Service\Exception\ValidationException + */ + public function testValidatesArgs() + { + $client = new Client('http://www.fragilerock.com/'); + $client->setDescription($this->service); + $command = $this->factory->factory('test_command', array()); + $client->execute($command); + } + + /** + * @covers Guzzle\Service\Command\DynamicCommand + */ + public function testUsesDifferentLocations() + { + $client = new Client('http://www.tazmania.com/'); + $command = $this->factory->factory('body', array( + 'b' => 'my-data', + 'q' => 'abc', + 'h' => 'haha' + )); + + $request = $command->setClient($client)->prepare(); + + $this->assertEquals( + "PUT /?test=abc&i=test HTTP/1.1\r\n" . + "Host: www.tazmania.com\r\n" . + "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" . + "Expect: 100-Continue\r\n" . + "Content-Length: 29\r\n" . + "X-Custom: haha\r\n" . + "\r\n" . + "begin_body::my-data::end_body", (string) $request); + + unset($command); + unset($request); + + $command = $this->factory->factory('body', array( + 'b' => 'my-data', + 'q' => 'abc', + 'h' => 'haha', + 'i' => 'does not change the value because it\'s static' + )); + + $request = $command->setClient($client)->prepare(); + + $this->assertEquals( + "PUT /?test=abc&i=test HTTP/1.1\r\n" . + "Host: www.tazmania.com\r\n" . + "User-Agent: " . Utils::getDefaultUserAgent() . "\r\n" . + "Expect: 100-Continue\r\n" . + "Content-Length: 29\r\n" . + "X-Custom: haha\r\n" . + "\r\n" . + "begin_body::my-data::end_body", (string) $request); + } + + /** + * @covers Guzzle\Service\Command\DynamicCommand::build + */ + public function testBuildsConcreteCommands() + { + $c = $this->factory->factory('concrete'); + $this->assertEquals('Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', get_class($c)); + } + + /** + * @covers Guzzle\Service\Command\DynamicCommand::build + */ + public function testUsesAbsolutePaths() + { + $service = new ServiceDescription( + array( + 'test_path' => new ApiCommand(array( + 'method' => 'GET', + 'uri' => '/test', + )) + ) + ); + + $client = new Client('http://www.test.com/'); + $client->setDescription($service); + $command = $client->getCommand('test_path'); + $request = $command->prepare(); + $this->assertEquals('/test', $request->getPath()); + } + + /** + * @covers Guzzle\Service\Command\DynamicCommand::build + */ + public function testUsesRelativePaths() + { + $service = new ServiceDescription( + array( + 'test_path' => new ApiCommand(array( + 'method' => 'GET', + 'uri' => 'test/abc', + )) + ) + ); + + $client = new Client('http://www.test.com/api/v2'); + $client->setDescription($service); + $command = $client->getCommand('test_path'); + $request = $command->prepare(); + $this->assertEquals('/api/v2/test/abc', $request->getPath()); + } + + /** + * @covers Guzzle\Service\Command\DynamicCommand::build + */ + public function testAllowsPostFieldsAndFiles() + { + $service = new ServiceDescription( + array( + 'post_command' => new ApiCommand(array( + 'method' => 'POST', + 'uri' => '/key', + 'params' => array( + 'test' => array( + 'location' => 'post_field' + ), + 'test_2' => array( + 'location' => 'post_field:foo' + ), + 'test_3' => array( + 'location' => 'post_file' + ) + ) + )) + ) + ); + + $client = new Client('http://www.test.com/api/v2'); + $client->setDescription($service); + + $command = $client->getCommand('post_command', array( + 'test' => 'Hi!', + 'test_2' => 'There', + 'test_3' => __FILE__ + )); + $request = $command->prepare(); + $this->assertEquals('Hi!', $request->getPostField('test')); + $this->assertEquals('There', $request->getPostField('foo')); + $this->assertInternalType('array', $request->getPostFile('test_3')); + + $command = $client->getCommand('post_command', array( + 'test_3' => new PostFile('baz', __FILE__) + )); + $request = $command->prepare(); + $this->assertInternalType('array', $request->getPostFile('baz')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php new file mode 100644 index 0000000..6779d2a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php @@ -0,0 +1,74 @@ +client = new Client(); + + $map = new MapFactory(array( + 'test' => 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + $this->factory = new AliasFactory($this->client, array( + 'foo' => 'test', + 'bar' => 'sub', + 'sub' => 'test1', + 'krull' => 'test3', + 'krull_2' => 'krull', + 'sub_2' => 'bar', + 'bad_link' => 'jarjar' + )); + + $map2 = new MapFactory(array( + 'test3' => 'Guzzle\Tests\Service\Mock\Command\Sub\Sub' + )); + + $this->client->setCommandFactory(new CompositeFactory(array($map, $this->factory, $map2))); + } + + public function aliasProvider() + { + return array( + array('foo', 'Guzzle\Tests\Service\Mock\Command\MockCommand', false), + array('bar', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('sub_2', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', false), + array('krull', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('krull_2', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', false), + array('missing', null, true), + array('bad_link', null, true) + ); + } + + /** + * @covers Guzzle\Service\Command\Factory\AliasFactory + * @dataProvider aliasProvider + */ + public function testAliasesCommands($key, $result, $exception) + { + try { + $command = $this->client->getCommand($key); + if (is_null($result)) { + $this->assertNull($command); + } else { + $this->assertInstanceof($result, $command); + } + } catch (\Exception $e) { + if (!$exception) { + $this->fail('Got exception when it was not expected'); + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php new file mode 100644 index 0000000..3c7cb5a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php @@ -0,0 +1,142 @@ +getMockBuilder($class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @covers Guzzle\Service\Command\Factory\CompositeFactory::getIterator + * @covers Guzzle\Service\Command\Factory\CompositeFactory::count + */ + public function testIsIterable() + { + $factory = new CompositeFactory(array($this->getFactory(), $this->getFactory())); + $this->assertEquals(2, count($factory)); + $this->assertEquals(2, count(iterator_to_array($factory->getIterator()))); + } + + /** + * @covers Guzzle\Service\Command\Factory\CompositeFactory::find + * @covers Guzzle\Service\Command\Factory\CompositeFactory::has + */ + public function testFindsFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factory = new CompositeFactory(array($f1, $f2)); + $this->assertNull($factory->find('foo')); + $this->assertNull($factory->find($this->getFactory())); + $this->assertSame($f1, $factory->find('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertSame($f2, $factory->find('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + $this->assertSame($f1, $factory->find($f1)); + $this->assertSame($f2, $factory->find($f2)); + + $this->assertFalse($factory->has('foo')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\MapFactory')); + $this->assertTrue($factory->has('Guzzle\\Service\\Command\\Factory\\CompositeFactory')); + } + + /** + * @covers Guzzle\Service\Command\Factory\CompositeFactory::factory + * @covers Guzzle\Service\Command\Factory\CompositeFactory::__construct + */ + public function testCreatesCommands() + { + $factory = new CompositeFactory(); + $this->assertNull($factory->factory('foo')); + + $f1 = $this->getFactory(); + $mockCommand1 = $this->getMockForAbstractClass('Guzzle\\Service\\Command\\AbstractCommand'); + + $f1->expects($this->once()) + ->method('factory') + ->with($this->equalTo('foo')) + ->will($this->returnValue($mockCommand1)); + + $factory = new CompositeFactory(array($f1)); + $this->assertSame($mockCommand1, $factory->factory('foo')); + } + + /** + * @covers Guzzle\Service\Command\Factory\CompositeFactory::remove + */ + public function testAllowsRemovalOfFactories() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $factories = array($f1, $f2, $f3); + $factory = new CompositeFactory($factories); + + $factory->remove('foo'); + $this->assertEquals($factories, $factory->getIterator()->getArrayCopy()); + + $factory->remove($f1); + $this->assertEquals(array($f2, $f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\MapFactory'); + $this->assertEquals(array($f3), $factory->getIterator()->getArrayCopy()); + + $factory->remove('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + + $factory->remove('foo'); + $this->assertEquals(array(), $factory->getIterator()->getArrayCopy()); + } + + /** + * @covers Guzzle\Service\Command\Factory\CompositeFactory::add + */ + public function testAddsFactoriesBeforeAndAtEnd() + { + $f1 = $this->getFactory(); + $f2 = $this->getFactory(); + $f3 = $this->getFactory('Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $f4 = $this->getFactory(); + + $factory = new CompositeFactory(); + + $factory->add($f1); + $this->assertEquals(array($f1), $factory->getIterator()->getArrayCopy()); + + $factory->add($f2); + $this->assertEquals(array($f1, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f3, $f2); + $this->assertEquals(array($f1, $f3, $f2), $factory->getIterator()->getArrayCopy()); + + $factory->add($f4, 'Guzzle\\Service\\Command\\Factory\\CompositeFactory'); + $this->assertEquals(array($f1, $f4, $f3, $f2), $factory->getIterator()->getArrayCopy()); + } + + /** + * @covers Guzzle\Service\Command\Factory\CompositeFactory::getDefaultChain + */ + public function testProvidesDefaultChainForClients() + { + $client = $this->getMock('Guzzle\\Service\\Client'); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(1, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[0]); + + $description = $this->getMock('Guzzle\\Service\\Description\\ServiceDescription'); + $client->expects($this->once()) + ->method('getDescription') + ->will($this->returnValue($description)); + $chain = CompositeFactory::getDefaultChain($client); + $a = $chain->getIterator()->getArrayCopy(); + $this->assertEquals(2, count($a)); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ServiceDescriptionFactory', $a[0]); + $this->assertInstanceOf('Guzzle\\Service\\Command\\Factory\\ConcreteClassFactory', $a[1]); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php new file mode 100644 index 0000000..8e987da --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php @@ -0,0 +1,47 @@ + $prefix + )); + } + + $factory = new ConcreteClassFactory($client); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php new file mode 100644 index 0000000..46e19d8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php @@ -0,0 +1,35 @@ + 'Guzzle\Tests\Service\Mock\Command\MockCommand', + 'test1' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + )); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php new file mode 100644 index 0000000..633c3db --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php @@ -0,0 +1,63 @@ +getDescription(); + + $factory = new ServiceDescriptionFactory($d); + $this->assertSame($d, $factory->getServiceDescription()); + + if (is_null($result)) { + $this->assertNull($factory->factory($key)); + } else { + $this->assertInstanceof($result, $factory->factory($key)); + } + } + + public function testUsesInflectionIfNoExactMatch() + { + $d = $this->getDescription(); + $factory = new ServiceDescriptionFactory($d, new Inflector()); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('binks')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('JarJar')); + $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\MockCommand', $factory->factory('jar_jar')); + } + + protected function getDescription() + { + return ServiceDescription::factory(array( + 'commands' => array( + 'jar_jar' => array( + 'class' => 'Guzzle\Tests\Service\Mock\Command\MockCommand' + ), + 'binks' => array( + 'class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand' + ) + ) + )); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ApiCommandTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ApiCommandTest.php new file mode 100644 index 0000000..710d130 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ApiCommandTest.php @@ -0,0 +1,332 @@ + 'test', + 'doc' => 'doc', + 'doc_url' => 'http://www.example.com', + 'method' => 'POST', + 'uri' => '/api/v1', + 'result_type' => 'array', + 'result_doc' => 'returns the json_decoded response', + 'deprecated' => true, + 'params' => array( + 'key' => array( + 'required' => 'true', + 'type' => 'string', + 'max_length' => 10 + ), + 'key_2' => array( + 'required' => 'true', + 'type' => 'integer', + 'default' => 10 + ) + ) + )); + + $this->assertEquals('test', $c->getName()); + $this->assertEquals('doc', $c->getDoc()); + $this->assertEquals('http://www.example.com', $c->getDocUrl()); + $this->assertEquals('POST', $c->getMethod()); + $this->assertEquals('/api/v1', $c->getUri()); + $this->assertEquals('array', $c->getResultType()); + $this->assertEquals('returns the json_decoded response', $c->getResultDoc()); + $this->assertTrue($c->isDeprecated()); + $this->assertEquals('Guzzle\\Service\\Command\\DynamicCommand', $c->getConcreteClass()); + $this->assertEquals(array( + 'key' => new ApiParam(array( + 'name' => 'key', + 'required' => 'true', + 'type' => 'string', + 'max_length' => 10 + )), + 'key_2' => new ApiParam(array( + 'name' => 'key_2', + 'required' => 'true', + 'type' => 'integer', + 'default' => 10 + )) + ), $c->getParams()); + + $this->assertEquals(new ApiParam(array( + 'name' => 'key_2', + 'required' => 'true', + 'type' => 'integer', + 'default' => 10 + )), $c->getParam('key_2')); + + $this->assertNull($c->getParam('afefwef')); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::__construct + */ + public function testAllowsConcreteCommands() + { + $c = new ApiCommand(array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'params' => array( + 'p' => new ApiParam(array( + 'name' => 'foo' + )) + ) + )); + $this->assertEquals('Guzzle\\Service\\Command\ClosureCommand', $c->getConcreteClass()); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::toArray + */ + public function testConvertsToArray() + { + $data = array( + 'name' => 'test', + 'class' => 'Guzzle\\Service\\Command\ClosureCommand', + 'doc' => 'test', + 'doc_url' => 'http://www.example.com', + 'method' => 'PUT', + 'uri' => '/', + 'params' => array( + 'p' => new ApiParam(array( + 'name' => 'foo' + )) + ), + 'result_type' => null, + 'result_doc' => null, + 'deprecated' => false + ); + $c = new ApiCommand($data); + $this->assertEquals($data, $c->toArray()); + } + + /** + * Clear the class cache of the ApiCommand static factory method + */ + protected function clearCommandCache() + { + $refObject = new \ReflectionClass('Guzzle\Service\Description\ApiCommand'); + $refProperty = $refObject->getProperty('apiCommandCache'); + $refProperty->setAccessible(true); + $refProperty->setValue(null, array()); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::fromCommand + */ + public function testDoesNotErrorWhenNoAnnotationsArePresent() + { + $this->clearCommandCache(); + + $command = ApiCommand::fromCommand('Guzzle\\Tests\\Service\\Mock\\Command\\Sub\\Sub'); + $this->assertEquals(array(), $command->getParams()); + + // Ensure that the cache returns the same value + $this->assertSame($command, ApiCommand::fromCommand('Guzzle\\Tests\\Service\\Mock\\Command\\Sub\\Sub')); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::fromCommand + */ + public function testBuildsApiParamFromClassDocBlock() + { + $command = ApiCommand::fromCommand('Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand'); + $this->assertEquals(3, count($command->getParams())); + + $this->assertTrue($command->getParam('test')->getRequired()); + $this->assertEquals('123', $command->getParam('test')->getDefault()); + $this->assertEquals('Test argument', $command->getParam('test')->getDoc()); + + $this->assertEquals('abc', $command->getParam('_internal')->getDefault()); + } + + protected function getApiCommand() + { + return ApiCommand::fromCommand(get_class($this)); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + */ + public function testAddsDefaultAndInjectsConfigs() + { + $col = new Collection(array( + 'username' => 'user', + 'string' => 'test', + 'float' => 1.23 + )); + + $this->getApiCommand()->validate($col); + $this->assertEquals(false, $col->get('bool_2')); + $this->assertEquals('user_test_', $col->get('dynamic')); + $this->assertEquals(1.23, $col->get('float')); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + * @expectedException Guzzle\Service\Exception\ValidationException + */ + public function testValidatesTypeHints() + { + $this->getApiCommand()->validate(new Collection(array( + 'test' => 'uh oh', + 'username' => 'test' + ))); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + */ + public function testConvertsBooleanDefaults() + { + $c = new Collection(array( + 'test' => $this, + 'username' => 'test' + )); + + $this->getApiCommand()->validate($c); + $this->assertTrue($c->get('bool_1')); + $this->assertFalse($c->get('bool_2')); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + */ + public function testValidatesArgs() + { + $config = new Collection(array( + 'data' => 123, + 'min' => 'a', + 'max' => 'aaa' + )); + + $command = new ApiCommand(array( + 'params' => array( + 'data' => new ApiParam(array( + 'type' => 'string' + )), + 'min' => new ApiParam(array( + 'type' => 'string', + 'min_length' => 2 + )), + 'max' => new ApiParam(array( + 'type' => 'string', + 'max_length' => 2 + )) + ) + )); + + try { + $command->validate($config); + $this->fail('Did not throw expected exception'); + } catch (ValidationException $e) { + $concat = implode("\n", $e->getErrors()); + $this->assertContains("Value must be of type string", $concat); + $this->assertContains("Requires that the min argument be >= 2 characters", $concat); + $this->assertContains("Requires that the max argument be <= 2 characters", $concat); + } + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + */ + public function testRunsValuesThroughFilters() + { + $data = new Collection(array( + 'username' => 'TEST', + 'test_function' => 'foo' + )); + + $this->getApiCommand()->validate($data); + $this->assertEquals('test', $data->get('username')); + $this->assertEquals('FOO', $data->get('test_function')); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + */ + public function testTypeValidationCanBeDisabled() + { + $i = Inspector::getInstance(); + $i->setTypeValidation(false); + + $command = new ApiCommand(array( + 'params' => array( + 'data' => new ApiParam(array( + 'type' => 'string' + )) + ) + )); + + $command->validate(new Collection(array( + 'data' => new \stdClass() + )), $i); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + */ + public function testSkipsFurtherValidationIfNotSet() + { + $command = new ApiCommand(array( + 'params' => array( + 'data' => new ApiParam(array( + 'type' => 'string' + )) + ) + )); + + $command->validate(new Collection()); + } + + /** + * @covers Guzzle\Service\Description\ApiCommand::validate + * @expectedException Guzzle\Service\Exception\ValidationException + * @expectedExceptionMessage Validation errors: Requires that the data argument be supplied. + */ + public function testValidatesRequiredFieldsAreSet() + { + $command = new ApiCommand(array( + 'params' => array( + 'data' => new ApiParam(array( + 'required' => true + )) + ) + )); + + $command->validate(new Collection()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ApiParamTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ApiParamTest.php new file mode 100644 index 0000000..b2784a8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ApiParamTest.php @@ -0,0 +1,154 @@ + 'foo', + 'type' => 'bar', + 'type_args' => null, + 'required' => true, + 'default' => '123', + 'doc' => '456', + 'min_length' => 2, + 'max_length' => 5, + 'location' => 'body', + 'location_key' => null, + 'static' => 'static!', + 'prepend' => 'before.', + 'append' => '.after', + 'filters' => 'trim,json_encode' + ); + + public function testCreatesParamFromArray() + { + $p = new ApiParam($this->data); + $this->assertEquals('foo', $p->getName()); + $this->assertEquals('bar', $p->getType()); + $this->assertEquals(true, $p->getRequired()); + $this->assertEquals('123', $p->getDefault()); + $this->assertEquals('456', $p->getDoc()); + $this->assertEquals(2, $p->getMinLength()); + $this->assertEquals(5, $p->getMaxLength()); + $this->assertEquals('body', $p->getLocation()); + $this->assertEquals('static!', $p->getStatic()); + $this->assertEquals('before.', $p->getPrepend()); + $this->assertEquals('.after', $p->getAppend()); + $this->assertEquals(array('trim', 'json_encode'), $p->getFilters()); + } + + public function testCanConvertToArray() + { + $p = new ApiParam($this->data); + $this->assertEquals($this->data, $p->toArray()); + } + + public function testFromArrayConvertsBooleans() + { + $d = $this->data; + + $d['required'] = 'false'; + $p = new ApiParam($d); + $this->assertEquals(false, $p->getRequired()); + + $d['required'] = 'true'; + $p = new ApiParam($d); + $this->assertEquals(true, $p->getRequired()); + } + + public function testUsesStatic() + { + $d = $this->data; + $d['static'] = 'foo'; + $p = new ApiParam($d); + $this->assertEquals('foo', $p->getValue('bar')); + } + + public function testUsesDefault() + { + $d = $this->data; + $d['default'] = 'foo'; + $d['static'] = null; + $p = new ApiParam($d); + $this->assertEquals('foo', $p->getValue(null)); + } + + public function testConvertsBooleanValues() + { + $d = $this->data; + + $d['static'] = 'true'; + $p = new ApiParam($d); + $this->assertEquals(true, $p->getValue(null)); + + $d['static'] = 'false'; + $p = new ApiParam($d); + $this->assertEquals(false, $p->getValue(null)); + } + + public function testReturnsYourValue() + { + $d = $this->data; + $d['static'] = null; + $p = new ApiParam($d); + $this->assertEquals('foo', $p->getValue('foo')); + } + + public function testFiltersValues() + { + $d = $this->data; + $d['static'] = null; + $d['filters'] = 'strtoupper'; + $p = new ApiParam($d); + $this->assertEquals('FOO', $p->filter('foo')); + } + + public function testUsesArrayByDefaultForFilters() + { + $d = $this->data; + $d['filters'] = null; + $p = new ApiParam($d); + $this->assertEquals(array(), $p->getFilters()); + } + + public function testParsesLocationValue() + { + $p = new ApiParam(array( + 'location' => 'foo:bar' + )); + $this->assertEquals('foo', $p->getLocation()); + $this->assertEquals('bar', $p->getLocationKey()); + } + + public function testParsesTypeValues() + { + $p = new ApiParam(array( + 'type' => 'foo:baz,bar,boo' + )); + $this->assertEquals('foo', $p->getType()); + $this->assertEquals(array('baz', 'bar', 'boo'), $p->getTypeArgs()); + } + + public function testAllowsExplicitTypeArgs() + { + $p = new ApiParam(array( + 'type' => 'foo', + 'type_args' => array('baz', 'bar', 'boo') + )); + $this->assertEquals('foo', $p->getType()); + $this->assertEquals(array('baz', 'bar', 'boo'), $p->getTypeArgs()); + + $p = new ApiParam(array( + 'type' => 'foo', + 'type_args' => 'baz' + )); + $this->assertEquals('foo', $p->getType()); + $this->assertEquals(array('baz'), $p->getTypeArgs()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ArrayDescriptionBuilderTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ArrayDescriptionBuilderTest.php new file mode 100644 index 0000000..7ab040c --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ArrayDescriptionBuilderTest.php @@ -0,0 +1,95 @@ + array( + 'abstract' => array( + 'method' => 'GET', + 'params' => array( + 'test' => array( + 'type' => 'string', + 'required' => true + ) + ) + ), + 'abstract2' => array( + 'uri' => '/test', + 'extends' => 'abstract' + ), + 'concrete' => array( + 'extends' => 'abstract2' + ) + ) + )); + + $c = $d->getCommand('concrete'); + $this->assertEquals('/test', $c->getUri()); + $this->assertEquals('GET', $c->getMethod()); + $params = $c->getParams(); + $param = $params['test']; + $this->assertEquals('string', $param->getType()); + $this->assertTrue($param->getRequired()); + } + + /** + * @covers Guzzle\Service\Description\ServiceDescription::factory + * @covers Guzzle\Service\Description\ArrayDescriptionBuilder::build + * @expectedException RuntimeException + */ + public function testThrowsExceptionWhenExtendingMissingCommand() + { + ServiceDescription::factory(array( + 'commands' => array( + 'concrete' => array( + 'extends' => 'missing' + ) + ) + )); + } + + /** + * @covers Guzzle\Service\Description\ArrayDescriptionBuilder::build + */ + public function testRegistersCustomTypes() + { + ServiceDescription::factory(array( + 'types' => array( + 'foo' => array( + 'class' => 'Guzzle\\Common\\Validation\\Regex', + 'pattern' => '/[0-9]+/' + ) + ) + )); + + $valid = Inspector::getInstance()->validateConstraint('foo', 'abc'); + $this->assertEquals('abc does not match the regular expression', $valid); + } + + /** + * @covers Guzzle\Service\Description\ArrayDescriptionBuilder::build + * @expectedException RuntimeException + * @expectedExceptionMessage Custom types require a class attribute + */ + public function testCustomTypesRequireClassAttribute() + { + ServiceDescription::factory(array( + 'types' => array( + 'slug' => array() + ) + )); + } + +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/JsonDescriptionBuilderTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/JsonDescriptionBuilderTest.php new file mode 100644 index 0000000..ada16c5 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/JsonDescriptionBuilderTest.php @@ -0,0 +1,31 @@ +build('/foo.does.not.exist'); + } + + public function testBuildsServiceDescriptions() + { + $j = new JsonDescriptionBuilder(); + $description = $j->build(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json'); + $this->assertTrue($description->hasCommand('test')); + $test = $description->getCommand('test'); + $this->assertEquals('/path', $test->getUri()); + $test = $description->getCommand('concrete'); + $this->assertEquals('/abstract', $test->getUri()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionAbstractFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionAbstractFactoryTest.php new file mode 100644 index 0000000..ab0a4ed --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionAbstractFactoryTest.php @@ -0,0 +1,36 @@ +xmlFile = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'; + $this->jsonFile = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json'; + } + + public function testFactoryDelegatesToConcreteFactories() + { + $factory = new ServiceDescriptionAbstractFactory(); + $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', $factory->build($this->xmlFile)); + $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', $factory->build($this->jsonFile)); + } + + /** + * @expectedException Guzzle\Service\Exception\DescriptionBuilderException + */ + public function testFactoryEnsuresItCanHandleTheTypeOfFileOrArray() + { + $factory = new ServiceDescriptionAbstractFactory(); + $factory->build('jarJarBinks'); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php new file mode 100644 index 0000000..ed9b64e --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php @@ -0,0 +1,103 @@ +serviceData = array( + 'test_command' => new ApiCommand(array( + 'doc' => 'documentationForCommand', + 'method' => 'DELETE', + 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', + 'params' => array( + 'bucket' => array( + 'required' => true + ), + 'key' => array( + 'required' => true + ) + ) + )) + ); + } + + /** + * @covers Guzzle\Service\Description\ServiceDescription::factory + * @covers Guzzle\Service\Description\ArrayDescriptionBuilder::build + */ + public function testFactoryDelegatesToConcreteFactories() + { + $xmlFile = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'; + $jsonFile = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.json'; + $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($xmlFile)); + $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($jsonFile)); + } + + /** + * @covers Guzzle\Service\Description\ServiceDescription + * @covers Guzzle\Service\Description\ServiceDescription::__construct + * @covers Guzzle\Service\Description\ServiceDescription::getCommands + * @covers Guzzle\Service\Description\ServiceDescription::getCommand + */ + public function testConstructor() + { + $service = new ServiceDescription($this->serviceData); + + $this->assertEquals(1, count($service->getCommands())); + $this->assertFalse($service->hasCommand('foobar')); + $this->assertTrue($service->hasCommand('test_command')); + } + + /** + * @covers Guzzle\Service\Description\ServiceDescription::serialize + * @covers Guzzle\Service\Description\ServiceDescription::unserialize + */ + public function testIsSerializable() + { + $service = new ServiceDescription($this->serviceData); + + $data = serialize($service); + $d2 = unserialize($data); + $this->assertEquals($service, $d2); + } + + public function testAllowsForJsonBasedArrayParamsFunctionalTest() + { + $service = array( + 'test' => new ApiCommand(array( + 'method' => 'PUT', + 'params' => array( + 'data' => array( + 'required' => true, + 'type' => 'type:array', + 'filters' => 'json_encode', + 'location' => 'body' + ) + ) + )) + ); + + $description = new ServiceDescription($service); + $client = new Client(); + $client->setDescription($description); + $command = $client->getCommand('test', array( + 'data' => array( + 'foo' => 'bar' + ) + )); + + $request = $command->prepare(); + $this->assertEquals(json_encode(array( + 'foo' => 'bar' + )), (string) $request->getBody()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/XmlDescriptionBuilderTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/XmlDescriptionBuilderTest.php new file mode 100644 index 0000000..cf022a2 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Description/XmlDescriptionBuilderTest.php @@ -0,0 +1,66 @@ +build('file_not_found'); + } + + /** + * @covers Guzzle\Service\Description\XmlDescriptionBuilder + * @covers Guzzle\Service\Description\ServiceDescription + */ + public function testBuildsServiceUsingFile() + { + $x = new XmlDescriptionBuilder(); + $service = $x->build(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'); + $this->assertTrue($service->hasCommand('search')); + $this->assertTrue($service->hasCommand('test')); + $this->assertTrue($service->hasCommand('trends.location')); + $this->assertTrue($service->hasCommand('geo.id')); + $this->assertInstanceOf('Guzzle\\Service\\Description\\ApiCommand', $service->getCommand('search')); + $this->assertInternalType('array', $service->getCommands()); + $this->assertEquals(7, count($service->getCommands())); + $this->assertNull($service->getCommand('missing')); + + $command = $service->getCommand('test'); + $this->assertInstanceOf('Guzzle\\Service\\Description\\ApiCommand', $command); + $this->assertEquals('test', $command->getName()); + $this->assertInternalType('array', $command->getParams()); + + $this->assertEquals(array( + 'name' => 'bucket', + 'required' => true, + 'doc' => 'Bucket location' + ), array_filter($command->getParam('bucket')->toArray())); + + $this->assertEquals('DELETE', $command->getMethod()); + $this->assertEquals('{ bucket }/{ key }{ format }', $command->getUri()); + $this->assertEquals('Documentation', $command->getDoc()); + + $this->assertArrayHasKey('custom_filter', Inspector::getInstance()->getRegisteredConstraints()); + } + + /** + * @covers Guzzle\Service\Description\XmlDescriptionBuilder + */ + public function testCanExtendOtherFiles() + { + $x = new XmlDescriptionBuilder(); + $service = $x->build(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'TestData' . DIRECTORY_SEPARATOR . 'test_service.xml'); + $command = $service->getCommand('concrete'); + $this->assertEquals('/test', $command->getUri()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php new file mode 100644 index 0000000..6455295 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php @@ -0,0 +1,15 @@ +assertEquals($items, $e->getCommands()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php new file mode 100644 index 0000000..ef789d8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php @@ -0,0 +1,17 @@ +setErrors($errors); + $this->assertEquals($errors, $e->getErrors()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/InspectorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/InspectorTest.php new file mode 100644 index 0000000..1c03360 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/InspectorTest.php @@ -0,0 +1,96 @@ +assertTrue($i->getTypeValidation()); + $i->setTypeValidation(false); + $this->assertFalse($i->getTypeValidation()); + } + + /** + * @cover Guzzle\Service\Inspector::__constructor + */ + public function testRegistersDefaultFilters() + { + $inspector = new Inspector(); + $this->assertNotEmpty($inspector->getRegisteredConstraints()); + } + + /** + * @covers Guzzle\Service\Inspector + * @expectedException InvalidArgumentException + */ + public function testChecksFilterValidity() + { + Inspector::getInstance()->getConstraint('foooo'); + } + + /** + * @covers Guzzle\Service\Inspector::prepareConfig + */ + public function testPreparesConfig() + { + $c = Inspector::prepareConfig(array( + 'a' => '123', + 'base_url' => 'http://www.test.com/' + ), array( + 'a' => 'xyz', + 'b' => 'lol' + ), array('a')); + + $this->assertInstanceOf('Guzzle\Common\Collection', $c); + $this->assertEquals(array( + 'a' => '123', + 'b' => 'lol', + 'base_url' => 'http://www.test.com/' + ), $c->getAll()); + + try { + $c = Inspector::prepareConfig(null, null, array('a')); + $this->fail('Exception not throw when missing config'); + } catch (ValidationException $e) { + } + } + + /** + * @covers Guzzle\Service\Inspector::registerConstraint + * @covers Guzzle\Service\Inspector::getConstraint + * @covers Guzzle\Service\Inspector::getRegisteredConstraints + */ + public function testRegistersCustomConstraints() + { + $constraintClass = 'Guzzle\\Common\\Validation\\Ip'; + + Inspector::getInstance()->registerConstraint('mock', $constraintClass); + Inspector::getInstance()->registerConstraint('mock_2', $constraintClass, array( + 'version' => '4' + )); + + $this->assertArrayHasKey('mock', Inspector::getInstance()->getRegisteredConstraints()); + $this->assertArrayHasKey('mock_2', Inspector::getInstance()->getRegisteredConstraints()); + + $this->assertInstanceOf($constraintClass, Inspector::getInstance()->getConstraint('mock')); + $this->assertInstanceOf($constraintClass, Inspector::getInstance()->getConstraint('mock_2')); + + $this->assertTrue(Inspector::getInstance()->validateConstraint('mock', '192.168.16.121')); + $this->assertTrue(Inspector::getInstance()->validateConstraint('mock_2', '10.1.1.0')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/JsonLoaderTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/JsonLoaderTest.php new file mode 100644 index 0000000..219d1a2 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/JsonLoaderTest.php @@ -0,0 +1,45 @@ +parseJsonFile('fooooooo!'); + } + + /** + * @expectedException Guzzle\Service\Exception\JsonException + * @expectedExceptionMessage Error loading JSON data from + */ + public function testJsonMustBeValue() + { + $loader = new JsonLoader(); + $loader->parseJsonFile(__FILE__); + } + + public function testFactoryCanCreateFromJson() + { + $file = dirname(__DIR__) . '/TestData/services/json1.json'; + $loader = new JsonLoader(); + $data = $loader->parseJsonFile($file); + + $this->assertArrayHasKey('includes', $data); + $this->assertArrayHasKey('services', $data); + $this->assertInternalType('array', $data['services']['foo']); + $this->assertInternalType('array', $data['services']['abstract']); + $this->assertInternalType('array', $data['services']['mock']); + $this->assertEquals('bar', $data['services']['foo']['params']['baz']); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php new file mode 100644 index 0000000..167a19f --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php @@ -0,0 +1,27 @@ +request = $this->client->createRequest('GET'); + + // Add the next token and page size query string values + $this->request->getQuery()->set('next_token', $this->get('next_token')); + + if ($this->get('page_size')) { + $this->request->getQuery()->set('page_size', $this->get('page_size')); + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php new file mode 100644 index 0000000..fefc36d --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php @@ -0,0 +1,21 @@ +request = $this->client->createRequest(); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php new file mode 100644 index 0000000..4d6594d --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php @@ -0,0 +1,22 @@ +request = $this->client->getRequest('HEAD'); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php new file mode 100644 index 0000000..db152e5 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php @@ -0,0 +1,12 @@ + '{{scheme}}://127.0.0.1:8124/{{api_version}}/{{subdomain}}', + 'scheme' => 'http', + 'api_version' => 'v1' + ), array('username', 'password', 'subdomain')); + + return new self($config->get('base_url'), $config); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php new file mode 100644 index 0000000..ccea5e7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php @@ -0,0 +1,24 @@ +nextToken) { + $this->command->set('next_token', $this->nextToken); + } + + $this->command->set('page_size', (int) $this->calculatePageSize()); + $this->command->execute(); + + $data = json_decode($this->command->getResponse()->getBody(true), true); + + $this->nextToken = $data['next_token']; + + return $data['resources']; + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Plugin/PluginCollectionPluginTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Plugin/PluginCollectionPluginTest.php new file mode 100644 index 0000000..69646e3 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Plugin/PluginCollectionPluginTest.php @@ -0,0 +1,44 @@ + array( + 'class' => 'Guzzle\\Tests\\Service\\Mock\\MockClient', + 'params' => array( + 'base_url' => 'http://www.test.com/', + 'subdomain' => 'michael', + 'password' => 'test', + 'username' => 'michael', + ) + ) + )); + + $plugin = $this->getMock('Symfony\Component\EventDispatcher\EventSubscriberInterface'); + + $plugin::staticExpects($this->any()) + ->method('getSubscribedEvents') + ->will($this->returnValue(array('client.create_request' => 'onRequestCreate'))); + + $s->addSubscriber(new PluginCollectionPlugin(array($plugin))); + + $c = $s->get('michael.mock'); + $this->assertTrue($c->getEventDispatcher()->hasListeners('client.create_request')); + + $listeners = $c->getEventDispatcher()->getListeners('client.create_request'); + $this->assertSame($plugin, $listeners[0][0]); + $this->assertEquals('onRequestCreate', $listeners[0][1]); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorApplyBatchedTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorApplyBatchedTest.php new file mode 100644 index 0000000..039b6d8 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorApplyBatchedTest.php @@ -0,0 +1,59 @@ +assertInternalType('array', ResourceIteratorApplyBatched::getAllEvents()); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIteratorApplyBatched + */ + public function testSendsRequestsForNextSetOfResources() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3, + 'limit' => 7 + )); + + $received = array(); + $apply = new ResourceIteratorApplyBatched($ri, function(ResourceIterator $i, array $batch) use (&$received) { + $received[] = $batch; + }); + + $apply->apply(3); + + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(1, $requests[2]->getQuery()->get('page_size')); + + $this->assertEquals(array('d', 'e', 'f'), array_values($received[0])); + $this->assertEquals(array('g', 'h', 'i'), array_values($received[1])); + $this->assertEquals(array('j'), array_values($received[2])); + + $this->assertEquals(3, $apply->getBatchCount()); + $this->assertEquals(7, $apply->getIteratedCount()); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php new file mode 100644 index 0000000..4fd31d0 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php @@ -0,0 +1,33 @@ +build('foo'); + } + + public function testBuildsResourceIterators() + { + $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); + $command = new MockCommand(); + $iterator = $factory->build($command, array( + 'client.namespace' => 'Guzzle\Tests\Service\Mock' + )); + + $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php new file mode 100644 index 0000000..3d8cbb7 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php @@ -0,0 +1,197 @@ +assertInternalType('array', ResourceIterator::getAllEvents()); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIterator + */ + public function testConstructorConfiguresDefaults() + { + $ri = $this->getMockForAbstractClass('Guzzle\\Service\\Resource\\ResourceIterator', array( + $this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), + array( + 'limit' => 10, + 'page_size' => 3 + ) + ), 'MockIterator'); + + $this->assertEquals(false, $ri->getNextToken()); + $this->assertEquals(false, $ri->current()); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIterator + */ + public function testSendsRequestsForNextSetOfResources() + { + // Queue up an array of responses for iterating + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }" + )); + + // Create a new resource iterator using the IterableCommand mock + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3 + )); + + // Ensure that no requests have been sent yet + $this->assertEquals(0, count($this->getServer()->getReceivedRequests(false))); + + //$this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $ri->toArray(); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[2]->getQuery()->get('page_size')); + + // Reset and resend + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 41\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"j\"] }", + )); + + $d = array(); + reset($ri); + foreach ($ri as $data) { + $d[] = $data; + } + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $d); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIterator + */ + public function testCalculatesPageSize() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"g\", \"h\", \"i\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"j\", \"resources\": [\"j\", \"k\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command'), array( + 'page_size' => 3, + 'limit' => 7 + )); + + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $ri->toArray()); + $requests = $this->getServer()->getReceivedRequests(true); + $this->assertEquals(3, count($requests)); + $this->assertEquals(3, $requests[0]->getQuery()->get('page_size')); + $this->assertEquals(3, $requests[1]->getQuery()->get('page_size')); + $this->assertEquals(1, $requests[2]->getQuery()->get('page_size')); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIterator + */ + public function testUseAsArray() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\nContent-Length: 52\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"g\", \"h\", \"i\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the key is never < 0 + $this->assertEquals(0, $ri->key()); + $this->assertEquals(0, count($ri)); + + // Ensure that the iterator can be used as KVP array + $data = array(); + foreach ($ri as $key => $value) { + $data[$key] = $value; + } + + // Ensure that the iterate is countable + $this->assertEquals(6, count($ri)); + $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i'), $data); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIterator + */ + public function testBailsWhenSendReturnsNoResults() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"g\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + + // Ensure that the iterator can be used as KVP array + $data = $ri->toArray(); + + // Ensure that the iterate is countable + $this->assertEquals(3, count($ri)); + $this->assertEquals(array('d', 'e', 'f'), $data); + + $this->assertEquals(2, $ri->getRequestCount()); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIterator::set + * @covers Guzzle\Service\Resource\ResourceIterator::get + */ + public function testHoldsDataOptions() + { + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $this->assertNull($ri->get('foo')); + $this->assertSame($ri, $ri->set('foo', 'bar')); + $this->assertEquals('bar', $ri->get('foo')); + } + + /** + * @covers Guzzle\Service\Resource\ResourceIterator::setLimit + * @covers Guzzle\Service\Resource\ResourceIterator::setPageSize + */ + public function testSettingLimitOrPageSizeClearsData() + { + $this->getServer()->flush(); + $this->getServer()->enqueue(array( + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }", + "HTTP/1.1 200 OK\r\n\r\n{ \"next_token\": \"\", \"resources\": [\"d\", \"e\", \"f\"] }" + )); + + $ri = new MockCommandIterator($this->getServiceBuilder()->get('mock')->getCommand('iterable_command')); + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + + $ri->setLimit(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + + $ri->toArray(); + $this->assertNotEmpty($this->readAttribute($ri, 'resources')); + $ri->setPageSize(10); + $this->assertEmpty($this->readAttribute($ri, 'resources')); + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/FileBody.txt new file mode 100644 index 0000000..e69de29 diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response new file mode 100644 index 0000000..b6938a2 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/mock_response @@ -0,0 +1,3 @@ +HTTP/1.1 200 OK +Content-Length: 0 + diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json new file mode 100644 index 0000000..8fd2247 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json1.json @@ -0,0 +1,18 @@ +{ + "includes": [ "json2.json" ], + "services": { + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle.Tests.Service.Mock.MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json new file mode 100644 index 0000000..47d4b55 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/json2.json @@ -0,0 +1,11 @@ +{ + "services": { + "foo": { + "class": "Guzzle.Tests.Service.Mock.MockClient", + "extends": "abstract", + "params": { + "baz": "bar" + } + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/new_style.xml b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/new_style.xml new file mode 100644 index 0000000..a0c034d --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/new_style.xml @@ -0,0 +1,26 @@ + + + + + + Guzzle.Service.Builder.ServiceBuilder + + + + + + + + + + + + + + + + + + + + diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/old_style.xml b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/old_style.xml new file mode 100644 index 0000000..2c75858 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/old_style.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json new file mode 100644 index 0000000..bed29ce --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.json @@ -0,0 +1,15 @@ +{ + "abstract": { + "access_key": "xyz", + "secret": "abc" + }, + "mock": { + "class": "Guzzle.Tests.Service.Mock.MockClient", + "extends": "abstract", + "params": { + "username": "foo", + "password": "baz", + "subdomain": "bar" + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.xml b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.xml new file mode 100644 index 0000000..8fb2143 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/services/services.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json new file mode 100644 index 0000000..b981c6a --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.json @@ -0,0 +1,12 @@ +{ + "includes": [ "test_service2.json" ], + "types": {}, + "commands": { + "test": { + "uri": "/path" + }, + "concrete": { + "extends": "abstract" + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.xml b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.xml new file mode 100644 index 0000000..40be420 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + Documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json new file mode 100644 index 0000000..223147e --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.json @@ -0,0 +1,7 @@ +{ + "commands": { + "abstract": { + "uri": "/abstract" + } + } +} diff --git a/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.xml b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.xml new file mode 100644 index 0000000..f402d63 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/Guzzle/Tests/TestData/test_service2.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/core/vendor/guzzle/guzzle/tests/bootstrap.php b/core/vendor/guzzle/guzzle/tests/bootstrap.php new file mode 100644 index 0000000..aa919d6 --- /dev/null +++ b/core/vendor/guzzle/guzzle/tests/bootstrap.php @@ -0,0 +1,29 @@ +