File manager - Edit - /home/opticamezl/www/newok/guzzle.tar
Back
guzzle/LICENSE 0000604 00000002127 15173213565 0007077 0 ustar 00 Copyright (c) 2011 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. guzzle/CHANGELOG.md 0000604 00000136351 15173213565 0007712 0 ustar 00 # CHANGELOG ## 3.9.3 - 2015-03-18 * Ensuring Content-Length is not stripped from a request when it is `0`. * Added more information to stream wrapper exceptions. * Message parser will no longer throw warnings for malformed messages. * Giving a valid cache TTL when max-age is 0. ## 3.9.2 - 2014-09-10 * Retrying "Connection died, retrying a fresh connect" curl errors. * Automatically extracting the cacert from the phar in client constructor. * Added EntityBody support for OPTIONS requests. ## 3.9.1 - 2014-05-07 * Added a fix to ReadLimitEntityBody to ensure it doesn't infinitely loop. * Added a fix to the stream checksum function so that when the first read returns a falsey value, it still continues to consume the stream until EOF. ## 3.9.0 - 2014-04-23 * `null`, `false`, and `"_guzzle_blank_"` all now serialize as an empty value with no trailing "=". See dc1d824277. * No longer performing an MD5 check on the cacert each time the phar is used, but rather copying the cacert to the temp directory. * `"0"` can now be added as a URL path * Deleting cookies that are set to empty * If-Modified-Since is no longer unnecessarily added to the CachePlugin * Cookie path matching now follows RFC 6265 s5.1.4 * Updated service descriptions are now added to a service client's composite factory. * MockPlugin now throws an exception if the queue is empty. * Properly parsing URLs that start with "http" but are not absolute * Added the ability to configure the curl_multi_select timeout setting * OAuth parameters are now sorted using lexicographical byte value ordering * Fixing invalid usage of an out of range PHP feature in the ErrorResponsePlugin ## 3.8.1 -2014-01-28 * Bug: Always using GET requests when redirecting from a 303 response * Bug: CURLOPT_SSL_VERIFYHOST is now correctly set to false when setting `$certificateAuthority` to false in `Guzzle\Http\ClientInterface::setSslVerification()` * Bug: RedirectPlugin now uses strict RFC 3986 compliance when combining a base URL with a relative URL * Bug: The body of a request can now be set to `"0"` * Sending PHP stream requests no longer forces `HTTP/1.0` * Adding more information to ExceptionCollection exceptions so that users have more context, including a stack trace of each sub-exception * Updated the `$ref` attribute in service descriptions to merge over any existing parameters of a schema (rather than clobbering everything). * Merging URLs will now use the query string object from the relative URL (thus allowing custom query aggregators) * Query strings are now parsed in a way that they do no convert empty keys with no value to have a dangling `=`. For example `foo&bar=baz` is now correctly parsed and recognized as `foo&bar=baz` rather than `foo=&bar=baz`. * Now properly escaping the regular expression delimiter when matching Cookie domains. * Network access is now disabled when loading XML documents ## 3.8.0 - 2013-12-05 * Added the ability to define a POST name for a file * JSON response parsing now properly walks additionalProperties * cURL error code 18 is now retried automatically in the BackoffPlugin * Fixed a cURL error when URLs contain fragments * Fixed an issue in the BackoffPlugin retry event where it was trying to access all exceptions as if they were CurlExceptions * CURLOPT_PROGRESS function fix for PHP 5.5 (69fcc1e) * Added the ability for Guzzle to work with older versions of cURL that do not support `CURLOPT_TIMEOUT_MS` * Fixed a bug that was encountered when parsing empty header parameters * UriTemplate now has a `setRegex()` method to match the docs * The `debug` request parameter now checks if it is truthy rather than if it exists * Setting the `debug` request parameter to true shows verbose cURL output instead of using the LogPlugin * Added the ability to combine URLs using strict RFC 3986 compliance * Command objects can now return the validation errors encountered by the command * Various fixes to cache revalidation (#437 and 29797e5) * Various fixes to the AsyncPlugin * Cleaned up build scripts ## 3.7.4 - 2013-10-02 * Bug fix: 0 is now an allowed value in a description parameter that has a default value (#430) * Bug fix: SchemaFormatter now returns an integer when formatting to a Unix timestamp (see https://github.com/aws/aws-sdk-php/issues/147) * Bug fix: Cleaned up and fixed URL dot segment removal to properly resolve internal dots * Minimum PHP version is now properly specified as 5.3.3 (up from 5.3.2) (#420) * Updated the bundled cacert.pem (#419) * OauthPlugin now supports adding authentication to headers or query string (#425) ## 3.7.3 - 2013-09-08 * Added the ability to get the exception associated with a request/command when using `MultiTransferException` and `CommandTransferException`. * Setting `additionalParameters` of a response to false is now honored when parsing responses with a service description * Schemas are only injected into response models when explicitly configured. * No longer guessing Content-Type based on the path of a request. Content-Type is now only guessed based on the path of an EntityBody. * Bug fix: ChunkedIterator can now properly chunk a \Traversable as well as an \Iterator. * Bug fix: FilterIterator now relies on `\Iterator` instead of `\Traversable`. * Bug fix: Gracefully handling malformed responses in RequestMediator::writeResponseBody() * Bug fix: Replaced call to canCache with canCacheRequest in the CallbackCanCacheStrategy of the CachePlugin * Bug fix: Visiting XML attributes first before visting XML children when serializing requests * Bug fix: Properly parsing headers that contain commas contained in quotes * Bug fix: mimetype guessing based on a filename is now case-insensitive ## 3.7.2 - 2013-08-02 * Bug fix: Properly URL encoding paths when using the PHP-only version of the UriTemplate expander See https://github.com/guzzle/guzzle/issues/371 * Bug fix: Cookie domains are now matched correctly according to RFC 6265 See https://github.com/guzzle/guzzle/issues/377 * Bug fix: GET parameters are now used when calculating an OAuth signature * Bug fix: Fixed an issue with cache revalidation where the If-None-Match header was being double quoted * `Guzzle\Common\AbstractHasDispatcher::dispatch()` now returns the event that was dispatched * `Guzzle\Http\QueryString::factory()` now guesses the most appropriate query aggregator to used based on the input. See https://github.com/guzzle/guzzle/issues/379 * Added a way to add custom domain objects to service description parsing using the `operation.parse_class` event. See https://github.com/guzzle/guzzle/pull/380 * cURL multi cleanup and optimizations ## 3.7.1 - 2013-07-05 * Bug fix: Setting default options on a client now works * Bug fix: Setting options on HEAD requests now works. See #352 * Bug fix: Moving stream factory before send event to before building the stream. See #353 * Bug fix: Cookies no longer match on IP addresses per RFC 6265 * Bug fix: Correctly parsing header parameters that are in `<>` and quotes * Added `cert` and `ssl_key` as request options * `Host` header can now diverge from the host part of a URL if the header is set manually * `Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor` was rewritten to change from using SimpleXML to XMLWriter * OAuth parameters are only added via the plugin if they aren't already set * Exceptions are now thrown when a URL cannot be parsed * Returning `false` if `Guzzle\Http\EntityBody::getContentMd5()` fails * Not setting a `Content-MD5` on a command if calculating the Content-MD5 fails via the CommandContentMd5Plugin ## 3.7.0 - 2013-06-10 * See UPGRADING.md for more information on how to upgrade. * Requests now support the ability to specify an array of $options when creating a request to more easily modify a request. You can pass a 'request.options' configuration setting to a client to apply default request options to every request created by a client (e.g. default query string variables, headers, curl options, etc). * Added a static facade class that allows you to use Guzzle with static methods and mount the class to `\Guzzle`. See `Guzzle\Http\StaticClient::mount`. * Added `command.request_options` to `Guzzle\Service\Command\AbstractCommand` to pass request options to requests created by a command (e.g. custom headers, query string variables, timeout settings, etc). * Stream size in `Guzzle\Stream\PhpStreamRequestFactory` will now be set if Content-Length is returned in the headers of a response * Added `Guzzle\Common\Collection::setPath($path, $value)` to set a value into an array using a nested key (e.g. `$collection->setPath('foo/baz/bar', 'test'); echo $collection['foo']['bar']['bar'];`) * ServiceBuilders now support storing and retrieving arbitrary data * CachePlugin can now purge all resources for a given URI * CachePlugin can automatically purge matching cached items when a non-idempotent request is sent to a resource * CachePlugin now uses the Vary header to determine if a resource is a cache hit * `Guzzle\Http\Message\Response` now implements `\Serializable` * Added `Guzzle\Cache\CacheAdapterFactory::fromCache()` to more easily create cache adapters * `Guzzle\Service\ClientInterface::execute()` now accepts an array, single command, or Traversable * Fixed a bug in `Guzzle\Http\Message\Header\Link::addLink()` * Better handling of calculating the size of a stream in `Guzzle\Stream\Stream` using fstat() and caching the size * `Guzzle\Common\Exception\ExceptionCollection` now creates a more readable exception message * Fixing BC break: Added back the MonologLogAdapter implementation rather than extending from PsrLog so that older Symfony users can still use the old version of Monolog. * Fixing BC break: Added the implementation back in for `Guzzle\Http\Message\AbstractMessage::getTokenizedHeader()`. Now triggering an E_USER_DEPRECATED warning when used. Use `$message->getHeader()->parseParams()`. * Several performance improvements to `Guzzle\Common\Collection` * Added an `$options` argument to the end of the following methods of `Guzzle\Http\ClientInterface`: createRequest, head, delete, put, patch, post, options, prepareRequest * Added an `$options` argument to the end of `Guzzle\Http\Message\Request\RequestFactoryInterface::createRequest()` * Added an `applyOptions()` method to `Guzzle\Http\Message\Request\RequestFactoryInterface` * Changed `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $body = null)` to `Guzzle\Http\ClientInterface::get($uri = null, $headers = null, $options = array())`. You can still pass in a resource, string, or EntityBody into the $options parameter to specify the download location of the response. * Changed `Guzzle\Common\Collection::__construct($data)` to no longer accepts a null value for `$data` but a default `array()` * Added `Guzzle\Stream\StreamInterface::isRepeatable` * Removed `Guzzle\Http\ClientInterface::setDefaultHeaders(). Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. or $client->getConfig()->setPath('request.options/headers', array('header_name' => 'value'))`. * Removed `Guzzle\Http\ClientInterface::getDefaultHeaders(). Use $client->getConfig()->getPath('request.options/headers')`. * Removed `Guzzle\Http\ClientInterface::expandTemplate()` * Removed `Guzzle\Http\ClientInterface::setRequestFactory()` * Removed `Guzzle\Http\ClientInterface::getCurlMulti()` * Removed `Guzzle\Http\Message\RequestInterface::canCache` * Removed `Guzzle\Http\Message\RequestInterface::setIsRedirect` * Removed `Guzzle\Http\Message\RequestInterface::isRedirect` * Made `Guzzle\Http\Client::expandTemplate` and `getUriTemplate` protected methods. * You can now enable E_USER_DEPRECATED warnings to see if you are using a deprecated method by setting `Guzzle\Common\Version::$emitWarnings` to true. * Marked `Guzzle\Http\Message\Request::isResponseBodyRepeatable()` as deprecated. Use `$request->getResponseBody()->isRepeatable()` instead. * Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. * Marked `Guzzle\Http\Message\Request::canCache()` as deprecated. Use `Guzzle\Plugin\Cache\DefaultCanCacheStrategy->canCacheRequest()` instead. * Marked `Guzzle\Http\Message\Request::setIsRedirect()` as deprecated. Use the HistoryPlugin instead. * Marked `Guzzle\Http\Message\Request::isRedirect()` as deprecated. Use the HistoryPlugin instead. * Marked `Guzzle\Cache\CacheAdapterFactory::factory()` as deprecated * Marked 'command.headers', 'command.response_body' and 'command.on_complete' as deprecated for AbstractCommand. These will work through Guzzle 4.0 * Marked 'request.params' for `Guzzle\Http\Client` as deprecated. Use [request.options][params]. * Marked `Guzzle\Service\Client::enableMagicMethods()` as deprecated. Magic methods can no longer be disabled on a Guzzle\Service\Client. * Marked `Guzzle\Service\Client::getDefaultHeaders()` as deprecated. Use $client->getConfig()->getPath('request.options/headers')`. * Marked `Guzzle\Service\Client::setDefaultHeaders()` as deprecated. Use $client->getConfig()->setPath('request.options/headers/{header_name}', 'value')`. * Marked `Guzzle\Parser\Url\UrlParser` as deprecated. Just use PHP's `parse_url()` and percent encode your UTF-8. * Marked `Guzzle\Common\Collection::inject()` as deprecated. * Marked `Guzzle\Plugin\CurlAuth\CurlAuthPlugin` as deprecated. Use `$client->getConfig()->setPath('request.options/auth', array('user', 'pass', 'Basic|Digest');` * CacheKeyProviderInterface and DefaultCacheKeyProvider are no longer used. All of this logic is handled in a CacheStorageInterface. These two objects and interface will be removed in a future version. * Always setting X-cache headers on cached responses * Default cache TTLs are now handled by the CacheStorageInterface of a CachePlugin * `CacheStorageInterface::cache($key, Response $response, $ttl = null)` has changed to `cache(RequestInterface $request, Response $response);` * `CacheStorageInterface::fetch($key)` has changed to `fetch(RequestInterface $request);` * `CacheStorageInterface::delete($key)` has changed to `delete(RequestInterface $request);` * Added `CacheStorageInterface::purge($url)` * `DefaultRevalidation::__construct(CacheKeyProviderInterface $cacheKey, CacheStorageInterface $cache, CachePlugin $plugin)` has changed to `DefaultRevalidation::__construct(CacheStorageInterface $cache, CanCacheStrategyInterface $canCache = null)` * Added `RevalidationInterface::shouldRevalidate(RequestInterface $request, Response $response)` ## 3.6.0 - 2013-05-29 * ServiceDescription now implements ToArrayInterface * Added command.hidden_params to blacklist certain headers from being treated as additionalParameters * Guzzle can now correctly parse incomplete URLs * Mixed casing of headers are now forced to be a single consistent casing across all values for that header. * Messages internally use a HeaderCollection object to delegate handling case-insensitive header resolution * Removed the whole changedHeader() function system of messages because all header changes now go through addHeader(). * Specific header implementations can be created for complex headers. When a message creates a header, it uses a HeaderFactory which can map specific headers to specific header classes. There is now a Link header and CacheControl header implementation. * Removed from interface: Guzzle\Http\ClientInterface::setUriTemplate * Removed from interface: Guzzle\Http\ClientInterface::setCurlMulti() * Removed Guzzle\Http\Message\Request::receivedRequestHeader() and implemented this functionality in Guzzle\Http\Curl\RequestMediator * Removed the optional $asString parameter from MessageInterface::getHeader(). Just cast the header to a string. * Removed the optional $tryChunkedTransfer option from Guzzle\Http\Message\EntityEnclosingRequestInterface * Removed the $asObjects argument from Guzzle\Http\Message\MessageInterface::getHeaders() * Removed Guzzle\Parser\ParserRegister::get(). Use getParser() * Removed Guzzle\Parser\ParserRegister::set(). Use registerParser(). * All response header helper functions return a string rather than mixing Header objects and strings inconsistently * Removed cURL blacklist support. This is no longer necessary now that Expect, Accept, etc are managed by Guzzle directly via interfaces * Removed the injecting of a request object onto a response object. The methods to get and set a request still exist but are a no-op until removed. * Most classes that used to require a ``Guzzle\Service\Command\CommandInterface` typehint now request a `Guzzle\Service\Command\ArrayCommandInterface`. * Added `Guzzle\Http\Message\RequestInterface::startResponse()` to the RequestInterface to handle injecting a response on a request while the request is still being transferred * The ability to case-insensitively search for header values * Guzzle\Http\Message\Header::hasExactHeader * Guzzle\Http\Message\Header::raw. Use getAll() * Deprecated cache control specific methods on Guzzle\Http\Message\AbstractMessage. Use the CacheControl header object instead. * `Guzzle\Service\Command\CommandInterface` now extends from ToArrayInterface and ArrayAccess * Added the ability to cast Model objects to a string to view debug information. ## 3.5.0 - 2013-05-13 * Bug: Fixed a regression so that request responses are parsed only once per oncomplete event rather than multiple times * Bug: Better cleanup of one-time events accross the board (when an event is meant to fire once, it will now remove itself from the EventDispatcher) * Bug: `Guzzle\Log\MessageFormatter` now properly writes "total_time" and "connect_time" values * Bug: Cloning an EntityEnclosingRequest now clones the EntityBody too * Bug: Fixed an undefined index error when parsing nested JSON responses with a sentAs parameter that reference a non-existent key * Bug: All __call() method arguments are now required (helps with mocking frameworks) * Deprecating Response::getRequest() and now using a shallow clone of a request object to remove a circular reference to help with refcount based garbage collection of resources created by sending a request * Deprecating ZF1 cache and log adapters. These will be removed in the next major version. * Deprecating `Response::getPreviousResponse()` (method signature still exists, but it'sdeprecated). Use the HistoryPlugin for a history. * Added a `responseBody` alias for the `response_body` location * Refactored internals to no longer rely on Response::getRequest() * HistoryPlugin can now be cast to a string * HistoryPlugin now logs transactions rather than requests and responses to more accurately keep track of the requests and responses that are sent over the wire * Added `getEffectiveUrl()` and `getRedirectCount()` to Response objects ## 3.4.3 - 2013-04-30 * Bug fix: Fixing bug introduced in 3.4.2 where redirect responses are duplicated on the final redirected response * Added a check to re-extract the temp cacert bundle from the phar before sending each request ## 3.4.2 - 2013-04-29 * Bug fix: Stream objects now work correctly with "a" and "a+" modes * Bug fix: Removing `Transfer-Encoding: chunked` header when a Content-Length is present * Bug fix: AsyncPlugin no longer forces HEAD requests * Bug fix: DateTime timezones are now properly handled when using the service description schema formatter * Bug fix: CachePlugin now properly handles stale-if-error directives when a request to the origin server fails * Setting a response on a request will write to the custom request body from the response body if one is specified * LogPlugin now writes to php://output when STDERR is undefined * Added the ability to set multiple POST files for the same key in a single call * application/x-www-form-urlencoded POSTs now use the utf-8 charset by default * Added the ability to queue CurlExceptions to the MockPlugin * Cleaned up how manual responses are queued on requests (removed "queued_response" and now using request.before_send) * Configuration loading now allows remote files ## 3.4.1 - 2013-04-16 * Large refactoring to how CurlMulti handles work. There is now a proxy that sits in front of a pool of CurlMulti handles. This greatly simplifies the implementation, fixes a couple bugs, and provides a small performance boost. * Exceptions are now properly grouped when sending requests in parallel * Redirects are now properly aggregated when a multi transaction fails * Redirects now set the response on the original object even in the event of a failure * Bug fix: Model names are now properly set even when using $refs * Added support for PHP 5.5's CurlFile to prevent warnings with the deprecated @ syntax * Added support for oauth_callback in OAuth signatures * Added support for oauth_verifier in OAuth signatures * Added support to attempt to retrieve a command first literally, then ucfirst, the with inflection ## 3.4.0 - 2013-04-11 * Bug fix: URLs are now resolved correctly based on http://tools.ietf.org/html/rfc3986#section-5.2. #289 * Bug fix: Absolute URLs with a path in a service description will now properly override the base URL. #289 * Bug fix: Parsing a query string with a single PHP array value will now result in an array. #263 * Bug fix: Better normalization of the User-Agent header to prevent duplicate headers. #264. * Bug fix: Added `number` type to service descriptions. * Bug fix: empty parameters are removed from an OAuth signature * Bug fix: Revalidating a cache entry prefers the Last-Modified over the Date header * Bug fix: Fixed "array to string" error when validating a union of types in a service description * Bug fix: Removed code that attempted to determine the size of a stream when data is written to the stream * Bug fix: Not including an `oauth_token` if the value is null in the OauthPlugin. * Bug fix: Now correctly aggregating successful requests and failed requests in CurlMulti when a redirect occurs. * The new default CURLOPT_TIMEOUT setting has been increased to 150 seconds so that Guzzle works on poor connections. * Added a feature to EntityEnclosingRequest::setBody() that will automatically set the Content-Type of the request if the Content-Type can be determined based on the entity body or the path of the request. * Added the ability to overwrite configuration settings in a client when grabbing a throwaway client from a builder. * Added support for a PSR-3 LogAdapter. * Added a `command.after_prepare` event * Added `oauth_callback` parameter to the OauthPlugin * Added the ability to create a custom stream class when using a stream factory * Added a CachingEntityBody decorator * Added support for `additionalParameters` in service descriptions to define how custom parameters are serialized. * The bundled SSL certificate is now provided in the phar file and extracted when running Guzzle from a phar. * You can now send any EntityEnclosingRequest with POST fields or POST files and cURL will handle creating bodies * POST requests using a custom entity body are now treated exactly like PUT requests but with a custom cURL method. This means that the redirect behavior of POST requests with custom bodies will not be the same as POST requests that use POST fields or files (the latter is only used when emulating a form POST in the browser). * Lots of cleanup to CurlHandle::factory and RequestFactory::createRequest ## 3.3.1 - 2013-03-10 * Added the ability to create PHP streaming responses from HTTP requests * Bug fix: Running any filters when parsing response headers with service descriptions * Bug fix: OauthPlugin fixes to allow for multi-dimensional array signing, and sorting parameters before signing * Bug fix: Removed the adding of default empty arrays and false Booleans to responses in order to be consistent across response location visitors. * Bug fix: Removed the possibility of creating configuration files with circular dependencies * RequestFactory::create() now uses the key of a POST file when setting the POST file name * Added xmlAllowEmpty to serialize an XML body even if no XML specific parameters are set ## 3.3.0 - 2013-03-03 * A large number of performance optimizations have been made * Bug fix: Added 'wb' as a valid write mode for streams * Bug fix: `Guzzle\Http\Message\Response::json()` now allows scalar values to be returned * Bug fix: Fixed bug in `Guzzle\Http\Message\Response` where wrapping quotes were stripped from `getEtag()` * BC: Removed `Guzzle\Http\Utils` class * BC: Setting a service description on a client will no longer modify the client's command factories. * BC: Emitting IO events from a RequestMediator is now a parameter that must be set in a request's curl options using the 'emit_io' key. This was previously set under a request's parameters using 'curl.emit_io' * BC: `Guzzle\Stream\Stream::getWrapper()` and `Guzzle\Stream\Stream::getSteamType()` are no longer converted to lowercase * Operation parameter objects are now lazy loaded internally * Added ErrorResponsePlugin that can throw errors for responses defined in service description operations' errorResponses * Added support for instantiating responseType=class responseClass classes. Classes must implement `Guzzle\Service\Command\ResponseClassInterface` * Added support for additionalProperties for top-level parameters in responseType=model responseClasses. These additional properties also support locations and can be used to parse JSON responses where the outermost part of the JSON is an array * Added support for nested renaming of JSON models (rename sentAs to name) * CachePlugin * Added support for stale-if-error so that the CachePlugin can now serve stale content from the cache on error * Debug headers can now added to cached response in the CachePlugin ## 3.2.0 - 2013-02-14 * CurlMulti is no longer reused globally. A new multi object is created per-client. This helps to isolate clients. * URLs with no path no longer contain a "/" by default * Guzzle\Http\QueryString does no longer manages the leading "?". This is now handled in Guzzle\Http\Url. * BadResponseException no longer includes the full request and response message * Adding setData() to Guzzle\Service\Description\ServiceDescriptionInterface * Adding getResponseBody() to Guzzle\Http\Message\RequestInterface * Various updates to classes to use ServiceDescriptionInterface type hints rather than ServiceDescription * Header values can now be normalized into distinct values when multiple headers are combined with a comma separated list * xmlEncoding can now be customized for the XML declaration of a XML service description operation * Guzzle\Http\QueryString now uses Guzzle\Http\QueryAggregator\QueryAggregatorInterface objects to add custom value aggregation and no longer uses callbacks * The URL encoding implementation of Guzzle\Http\QueryString can now be customized * Bug fix: Filters were not always invoked for array service description parameters * Bug fix: Redirects now use a target response body rather than a temporary response body * Bug fix: The default exponential backoff BackoffPlugin was not giving when the request threshold was exceeded * Bug fix: Guzzle now takes the first found value when grabbing Cache-Control directives ## 3.1.2 - 2013-01-27 * Refactored how operation responses are parsed. Visitors now include a before() method responsible for parsing the response body. For example, the XmlVisitor now parses the XML response into an array in the before() method. * Fixed an issue where cURL would not automatically decompress responses when the Accept-Encoding header was sent * CURLOPT_SSL_VERIFYHOST is never set to 1 because it is deprecated (see 5e0ff2ef20f839e19d1eeb298f90ba3598784444) * Fixed a bug where redirect responses were not chained correctly using getPreviousResponse() * Setting default headers on a client after setting the user-agent will not erase the user-agent setting ## 3.1.1 - 2013-01-20 * Adding wildcard support to Guzzle\Common\Collection::getPath() * Adding alias support to ServiceBuilder configs * Adding Guzzle\Service\Resource\CompositeResourceIteratorFactory and cleaning up factory interface ## 3.1.0 - 2013-01-12 * BC: CurlException now extends from RequestException rather than BadResponseException * BC: Renamed Guzzle\Plugin\Cache\CanCacheStrategyInterface::canCache() to canCacheRequest() and added CanCacheResponse() * Added getData to ServiceDescriptionInterface * Added context array to RequestInterface::setState() * Bug: Removing hard dependency on the BackoffPlugin from Guzzle\Http * Bug: Adding required content-type when JSON request visitor adds JSON to a command * Bug: Fixing the serialization of a service description with custom data * Made it easier to deal with exceptions thrown when transferring commands or requests in parallel by providing an array of successful and failed responses * Moved getPath from Guzzle\Service\Resource\Model to Guzzle\Common\Collection * Added Guzzle\Http\IoEmittingEntityBody * Moved command filtration from validators to location visitors * Added `extends` attributes to service description parameters * Added getModels to ServiceDescriptionInterface ## 3.0.7 - 2012-12-19 * Fixing phar detection when forcing a cacert to system if null or true * Allowing filename to be passed to `Guzzle\Http\Message\Request::setResponseBody()` * Cleaning up `Guzzle\Common\Collection::inject` method * Adding a response_body location to service descriptions ## 3.0.6 - 2012-12-09 * CurlMulti performance improvements * Adding setErrorResponses() to Operation * composer.json tweaks ## 3.0.5 - 2012-11-18 * Bug: Fixing an infinite recursion bug caused from revalidating with the CachePlugin * Bug: Response body can now be a string containing "0" * Bug: Using Guzzle inside of a phar uses system by default but now allows for a custom cacert * Bug: QueryString::fromString now properly parses query string parameters that contain equal signs * Added support for XML attributes in service description responses * DefaultRequestSerializer now supports array URI parameter values for URI template expansion * Added better mimetype guessing to requests and post files ## 3.0.4 - 2012-11-11 * Bug: Fixed a bug when adding multiple cookies to a request to use the correct glue value * Bug: Cookies can now be added that have a name, domain, or value set to "0" * Bug: Using the system cacert bundle when using the Phar * Added json and xml methods to Response to make it easier to parse JSON and XML response data into data structures * Enhanced cookie jar de-duplication * Added the ability to enable strict cookie jars that throw exceptions when invalid cookies are added * Added setStream to StreamInterface to actually make it possible to implement custom rewind behavior for entity bodies * Added the ability to create any sort of hash for a stream rather than just an MD5 hash ## 3.0.3 - 2012-11-04 * Implementing redirects in PHP rather than cURL * Added PECL URI template extension and using as default parser if available * Bug: Fixed Content-Length parsing of Response factory * Adding rewind() method to entity bodies and streams. Allows for custom rewinding of non-repeatable streams. * Adding ToArrayInterface throughout library * Fixing OauthPlugin to create unique nonce values per request ## 3.0.2 - 2012-10-25 * Magic methods are enabled by default on clients * Magic methods return the result of a command * Service clients no longer require a base_url option in the factory * Bug: Fixed an issue with URI templates where null template variables were being expanded ## 3.0.1 - 2012-10-22 * Models can now be used like regular collection objects by calling filter, map, etc * Models no longer require a Parameter structure or initial data in the constructor * Added a custom AppendIterator to get around a PHP bug with the `\AppendIterator` ## 3.0.0 - 2012-10-15 * Rewrote service description format to be based on Swagger * Now based on JSON schema * Added nested input structures and nested response models * Support for JSON and XML input and output models * Renamed `commands` to `operations` * Removed dot class notation * Removed custom types * Broke the project into smaller top-level namespaces to be more component friendly * Removed support for XML configs and descriptions. Use arrays or JSON files. * Removed the Validation component and Inspector * Moved all cookie code to Guzzle\Plugin\Cookie * Magic methods on a Guzzle\Service\Client now return the command un-executed. * Calling getResult() or getResponse() on a command will lazily execute the command if needed. * Now shipping with cURL's CA certs and using it by default * Added previousResponse() method to response objects * No longer sending Accept and Accept-Encoding headers on every request * Only sending an Expect header by default when a payload is greater than 1MB * Added/moved client options: * curl.blacklist to curl.option.blacklist * Added ssl.certificate_authority * Added a Guzzle\Iterator component * Moved plugins from Guzzle\Http\Plugin to Guzzle\Plugin * Added a more robust backoff retry strategy (replaced the ExponentialBackoffPlugin) * Added a more robust caching plugin * Added setBody to response objects * Updating LogPlugin to use a more flexible MessageFormatter * Added a completely revamped build process * Cleaning up Collection class and removing default values from the get method * Fixed ZF2 cache adapters ## 2.8.8 - 2012-10-15 * Bug: Fixed a cookie issue that caused dot prefixed domains to not match where popular browsers did ## 2.8.7 - 2012-09-30 * Bug: Fixed config file aliases for JSON includes * Bug: Fixed cookie bug on a request object by using CookieParser to parse cookies on requests * Bug: Removing the path to a file when sending a Content-Disposition header on a POST upload * Bug: Hardening request and response parsing to account for missing parts * Bug: Fixed PEAR packaging * Bug: Fixed Request::getInfo * Bug: Fixed cases where CURLM_CALL_MULTI_PERFORM return codes were causing curl transactions to fail * Adding the ability for the namespace Iterator factory to look in multiple directories * Added more getters/setters/removers from service descriptions * Added the ability to remove POST fields from OAuth signatures * OAuth plugin now supports 2-legged OAuth ## 2.8.6 - 2012-09-05 * Added the ability to modify and build service descriptions * Added the use of visitors to apply parameters to locations in service descriptions using the dynamic command * Added a `json` parameter location * Now allowing dot notation for classes in the CacheAdapterFactory * Using the union of two arrays rather than an array_merge when extending service builder services and service params * Ensuring that a service is a string before doing strpos() checks on it when substituting services for references in service builder config files. * Services defined in two different config files that include one another will by default replace the previously defined service, but you can now create services that extend themselves and merge their settings over the previous * The JsonLoader now supports aliasing filenames with different filenames. This allows you to alias something like '_default' with a default JSON configuration file. ## 2.8.5 - 2012-08-29 * Bug: Suppressed empty arrays from URI templates * Bug: Added the missing $options argument from ServiceDescription::factory to enable caching * Added support for HTTP responses that do not contain a reason phrase in the start-line * AbstractCommand commands are now invokable * Added a way to get the data used when signing an Oauth request before a request is sent ## 2.8.4 - 2012-08-15 * Bug: Custom delay time calculations are no longer ignored in the ExponentialBackoffPlugin * Added the ability to transfer entity bodies as a string rather than streamed. This gets around curl error 65. Set `body_as_string` in a request's curl options to enable. * Added a StreamInterface, EntityBodyInterface, and added ftell() to Guzzle\Common\Stream * Added an AbstractEntityBodyDecorator and a ReadLimitEntityBody decorator to transfer only a subset of a decorated stream * Stream and EntityBody objects will now return the file position to the previous position after a read required operation (e.g. getContentMd5()) * Added additional response status codes * Removed SSL information from the default User-Agent header * DELETE requests can now send an entity body * Added an EventDispatcher to the ExponentialBackoffPlugin and added an ExponentialBackoffLogger to log backoff retries * Added the ability of the MockPlugin to consume mocked request bodies * LogPlugin now exposes request and response objects in the extras array ## 2.8.3 - 2012-07-30 * Bug: Fixed a case where empty POST requests were sent as GET requests * Bug: Fixed a bug in ExponentialBackoffPlugin that caused fatal errors when retrying an EntityEnclosingRequest that does not have a body * Bug: Setting the response body of a request to null after completing a request, not when setting the state of a request to new * Added multiple inheritance to service description commands * Added an ApiCommandInterface and added ``getParamNames()`` and ``hasParam()`` * Removed the default 2mb size cutoff from the Md5ValidatorPlugin so that it now defaults to validating everything * Changed CurlMulti::perform to pass a smaller timeout to CurlMulti::executeHandles ## 2.8.2 - 2012-07-24 * Bug: Query string values set to 0 are no longer dropped from the query string * Bug: A Collection object is no longer created each time a call is made to ``Guzzle\Service\Command\AbstractCommand::getRequestHeaders()`` * Bug: ``+`` is now treated as an encoded space when parsing query strings * QueryString and Collection performance improvements * Allowing dot notation for class paths in filters attribute of a service descriptions ## 2.8.1 - 2012-07-16 * Loosening Event Dispatcher dependency * POST redirects can now be customized using CURLOPT_POSTREDIR ## 2.8.0 - 2012-07-15 * BC: Guzzle\Http\Query * Query strings with empty variables will always show an equal sign unless the variable is set to QueryString::BLANK (e.g. ?acl= vs ?acl) * Changed isEncodingValues() and isEncodingFields() to isUrlEncoding() * Changed setEncodeValues(bool) and setEncodeFields(bool) to useUrlEncoding(bool) * Changed the aggregation functions of QueryString to be static methods * Can now use fromString() with querystrings that have a leading ? * cURL configuration values can be specified in service descriptions using ``curl.`` prefixed parameters * Content-Length is set to 0 before emitting the request.before_send event when sending an empty request body * Cookies are no longer URL decoded by default * Bug: URI template variables set to null are no longer expanded ## 2.7.2 - 2012-07-02 * 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 - 2012-06-26 * Minor patch to update version number in UA string * Updating build process ## 2.7.0 - 2012-06-25 * 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 - 2012-06-10 * 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 - 2012-06-03 * 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 - 2012-05-30 * 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 - 2012-05-23 * [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 - 2012-05-19 * [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 - 2012-05-19 * [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 - 2012-05-15 * [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 - 2012-05-08 * 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 strategies. * 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 guzzle/phpunit.xml.dist 0000604 00000004512 15173213565 0011245 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="./tests/bootstrap.php" colors="true" processIsolation="false" stopOnFailure="false" syntaxCheck="false" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"> <testsuites> <testsuite> <directory>./tests/Guzzle/Tests</directory> </testsuite> </testsuites> <logging> <log type="junit" target="build/artifacts/logs/junit.xml" logIncompleteSkipped="false" /> </logging> <filter> <whitelist> <directory suffix=".php">./src/Guzzle</directory> <exclude> <directory suffix="Interface.php">./src/Guzzle</directory> <file>./src/Guzzle/Common/Exception/GuzzleException.php</file> <file>./src/Guzzle/Http/Exception/HttpException.php</file> <file>./src/Guzzle/Http/Exception/ServerErrorResponseException.php</file> <file>./src/Guzzle/Http/Exception/ClientErrorResponseException.php</file> <file>./src/Guzzle/Http/Exception/TooManyRedirectsException.php</file> <file>./src/Guzzle/Http/Exception/CouldNotRewindStreamException.php</file> <file>./src/Guzzle/Common/Exception/BadMethodCallException.php</file> <file>./src/Guzzle/Common/Exception/InvalidArgumentException.php</file> <file>./src/Guzzle/Common/Exception/RuntimeException.php</file> <file>./src/Guzzle/Common/Exception/UnexpectedValueException.php</file> <file>./src/Guzzle/Service/Exception/ClientNotFoundException.php</file> <file>./src/Guzzle/Service/Exception/CommandException.php</file> <file>./src/Guzzle/Service/Exception/DescriptionBuilderException.php</file> <file>./src/Guzzle/Service/Exception/ServiceBuilderException.php</file> <file>./src/Guzzle/Service/Exception/ServiceNotFoundException.php</file> <file>./src/Guzzle/Service/Exception/ValidationException.php</file> <file>./src/Guzzle/Service/Exception/JsonException.php</file> </exclude> </whitelist> </filter> </phpunit> guzzle/tests/Guzzle/Tests/Common/EventTest.php 0000604 00000002671 15173213566 0015505 0 ustar 00 <?php namespace Guzzle\Tests\Common; use Guzzle\Common\Event; /** * @covers Guzzle\Common\Event */ class EventTest extends \Guzzle\Tests\GuzzleTestCase { /** * @return Event */ private function getEvent() { return new Event(array( 'test' => '123', 'other' => '456', 'event' => 'test.notify' )); } public function testAllowsParameterInjection() { $event = new Event(array( 'test' => '123' )); $this->assertEquals('123', $event['test']); } 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']); } public function testImplementsIteratorAggregate() { $event = $this->getEvent(); $this->assertInstanceOf('ArrayIterator', $event->getIterator()); } public function testConvertsToArray() { $this->assertEquals(array( 'test' => '123', 'other' => '456', 'event' => 'test.notify' ), $this->getEvent()->toArray()); } } guzzle/tests/Guzzle/Tests/Common/Exception/BatchTransferExceptionTest.php 0000604 00000001544 15173213566 0022765 0 ustar 00 <?php namespace Guzzle\Tests\Common\Exception; use Guzzle\Batch\Exception\BatchTransferException; class BatchTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase { public function testContainsBatch() { $e = new \Exception('Baz!'); $t = $this->getMock('Guzzle\Batch\BatchTransferInterface'); $d = $this->getMock('Guzzle\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()); } } guzzle/tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php 0000604 00000005064 15173213566 0022333 0 ustar 00 <?php namespace Guzzle\Tests\Common\Exception; use Guzzle\Common\Exception\ExceptionCollection; class ExceptionCollectionTest extends \Guzzle\Tests\GuzzleTestCase { private function getExceptions() { return array( new \Exception('Test'), new \Exception('Testing') ); } public function testAggregatesExceptions() { $e = new ExceptionCollection(); $exceptions = $this->getExceptions(); $e->add($exceptions[0]); $e->add($exceptions[1]); $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $e->getMessage()); $this->assertContains(" Test\n\n #0 ./", $e->getMessage()); $this->assertSame($exceptions[0], $e->getFirst()); } public function testCanSetExceptions() { $ex = new \Exception('foo'); $e = new ExceptionCollection(); $e->setExceptions(array($ex)); $this->assertSame($ex, $e->getFirst()); } 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('Meta description!'); $e2->add(new \Exception("Test 2")); $e3 = new ExceptionCollection(); $e3->add(new \Exception('Baz')); $e2->add($e3); $e1->add($e2); $message = $e1->getMessage(); $this->assertContains("(Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); $this->assertContains("\n Test\n\n #0 ", $message); $this->assertContains("\n\n(Guzzle\\Common\\Exception\\ExceptionCollection) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); $this->assertContains("\n\n Meta description!\n\n", $message); $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); $this->assertContains("\n Test 2\n\n #0 ", $message); $this->assertContains(" (Exception) ./tests/Guzzle/Tests/Common/Exception/ExceptionCollectionTest.php line ", $message); $this->assertContains(" Baz\n\n #0", $message); } } guzzle/tests/Guzzle/Tests/Common/CollectionTest.php 0000604 00000041674 15173213566 0016525 0 ustar 00 <?php namespace Guzzle\Tests\Common; use Guzzle\Common\Collection; use Guzzle\Common\Exception\InvalidArgumentException; use Guzzle\Http\QueryString; /** * @covers Guzzle\Common\Collection */ class CollectionTest extends \Guzzle\Tests\GuzzleTestCase { /** @var Collection */ protected $coll; protected function setUp() { $this->coll = new Collection(); } public function testConstructorCanBeCalledWithNoParams() { $this->coll = new Collection(); $p = $this->coll->getAll(); $this->assertEmpty($p, '-> Collection must be empty when no data is passed'); } 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'); $this->assertEquals($this->coll->getAll(), $this->coll->toArray()); } 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); } 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'))); } public function testHandlesMergingInDisparateDataSources() { $params = array( 'test' => 'value1', 'test2' => 'value2', 'test3' => array('value3', 'value4') ); $this->coll->merge($params); $this->assertEquals($this->coll->getAll(), $params); // Pass the same object to itself $this->assertEquals($this->coll->merge($this->coll), $this->coll); } 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()); } 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'))); } 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()); } 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')); } 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')); } 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' ))); } 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)); } 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()); } 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()); } 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()); } 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')); } 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)); } public function testCanReplaceAllData() { $this->assertSame($this->coll, $this->coll->replace(array( 'a' => '123' ))); $this->assertEquals(array( 'a' => '123' ), $this->coll->getAll()); } 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_a_{0}', array( 'abc' => 'this', 0 => 'test' )), array('this_is_a_test', 'this_is_a_test', array( 'abc' => 'this' )), array('{abc}_is_{not_found}a_{0}', '{abc}_is_{not_found}a_{0}', array()) ); } /** * @dataProvider dataProvider */ public function testInjectsConfigData($output, $input, $config) { $collection = new Collection($config); $this->assertEquals($output, $collection->inject($input)); } 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')); } public function testPreparesFromConfig() { $c = Collection::fromConfig(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 = Collection::fromConfig(array(), array(), array('a')); $this->fail('Exception not throw when missing config'); } catch (InvalidArgumentException $e) { } } function falseyDataProvider() { return array( array(false, false), array(null, null), array('', ''), array(array(), array()), array(0, 0), ); } /** * @dataProvider falseyDataProvider */ public function testReturnsCorrectData($a, $b) { $c = new Collection(array('value' => $a)); $this->assertSame($b, $c->get('value')); } public function testRetrievesNestedKeysUsingPath() { $data = array( 'foo' => 'bar', 'baz' => array( 'mesa' => array( 'jar' => 'jar' ) ) ); $collection = new Collection($data); $this->assertEquals('bar', $collection->getPath('foo')); $this->assertEquals('jar', $collection->getPath('baz/mesa/jar')); $this->assertNull($collection->getPath('wewewf')); $this->assertNull($collection->getPath('baz/mesa/jar/jar')); } public function testFalseyKeysStillDescend() { $collection = new Collection(array( '0' => array( 'a' => 'jar' ), 1 => 'other' )); $this->assertEquals('jar', $collection->getPath('0/a')); $this->assertEquals('other', $collection->getPath('1')); } public function getPathProvider() { $data = array( 'foo' => 'bar', 'baz' => array( 'mesa' => array( 'jar' => 'jar', 'array' => array('a', 'b', 'c') ), 'bar' => array( 'baz' => 'bam', 'array' => array('d', 'e', 'f') ) ), 'bam' => array( array('foo' => 1), array('foo' => 2), array('array' => array('h', 'i')) ) ); $c = new Collection($data); return array( // Simple path selectors array($c, 'foo', 'bar'), array($c, 'baz', $data['baz']), array($c, 'bam', $data['bam']), array($c, 'baz/mesa', $data['baz']['mesa']), array($c, 'baz/mesa/jar', 'jar'), // Merge everything two levels under baz array($c, 'baz/*', array( 'jar' => 'jar', 'array' => array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array']), 'baz' => 'bam' )), // Does not barf on missing keys array($c, 'fefwfw', null), // Does not barf when a wildcard does not resolve correctly array($c, '*/*/*/*/*/wefwfe', array()), // Allows custom separator array($c, '*|mesa', $data['baz']['mesa'], '|'), // Merge all 'array' keys two levels under baz (the trailing * does not hurt the results) array($c, 'baz/*/array/*', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), // Merge all 'array' keys two levels under baz array($c, 'baz/*/array', array_merge($data['baz']['mesa']['array'], $data['baz']['bar']['array'])), array($c, 'baz/mesa/array', $data['baz']['mesa']['array']), // Having a trailing * does not hurt the results array($c, 'baz/mesa/array/*', $data['baz']['mesa']['array']), // Merge of anything one level deep array($c, '*', array_merge(array('bar'), $data['baz'], $data['bam'])), // Funky merge of anything two levels deep array($c, '*/*', array( 'jar' => 'jar', 'array' => array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'), 'baz' => 'bam', 'foo' => array(1, 2) )), // Funky merge of all 'array' keys that are two levels deep array($c, '*/*/array', array('a', 'b', 'c', 'd', 'e', 'f', 'h', 'i')) ); } /** * @dataProvider getPathProvider */ public function testGetPath(Collection $c, $path, $expected, $separator = '/') { $this->assertEquals($expected, $c->getPath($path, $separator)); } public function testOverridesSettings() { $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); $c->overwriteWith(array('foo' => 10, 'bar' => 300)); $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); } public function testOverwriteWithCollection() { $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); $b = new Collection(array('foo' => 10, 'bar' => 300)); $c->overwriteWith($b); $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); } public function testOverwriteWithTraversable() { $c = new Collection(array('foo' => 1, 'baz' => 2, 'bar' => 3)); $b = new Collection(array('foo' => 10, 'bar' => 300)); $c->overwriteWith($b->getIterator()); $this->assertEquals(array('foo' => 10, 'baz' => 2, 'bar' => 300), $c->getAll()); } public function testCanSetNestedPathValueThatDoesNotExist() { $c = new Collection(array()); $c->setPath('foo/bar/baz/123', 'hi'); $this->assertEquals('hi', $c['foo']['bar']['baz']['123']); } public function testCanSetNestedPathValueThatExists() { $c = new Collection(array('foo' => array('bar' => 'test'))); $c->setPath('foo/bar', 'hi'); $this->assertEquals('hi', $c['foo']['bar']); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException */ public function testVerifiesNestedPathIsValidAtExactLevel() { $c = new Collection(array('foo' => 'bar')); $c->setPath('foo/bar', 'hi'); $this->assertEquals('hi', $c['foo']['bar']); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException */ public function testVerifiesThatNestedPathIsValidAtAnyLevel() { $c = new Collection(array('foo' => 'bar')); $c->setPath('foo/bar/baz', 'test'); } } guzzle/tests/Guzzle/Tests/Common/VersionTest.php 0000604 00000001037 15173213566 0016044 0 ustar 00 <?php namespace Guzzle\Tests\Common; use Guzzle\Common\Version; /** * @covers Guzzle\Common\Version */ class VersionTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException \PHPUnit_Framework_Error_Deprecated */ public function testEmitsWarnings() { Version::$emitWarnings = true; Version::warn('testing!'); } public function testCanSilenceWarnings() { Version::$emitWarnings = false; Version::warn('testing!'); Version::$emitWarnings = true; } } guzzle/tests/Guzzle/Tests/Common/AbstractHasDispatcherTest.php 0000604 00000004133 15173213566 0020625 0 ustar 00 <?php namespace Guzzle\Tests\Common; use Guzzle\Common\Event; use Guzzle\Common\AbstractHasDispatcher; use Symfony\Component\EventDispatcher\EventDispatcher; /** * @covers Guzzle\Common\AbstractHasDispatcher */ class AbstractHasAdapterTest extends \Guzzle\Tests\GuzzleTestCase { public function testDoesNotRequireRegisteredEvents() { $this->assertEquals(array(), AbstractHasDispatcher::getAllEvents()); } public function testAllowsDispatcherToBeInjected() { $d = new EventDispatcher(); $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); $this->assertSame($mock, $mock->setEventDispatcher($d)); $this->assertSame($d, $mock->getEventDispatcher()); } public function testCreatesDefaultEventDispatcherIfNeeded() { $mock = $this->getMockForAbstractClass('Guzzle\Common\AbstractHasDispatcher'); $this->assertInstanceOf('Symfony\Component\EventDispatcher\EventDispatcher', $mock->getEventDispatcher()); } 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); } 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); } } guzzle/tests/Guzzle/Tests/Cache/ClosureCacheAdapterTest.php 0000604 00000005114 15173213566 0020033 0 ustar 00 <?php namespace Guzzle\Tests\Cache; use Guzzle\Cache\ClosureCacheAdapter; /** * @covers Guzzle\Cache\ClosureCacheAdapter */ class ClosureCacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** @var ClosureCacheAdapter */ private $adapter; /** Array of callables to use for testing */ private $callables; /** Cache data for testing */ public $data = array(); /** * Prepares the environment before running a test. */ protected function setUp() { parent::setUp(); $that = $this; $this->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(); } /** * @expectedException InvalidArgumentException */ public function testEnsuresCallablesArePresent() { $callables = $this->callables; unset($callables['delete']); $cache = new ClosureCacheAdapter($callables); } public function testAllCallablesMustBePresent() { $cache = new ClosureCacheAdapter($this->callables); } public function testCachesDataUsingCallables() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertEquals('data', $this->adapter->fetch('test')); } public function testChecksIfCacheContainsKeys() { $this->adapter->save('test', 'data', 1000); $this->assertTrue($this->adapter->contains('test')); $this->assertFalse($this->adapter->contains('foo')); } 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')); } } guzzle/tests/Guzzle/Tests/Cache/Zf2CacheAdapterTest.php 0000604 00000002715 15173213566 0017064 0 ustar 00 <?php namespace Guzzle\Tests\Cache; use Guzzle\Cache\Zf2CacheAdapter; use Zend\Cache\StorageFactory; /** * @covers Guzzle\Cache\Zf2CacheAdapter */ class Zf2CacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase { private $cache; private $adapter; /** * Prepares the environment before running a test. */ protected function setUp() { parent::setUp(); $this->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(); } public function testCachesDataUsingCallables() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertEquals('data', $this->adapter->fetch('test')); } public function testChecksIfCacheContainsKeys() { $this->adapter->save('test', 'data', 1000); $this->assertTrue($this->adapter->contains('test')); $this->assertFalse($this->adapter->contains('foo')); } 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')); } } guzzle/tests/Guzzle/Tests/Cache/NullCacheAdapterTest.php 0000604 00000001002 15173213566 0017321 0 ustar 00 <?php namespace Guzzle\Tests\Common\Cache; use Guzzle\Cache\NullCacheAdapter; /** * @covers Guzzle\Cache\NullCacheAdapter */ class NullCacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase { public function testNullCacheAdapter() { $c = new NullCacheAdapter(); $this->assertEquals(false, $c->contains('foo')); $this->assertEquals(true, $c->delete('foo')); $this->assertEquals(false, $c->fetch('foo')); $this->assertEquals(true, $c->save('foo', 'bar')); } } guzzle/tests/Guzzle/Tests/Cache/CacheAdapterTest.php 0000604 00000003225 15173213566 0016477 0 ustar 00 <?php namespace Guzzle\Tests\Cache; use Guzzle\Cache\DoctrineCacheAdapter; use Doctrine\Common\Cache\ArrayCache; /** * @covers Guzzle\Cache\DoctrineCacheAdapter * @covers Guzzle\Cache\AbstractCacheAdapter */ class CacheAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** @var ArrayCache */ private $cache; /** @var DoctrineCacheAdapter */ private $adapter; /** * Prepares the environment before running a test. */ protected function setUp() { parent::setUp(); $this->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(); } public function testGetCacheObject() { $this->assertEquals($this->cache, $this->adapter->getCacheObject()); } public function testSave() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); } public function testFetch() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertEquals('data', $this->adapter->fetch('test')); } public function testContains() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertTrue($this->adapter->contains('test')); } public function testDelete() { $this->assertTrue($this->adapter->save('test', 'data', 1000)); $this->assertTrue($this->adapter->delete('test')); $this->assertFalse($this->adapter->contains('test')); } } guzzle/tests/Guzzle/Tests/Cache/CacheAdapterFactoryTest.php 0000604 00000003133 15173213566 0020025 0 ustar 00 <?php namespace Guzzle\Tests\Cache; use Guzzle\Cache\CacheAdapterFactory; use Guzzle\Cache\DoctrineCacheAdapter; use Doctrine\Common\Cache\ArrayCache; use Zend\Cache\StorageFactory; /** * @covers Guzzle\Cache\CacheAdapterFactory */ class CacheAdapterFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** @var ArrayCache */ private $cache; /** @var DoctrineCacheAdapter */ private $adapter; /** * Prepares the environment before running a test. */ protected function setup() { parent::setUp(); $this->cache = new ArrayCache(); $this->adapter = new DoctrineCacheAdapter($this->cache); } /** * @expectedException \InvalidArgumentException */ public function testEnsuresConfigIsObject() { CacheAdapterFactory::fromCache(array()); } /** * @expectedException \InvalidArgumentException */ public function testEnsuresKnownType() { CacheAdapterFactory::fromCache(new \stdClass()); } public function cacheProvider() { return array( array(new DoctrineCacheAdapter(new ArrayCache()), 'Guzzle\Cache\DoctrineCacheAdapter'), array(new ArrayCache(), 'Guzzle\Cache\DoctrineCacheAdapter'), array(StorageFactory::factory(array('adapter' => 'memory')), 'Guzzle\Cache\Zf2CacheAdapter'), ); } /** * @dataProvider cacheProvider */ public function testCreatesNullCacheAdapterByDefault($cache, $type) { $adapter = CacheAdapterFactory::fromCache($cache); $this->assertInstanceOf($type, $adapter); } } guzzle/tests/Guzzle/Tests/Iterator/ChunkedIteratorTest.php 0000604 00000003245 15173213566 0020056 0 ustar 00 <?php namespace Guzzle\Tests\Iterator; use Guzzle\Iterator\ChunkedIterator; /** * @covers Guzzle\Iterator\ChunkedIterator */ class ChunkedIteratorTest extends \PHPUnit_Framework_TestCase { public function testChunksIterator() { $chunked = new ChunkedIterator(new \ArrayIterator(range(0, 100)), 10); $chunks = iterator_to_array($chunked, false); $this->assertEquals(11, count($chunks)); foreach ($chunks as $j => $chunk) { $this->assertEquals(range($j * 10, min(100, $j * 10 + 9)), $chunk); } } public function testChunksIteratorWithOddValues() { $chunked = new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2); $chunks = iterator_to_array($chunked, false); $this->assertEquals(3, count($chunks)); $this->assertEquals(array(1, 2), $chunks[0]); $this->assertEquals(array(3, 4), $chunks[1]); $this->assertEquals(array(5), $chunks[2]); } public function testMustNotTerminateWithTraversable() { $traversable = simplexml_load_string('<root><foo/><foo/><foo/></root>')->foo; $chunked = new ChunkedIterator($traversable, 2); $actual = iterator_to_array($chunked, false); $this->assertCount(2, $actual); } public function testSizeOfZeroMakesIteratorInvalid() { $chunked = new ChunkedIterator(new \ArrayIterator(range(1, 5)), 0); $chunked->rewind(); $this->assertFalse($chunked->valid()); } /** * @expectedException \InvalidArgumentException */ public function testSizeLowerZeroThrowsException() { new ChunkedIterator(new \ArrayIterator(range(1, 5)), -1); } } guzzle/tests/Guzzle/Tests/Iterator/FilterIteratorTest.php 0000604 00000001211 15173213566 0017711 0 ustar 00 <?php namespace Guzzle\Tests\Iterator; use Guzzle\Iterator\FilterIterator; /** * @covers Guzzle\Iterator\FilterIterator */ class FilterIteratorTest extends \PHPUnit_Framework_TestCase { public function testFiltersValues() { $i = new FilterIterator(new \ArrayIterator(range(0, 100)), function ($value) { return $value % 2; }); $this->assertEquals(range(1, 99, 2), iterator_to_array($i, false)); } /** * @expectedException \InvalidArgumentException */ public function testValidatesCallable() { $i = new FilterIterator(new \ArrayIterator(), new \stdClass()); } } guzzle/tests/Guzzle/Tests/Iterator/MethodProxyIteratorTest.php 0000604 00000001553 15173213566 0020757 0 ustar 00 <?php namespace Guzzle\Tests\Iterator; use Guzzle\Iterator\MethodProxyIterator; use Guzzle\Iterator\ChunkedIterator; /** * @covers Guzzle\Iterator\MethodProxyIterator */ class MethodProxyIteratorTest extends \PHPUnit_Framework_TestCase { public function testProxiesMagicCallsToInnermostIterator() { $i = new \ArrayIterator(); $proxy = new MethodProxyIterator(new MethodProxyIterator(new MethodProxyIterator($i))); $proxy->append('a'); $proxy->append('b'); $this->assertEquals(array('a', 'b'), $i->getArrayCopy()); $this->assertEquals(array('a', 'b'), $proxy->getArrayCopy()); } public function testUsesInnerIterator() { $i = new MethodProxyIterator(new ChunkedIterator(new \ArrayIterator(array(1, 2, 3, 4, 5)), 2)); $this->assertEquals(3, count(iterator_to_array($i, false))); } } guzzle/tests/Guzzle/Tests/Iterator/MapIteratorTest.php 0000604 00000001176 15173213566 0017213 0 ustar 00 <?php namespace Guzzle\Tests\Iterator; use Guzzle\Iterator\MapIterator; /** * @covers Guzzle\Iterator\MapIterator */ class MapIteratorTest extends \PHPUnit_Framework_TestCase { public function testFiltersValues() { $i = new MapIterator(new \ArrayIterator(range(0, 100)), function ($value) { return $value * 10; }); $this->assertEquals(range(0, 1000, 10), iterator_to_array($i, false)); } /** * @expectedException \InvalidArgumentException */ public function testValidatesCallable() { $i = new MapIterator(new \ArrayIterator(), new \stdClass()); } } guzzle/tests/Guzzle/Tests/Iterator/AppendIteratorTest.php 0000604 00000001240 15173213566 0017675 0 ustar 00 <?php namespace Guzzle\Tests\Iterator; use Guzzle\Iterator\AppendIterator; /** * @covers Guzzle\Iterator\AppendIterator */ class AppendIteratorTest extends \PHPUnit_Framework_TestCase { public function testTraversesIteratorsInOrder() { $a = new \ArrayIterator(array( 'a' => 1, 'b' => 2 )); $b = new \ArrayIterator(array()); $c = new \ArrayIterator(array( 'c' => 3, 'd' => 4 )); $i = new AppendIterator(); $i->append($a); $i->append($b); $i->append($c); $this->assertEquals(array(1, 2, 3, 4), iterator_to_array($i, false)); } } guzzle/tests/Guzzle/Tests/Service/Mock/MockClient.php 0000604 00000001721 15173213566 0016650 0 ustar 00 <?php namespace Guzzle\Tests\Service\Mock; use Guzzle\Common\Collection; use Guzzle\Service\Client; /** * Mock Guzzle Service */ class MockClient extends Client { /** * Factory method to create a new mock client * * @param array|Collection $config Configuration data. Array keys: * base_url - Base URL of web service * api_version - API version * scheme - URI scheme: http or https * * username - API username * * password - API password * * subdomain - Unfuddle account subdomain * * @return MockClient */ public static function factory($config = array()) { $config = Collection::fromConfig($config, array( 'base_url' => '{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); } } guzzle/tests/Guzzle/Tests/Service/Mock/Model/MockCommandIterator.php 0000604 00000001560 15173213566 0021563 0 ustar 00 <?php namespace Guzzle\Tests\Service\Mock\Model; use Guzzle\Service\Resource\ResourceIterator; class MockCommandIterator extends ResourceIterator { public $calledNext = 0; protected function sendRequest() { if ($this->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']; } public function next() { $this->calledNext++; parent::next(); } public function getResources() { return $this->resources; } public function getIteratedCount() { return $this->iteratedCount; } } guzzle/tests/Guzzle/Tests/Service/Mock/Command/Sub/Sub.php 0000604 00000000216 15173213566 0017456 0 ustar 00 <?php namespace Guzzle\Tests\Service\Mock\Command\Sub; use Guzzle\Tests\Service\Mock\Command\MockCommand; class Sub extends MockCommand {} guzzle/tests/Guzzle/Tests/Service/Mock/Command/IterableCommand.php 0000604 00000001512 15173213566 0021222 0 ustar 00 <?php namespace Guzzle\Tests\Service\Mock\Command; use Guzzle\Service\Description\Operation; class IterableCommand extends MockCommand { protected function createOperation() { return new Operation(array( 'name' => 'iterable_command', 'parameters' => array( 'page_size' => array('type' => 'integer'), 'next_token' => array('type' => 'string') ) )); } protected function build() { $this->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')); } } } guzzle/tests/Guzzle/Tests/Service/Mock/Command/MockCommand.php 0000604 00000001565 15173213566 0020374 0 ustar 00 <?php namespace Guzzle\Tests\Service\Mock\Command; use Guzzle\Service\Description\Operation; class MockCommand extends \Guzzle\Service\Command\AbstractCommand { protected function createOperation() { return new Operation(array( 'name' => get_called_class() == __CLASS__ ? 'mock_command' : 'sub.sub', 'httpMethod' => 'POST', 'parameters' => array( 'test' => array( 'default' => 123, 'required' => true, 'doc' => 'Test argument' ), '_internal' => array( 'default' => 'abc' ), 'foo' => array('filters' => array('strtoupper')) ) )); } protected function build() { $this->request = $this->client->createRequest(); } } guzzle/tests/Guzzle/Tests/Service/Mock/Command/OtherCommand.php 0000604 00000001434 15173213566 0020557 0 ustar 00 <?php namespace Guzzle\Tests\Service\Mock\Command; use Guzzle\Service\Description\Operation; class OtherCommand extends MockCommand { protected function createOperation() { return new Operation(array( 'name' => 'other_command', 'parameters' => array( 'test' => array( 'default' => '123', 'required' => true, 'doc' => 'Test argument' ), 'other' => array(), 'arg' => array('type' => 'string'), 'static' => array('static' => true, 'default' => 'this is static') ) )); } protected function build() { $this->request = $this->client->getRequest('HEAD'); } } guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionLoaderTest.php 0000604 00000014307 15173213566 0024011 0 ustar 00 <?php namespace Guzzle\Tests\Service\Description; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Description\ServiceDescriptionLoader; /** * @covers Guzzle\Service\Description\ServiceDescriptionLoader */ class ServiceDescriptionLoaderTest extends \Guzzle\Tests\GuzzleTestCase { public function testAllowsExtraData() { $d = ServiceDescription::factory(array( 'foo' => true, 'baz' => array('bar'), 'apiVersion' => '123', 'operations' => array() )); $this->assertEquals(true, $d->getData('foo')); $this->assertEquals(array('bar'), $d->getData('baz')); $this->assertEquals('123', $d->getApiVersion()); } public function testAllowsDeepNestedInheritance() { $d = ServiceDescription::factory(array( 'operations' => array( 'abstract' => array( 'httpMethod' => 'HEAD', 'parameters' => array( 'test' => array('type' => 'string', 'required' => true) ) ), 'abstract2' => array('uri' => '/test', 'extends' => 'abstract'), 'concrete' => array('extends' => 'abstract2'), 'override' => array('extends' => 'abstract', 'httpMethod' => 'PUT'), 'override2' => array('extends' => 'override', 'httpMethod' => 'POST', 'uri' => '/') ) )); $c = $d->getOperation('concrete'); $this->assertEquals('/test', $c->getUri()); $this->assertEquals('HEAD', $c->getHttpMethod()); $params = $c->getParams(); $param = $params['test']; $this->assertEquals('string', $param->getType()); $this->assertTrue($param->getRequired()); // Ensure that merging HTTP method does not make an array $this->assertEquals('PUT', $d->getOperation('override')->getHttpMethod()); $this->assertEquals('POST', $d->getOperation('override2')->getHttpMethod()); $this->assertEquals('/', $d->getOperation('override2')->getUri()); } /** * @expectedException RuntimeException */ public function testThrowsExceptionWhenExtendingMissingCommand() { ServiceDescription::factory(array( 'operations' => array( 'concrete' => array( 'extends' => 'missing' ) ) )); } public function testAllowsMultipleInheritance() { $description = ServiceDescription::factory(array( 'operations' => array( 'a' => array( 'httpMethod' => 'GET', 'parameters' => array( 'a1' => array( 'default' => 'foo', 'required' => true, 'prepend' => 'hi' ) ) ), 'b' => array( 'extends' => 'a', 'parameters' => array( 'b2' => array() ) ), 'c' => array( 'parameters' => array( 'a1' => array( 'default' => 'bar', 'required' => true, 'description' => 'test' ), 'c3' => array() ) ), 'd' => array( 'httpMethod' => 'DELETE', 'extends' => array('b', 'c'), 'parameters' => array( 'test' => array() ) ) ) )); $command = $description->getOperation('d'); $this->assertEquals('DELETE', $command->getHttpMethod()); $this->assertContains('a1', $command->getParamNames()); $this->assertContains('b2', $command->getParamNames()); $this->assertContains('c3', $command->getParamNames()); $this->assertContains('test', $command->getParamNames()); $this->assertTrue($command->getParam('a1')->getRequired()); $this->assertEquals('bar', $command->getParam('a1')->getDefault()); $this->assertEquals('test', $command->getParam('a1')->getDescription()); } public function testAddsOtherFields() { $description = ServiceDescription::factory(array( 'operations' => array(), 'description' => 'Foo', 'apiVersion' => 'bar' )); $this->assertEquals('Foo', $description->getDescription()); $this->assertEquals('bar', $description->getApiVersion()); } public function testCanLoadNestedExtends() { $description = ServiceDescription::factory(array( 'operations' => array( 'root' => array( 'class' => 'foo' ), 'foo' => array( 'extends' => 'root', 'parameters' => array( 'baz' => array('type' => 'string') ) ), 'foo_2' => array( 'extends' => 'foo', 'parameters' => array( 'bar' => array('type' => 'string') ) ), 'foo_3' => array( 'class' => 'bar', 'parameters' => array( 'bar2' => array('type' => 'string') ) ), 'foo_4' => array( 'extends' => array('foo_2', 'foo_3'), 'parameters' => array( 'bar3' => array('type' => 'string') ) ) ) )); $this->assertTrue($description->hasOperation('foo_4')); $foo4 = $description->getOperation('foo_4'); $this->assertTrue($foo4->hasParam('baz')); $this->assertTrue($foo4->hasParam('bar')); $this->assertTrue($foo4->hasParam('bar2')); $this->assertTrue($foo4->hasParam('bar3')); $this->assertEquals('bar', $foo4->getClass()); } } guzzle/tests/Guzzle/Tests/Service/Description/OperationTest.php 0000604 00000026271 15173213566 0021021 0 ustar 00 <?php namespace Guzzle\Tests\Service\Description; use Guzzle\Service\Description\Operation; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Description\ServiceDescription; /** * @covers Guzzle\Service\Description\Operation */ class OperationTest extends \Guzzle\Tests\GuzzleTestCase { public static function strtoupper($string) { return strtoupper($string); } public function testOperationIsDataObject() { $c = new Operation(array( 'name' => 'test', 'summary' => 'doc', 'notes' => 'notes', 'documentationUrl' => 'http://www.example.com', 'httpMethod' => 'POST', 'uri' => '/api/v1', 'responseClass' => 'array', 'responseNotes' => 'returns the json_decoded response', 'deprecated' => true, 'parameters' => array( 'key' => array( 'required' => true, 'type' => 'string', 'maxLength' => 10 ), 'key_2' => array( 'required' => true, 'type' => 'integer', 'default' => 10 ) ) )); $this->assertEquals('test', $c->getName()); $this->assertEquals('doc', $c->getSummary()); $this->assertEquals('http://www.example.com', $c->getDocumentationUrl()); $this->assertEquals('POST', $c->getHttpMethod()); $this->assertEquals('/api/v1', $c->getUri()); $this->assertEquals('array', $c->getResponseClass()); $this->assertEquals('returns the json_decoded response', $c->getResponseNotes()); $this->assertTrue($c->getDeprecated()); $this->assertEquals('Guzzle\\Service\\Command\\OperationCommand', $c->getClass()); $this->assertEquals(array( 'key' => new Parameter(array( 'name' => 'key', 'required' => true, 'type' => 'string', 'maxLength' => 10, 'parent' => $c )), 'key_2' => new Parameter(array( 'name' => 'key_2', 'required' => true, 'type' => 'integer', 'default' => 10, 'parent' => $c )) ), $c->getParams()); $this->assertEquals(new Parameter(array( 'name' => 'key_2', 'required' => true, 'type' => 'integer', 'default' => 10, 'parent' => $c )), $c->getParam('key_2')); $this->assertNull($c->getParam('afefwef')); $this->assertArrayNotHasKey('parent', $c->getParam('key_2')->toArray()); } public function testAllowsConcreteCommands() { $c = new Operation(array( 'name' => 'test', 'class' => 'Guzzle\\Service\\Command\ClosureCommand', 'parameters' => array( 'p' => new Parameter(array( 'name' => 'foo' )) ) )); $this->assertEquals('Guzzle\\Service\\Command\ClosureCommand', $c->getClass()); } public function testConvertsToArray() { $data = array( 'name' => 'test', 'class' => 'Guzzle\\Service\\Command\ClosureCommand', 'summary' => 'test', 'documentationUrl' => 'http://www.example.com', 'httpMethod' => 'PUT', 'uri' => '/', 'parameters' => array('p' => array('name' => 'foo')) ); $c = new Operation($data); $toArray = $c->toArray(); unset($data['name']); $this->assertArrayHasKey('parameters', $toArray); $this->assertInternalType('array', $toArray['parameters']); // Normalize the array unset($data['parameters']); unset($toArray['parameters']); $data['responseType'] = 'primitive'; $data['responseClass'] = 'array'; $this->assertEquals($data, $toArray); } public function testDeterminesIfHasParam() { $command = $this->getTestCommand(); $this->assertTrue($command->hasParam('data')); $this->assertFalse($command->hasParam('baz')); } public function testReturnsParamNames() { $command = $this->getTestCommand(); $this->assertEquals(array('data'), $command->getParamNames()); } protected function getTestCommand() { return new Operation(array( 'parameters' => array( 'data' => new Parameter(array( 'type' => 'string' )) ) )); } public function testCanBuildUpCommands() { $c = new Operation(array()); $c->setName('foo') ->setClass('Baz') ->setDeprecated(false) ->setSummary('summary') ->setDocumentationUrl('http://www.foo.com') ->setHttpMethod('PUT') ->setResponseNotes('oh') ->setResponseClass('string') ->setUri('/foo/bar') ->addParam(new Parameter(array( 'name' => 'test' ))); $this->assertEquals('foo', $c->getName()); $this->assertEquals('Baz', $c->getClass()); $this->assertEquals(false, $c->getDeprecated()); $this->assertEquals('summary', $c->getSummary()); $this->assertEquals('http://www.foo.com', $c->getDocumentationUrl()); $this->assertEquals('PUT', $c->getHttpMethod()); $this->assertEquals('oh', $c->getResponseNotes()); $this->assertEquals('string', $c->getResponseClass()); $this->assertEquals('/foo/bar', $c->getUri()); $this->assertEquals(array('test'), $c->getParamNames()); } public function testCanRemoveParams() { $c = new Operation(array()); $c->addParam(new Parameter(array('name' => 'foo'))); $this->assertTrue($c->hasParam('foo')); $c->removeParam('foo'); $this->assertFalse($c->hasParam('foo')); } public function testAddsNameToParametersIfNeeded() { $command = new Operation(array('parameters' => array('foo' => new Parameter(array())))); $this->assertEquals('foo', $command->getParam('foo')->getName()); } public function testContainsApiErrorInformation() { $command = $this->getOperation(); $this->assertEquals(1, count($command->getErrorResponses())); $arr = $command->toArray(); $this->assertEquals(1, count($arr['errorResponses'])); $command->addErrorResponse(400, 'Foo', 'Baz\\Bar'); $this->assertEquals(2, count($command->getErrorResponses())); $command->setErrorResponses(array()); $this->assertEquals(0, count($command->getErrorResponses())); } public function testHasNotes() { $o = new Operation(array('notes' => 'foo')); $this->assertEquals('foo', $o->getNotes()); $o->setNotes('bar'); $this->assertEquals('bar', $o->getNotes()); } public function testHasData() { $o = new Operation(array('data' => array('foo' => 'baz', 'bar' => 123))); $o->setData('test', false); $this->assertEquals('baz', $o->getData('foo')); $this->assertEquals(123, $o->getData('bar')); $this->assertNull($o->getData('wfefwe')); $this->assertEquals(array( 'parameters' => array(), 'class' => 'Guzzle\Service\Command\OperationCommand', 'data' => array('foo' => 'baz', 'bar' => 123, 'test' => false), 'responseClass' => 'array', 'responseType' => 'primitive' ), $o->toArray()); } public function testHasServiceDescription() { $s = new ServiceDescription(); $o = new Operation(array(), $s); $this->assertSame($s, $o->getServiceDescription()); } /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testValidatesResponseType() { $o = new Operation(array('responseClass' => 'array', 'responseType' => 'foo')); } public function testInfersResponseType() { $o = $this->getOperation(); $o->setServiceDescription(new ServiceDescription(array('models' => array('Foo' => array())))); $this->assertEquals('primitive', $o->getResponseType()); $this->assertEquals('primitive', $o->setResponseClass('boolean')->getResponseType()); $this->assertEquals('primitive', $o->setResponseClass('array')->getResponseType()); $this->assertEquals('primitive', $o->setResponseClass('integer')->getResponseType()); $this->assertEquals('primitive', $o->setResponseClass('string')->getResponseType()); $this->assertEquals('class', $o->setResponseClass('foo')->getResponseType()); $this->assertEquals('class', $o->setResponseClass(__CLASS__)->getResponseType()); $this->assertEquals('model', $o->setResponseClass('Foo')->getResponseType()); } public function testHasResponseType() { // infers in the constructor $o = new Operation(array('responseClass' => 'array')); $this->assertEquals('primitive', $o->getResponseType()); // Infers when set $o = new Operation(); $this->assertEquals('primitive', $o->getResponseType()); $this->assertEquals('model', $o->setResponseType('model')->getResponseType()); } public function testHasAdditionalParameters() { $o = new Operation(array( 'additionalParameters' => array( 'type' => 'string', 'name' => 'binks' ), 'parameters' => array( 'foo' => array('type' => 'integer') ) )); $this->assertEquals('string', $o->getAdditionalParameters()->getType()); $arr = $o->toArray(); $this->assertEquals(array( 'type' => 'string' ), $arr['additionalParameters']); } /** * @return Operation */ protected function getOperation() { return new Operation(array( 'name' => 'OperationTest', 'class' => get_class($this), 'parameters' => array( 'test' => array('type' => 'object'), 'bool_1' => array('default' => true, 'type' => 'boolean'), 'bool_2' => array('default' => false), 'float' => array('type' => 'numeric'), 'int' => array('type' => 'integer'), 'date' => array('type' => 'string'), 'timestamp' => array('type' => 'string'), 'string' => array('type' => 'string'), 'username' => array('type' => 'string', 'required' => true, 'filters' => 'strtolower'), 'test_function' => array('type' => 'string', 'filters' => __CLASS__ . '::strtoupper') ), 'errorResponses' => array( array('code' => 503, 'reason' => 'InsufficientCapacity', 'class' => 'Guzzle\\Exception\\RuntimeException') ) )); } } guzzle/tests/Guzzle/Tests/Service/Description/ServiceDescriptionTest.php 0000604 00000020413 15173213566 0022655 0 ustar 00 <?php namespace Guzzle\Tests\Service\Description; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Description\Operation; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Client; /** * @covers Guzzle\Service\Description\ServiceDescription */ class ServiceDescriptionTest extends \Guzzle\Tests\GuzzleTestCase { protected $serviceData; public function setup() { $this->serviceData = array( 'test_command' => new Operation(array( 'name' => 'test_command', 'description' => 'documentationForCommand', 'httpMethod' => 'DELETE', 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', 'parameters' => array( 'bucket' => array('required' => true), 'key' => array('required' => true) ) )) ); } /** * @covers Guzzle\Service\Description\ServiceDescription::factory * @covers Guzzle\Service\Description\ServiceDescriptionLoader::build */ public function testFactoryDelegatesToConcreteFactories() { $jsonFile = __DIR__ . '/../../TestData/test_service.json'; $this->assertInstanceOf('Guzzle\Service\Description\ServiceDescription', ServiceDescription::factory($jsonFile)); } public function testConstructor() { $service = new ServiceDescription(array('operations' => $this->serviceData)); $this->assertEquals(1, count($service->getOperations())); $this->assertFalse($service->hasOperation('foobar')); $this->assertTrue($service->hasOperation('test_command')); } public function testIsSerializable() { $service = new ServiceDescription(array('operations' => $this->serviceData)); $data = serialize($service); $d2 = unserialize($data); $this->assertEquals(serialize($service), serialize($d2)); } public function testSerializesParameters() { $service = new ServiceDescription(array( 'operations' => array( 'foo' => new Operation(array('parameters' => array('foo' => array('type' => 'string')))) ) )); $serialized = serialize($service); $this->assertContains('"parameters":{"foo":', $serialized); $service = unserialize($serialized); $this->assertTrue($service->getOperation('foo')->hasParam('foo')); } public function testAllowsForJsonBasedArrayParamsFunctionalTest() { $description = new ServiceDescription(array( 'operations' => array( 'test' => new Operation(array( 'httpMethod' => 'PUT', 'parameters' => array( 'data' => array( 'required' => true, 'filters' => 'json_encode', 'location' => 'body' ) ) )) ) )); $client = new Client(); $client->setDescription($description); $command = $client->getCommand('test', array( 'data' => array( 'foo' => 'bar' ) )); $request = $command->prepare(); $this->assertEquals('{"foo":"bar"}', (string) $request->getBody()); } public function testContainsModels() { $d = new ServiceDescription(array( 'operations' => array('foo' => array()), 'models' => array( 'Tag' => array('type' => 'object'), 'Person' => array('type' => 'object') ) )); $this->assertTrue($d->hasModel('Tag')); $this->assertTrue($d->hasModel('Person')); $this->assertFalse($d->hasModel('Foo')); $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $d->getModel('Tag')); $this->assertNull($d->getModel('Foo')); $this->assertContains('"models":{', serialize($d)); $this->assertEquals(array('Tag', 'Person'), array_keys($d->getModels())); } public function testCanAddModels() { $d = new ServiceDescription(array()); $this->assertFalse($d->hasModel('Foo')); $d->addModel(new Parameter(array('name' => 'Foo'))); $this->assertTrue($d->hasModel('Foo')); } public function testHasAttributes() { $d = new ServiceDescription(array( 'operations' => array(), 'name' => 'Name', 'description' => 'Description', 'apiVersion' => '1.24' )); $this->assertEquals('Name', $d->getName()); $this->assertEquals('Description', $d->getDescription()); $this->assertEquals('1.24', $d->getApiVersion()); $s = serialize($d); $this->assertContains('"name":"Name"', $s); $this->assertContains('"description":"Description"', $s); $this->assertContains('"apiVersion":"1.24"', $s); $d = unserialize($s); $this->assertEquals('Name', $d->getName()); $this->assertEquals('Description', $d->getDescription()); $this->assertEquals('1.24', $d->getApiVersion()); } public function testPersistsCustomAttributes() { $data = array( 'operations' => array('foo' => array('class' => 'foo', 'parameters' => array())), 'name' => 'Name', 'description' => 'Test', 'apiVersion' => '1.24', 'auth' => 'foo', 'keyParam' => 'bar' ); $d = new ServiceDescription($data); $d->setData('hello', 'baz'); $this->assertEquals('foo', $d->getData('auth')); $this->assertEquals('baz', $d->getData('hello')); $this->assertEquals('bar', $d->getData('keyParam')); // responseClass and responseType are added by default $data['operations']['foo']['responseClass'] = 'array'; $data['operations']['foo']['responseType'] = 'primitive'; $this->assertEquals($data + array('hello' => 'baz'), json_decode($d->serialize(), true)); } public function testHasToArray() { $data = array( 'operations' => array(), 'name' => 'Name', 'description' => 'Test' ); $d = new ServiceDescription($data); $arr = $d->toArray(); $this->assertEquals('Name', $arr['name']); $this->assertEquals('Test', $arr['description']); } public function testReturnsNullWhenRetrievingMissingOperation() { $s = new ServiceDescription(array()); $this->assertNull($s->getOperation('foo')); } public function testCanAddOperations() { $s = new ServiceDescription(array()); $this->assertFalse($s->hasOperation('Foo')); $s->addOperation(new Operation(array('name' => 'Foo'))); $this->assertTrue($s->hasOperation('Foo')); } /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testValidatesOperationTypes() { $s = new ServiceDescription(array( 'operations' => array('foo' => new \stdClass()) )); } public function testHasBaseUrl() { $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); $this->assertEquals('http://foo.com', $description->getBaseUrl()); $description->setBaseUrl('http://foobar.com'); $this->assertEquals('http://foobar.com', $description->getBaseUrl()); } public function testCanUseBasePath() { $description = new ServiceDescription(array('basePath' => 'http://foo.com')); $this->assertEquals('http://foo.com', $description->getBaseUrl()); } public function testModelsHaveNames() { $desc = array( 'models' => array( 'date' => array('type' => 'string'), 'user'=> array( 'type' => 'object', 'properties' => array( 'dob' => array('$ref' => 'date') ) ) ) ); $s = ServiceDescription::factory($desc); $this->assertEquals('date', $s->getModel('date')->getName()); $this->assertEquals('dob', $s->getModel('user')->getProperty('dob')->getName()); } } guzzle/tests/Guzzle/Tests/Service/Description/SchemaValidatorTest.php 0000604 00000030221 15173213566 0022115 0 ustar 00 <?php namespace Guzzle\Tests\Service\Description; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Description\SchemaValidator; /** * @covers Guzzle\Service\Description\SchemaValidator */ class SchemaValidatorTest extends \Guzzle\Tests\GuzzleTestCase { /** @var SchemaValidator */ protected $validator; public function setUp() { $this->validator = new SchemaValidator(); } public function testValidatesArrayListsAreNumericallyIndexed() { $value = array(array(1)); $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); $this->assertEquals( array('[Foo][0] must be an array of properties. Got a numerically indexed array.'), $this->validator->getErrors() ); } public function testValidatesArrayListsContainProperItems() { $value = array(true); $this->assertFalse($this->validator->validate($this->getComplexParam(), $value)); $this->assertEquals( array('[Foo][0] must be of type object'), $this->validator->getErrors() ); } public function testAddsDefaultValuesInLists() { $value = array(array()); $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); $this->assertEquals(array(array('Bar' => true)), $value); } public function testMergesDefaultValuesInLists() { $value = array( array('Baz' => 'hello!'), array('Bar' => false) ); $this->assertTrue($this->validator->validate($this->getComplexParam(), $value)); $this->assertEquals(array( array( 'Baz' => 'hello!', 'Bar' => true ), array('Bar' => false) ), $value); } public function testCorrectlyConvertsParametersToArrayWhenArraysArePresent() { $param = $this->getComplexParam(); $result = $param->toArray(); $this->assertInternalType('array', $result['items']); $this->assertEquals('array', $result['type']); $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getItems()); } public function testAllowsInstanceOf() { $p = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'instanceOf' => get_class($this) )); $this->assertTrue($this->validator->validate($p, $this)); $this->assertFalse($this->validator->validate($p, $p)); $this->assertEquals(array('[foo] must be an instance of ' . __CLASS__), $this->validator->getErrors()); } public function testEnforcesInstanceOfOnlyWhenObject() { $p = new Parameter(array( 'name' => 'foo', 'type' => array('object', 'string'), 'instanceOf' => get_class($this) )); $this->assertTrue($this->validator->validate($p, $this)); $s = 'test'; $this->assertTrue($this->validator->validate($p, $s)); } public function testConvertsObjectsToArraysWhenToArrayInterface() { $o = $this->getMockBuilder('Guzzle\Common\ToArrayInterface') ->setMethods(array('toArray')) ->getMockForAbstractClass(); $o->expects($this->once()) ->method('toArray') ->will($this->returnValue(array( 'foo' => 'bar' ))); $p = new Parameter(array( 'name' => 'test', 'type' => 'object', 'properties' => array( 'foo' => array('required' => 'true') ) )); $this->assertTrue($this->validator->validate($p, $o)); } public function testMergesValidationErrorsInPropertiesWithParent() { $p = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'properties' => array( 'bar' => array('type' => 'string', 'required' => true, 'description' => 'This is what it does'), 'test' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 5), 'test2' => array('type' => 'string', 'minLength' => 2, 'maxLength' => 2), 'test3' => array('type' => 'integer', 'minimum' => 100), 'test4' => array('type' => 'integer', 'maximum' => 10), 'test5' => array('type' => 'array', 'maxItems' => 2), 'test6' => array('type' => 'string', 'enum' => array('a', 'bc')), 'test7' => array('type' => 'string', 'pattern' => '/[0-9]+/'), 'test8' => array('type' => 'number'), 'baz' => array( 'type' => 'array', 'minItems' => 2, 'required' => true, "items" => array("type" => "string") ) ) )); $value = array( 'test' => 'a', 'test2' => 'abc', 'baz' => array(false), 'test3' => 10, 'test4' => 100, 'test5' => array(1, 3, 4), 'test6' => 'Foo', 'test7' => 'abc', 'test8' => 'abc' ); $this->assertFalse($this->validator->validate($p, $value)); $this->assertEquals(array ( '[foo][bar] is a required string: This is what it does', '[foo][baz] must contain 2 or more elements', '[foo][baz][0] must be of type string', '[foo][test2] length must be less than or equal to 2', '[foo][test3] must be greater than or equal to 100', '[foo][test4] must be less than or equal to 10', '[foo][test5] must contain 2 or fewer elements', '[foo][test6] must be one of "a" or "bc"', '[foo][test7] must match the following regular expression: /[0-9]+/', '[foo][test8] must be of type number', '[foo][test] length must be greater than or equal to 2', ), $this->validator->getErrors()); } public function testHandlesNullValuesInArraysWithDefaults() { $p = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'required' => true, 'properties' => array( 'bar' => array( 'type' => 'object', 'required' => true, 'properties' => array( 'foo' => array('default' => 'hi') ) ) ) )); $value = array(); $this->assertTrue($this->validator->validate($p, $value)); $this->assertEquals(array('bar' => array('foo' => 'hi')), $value); } public function testFailsWhenNullValuesInArraysWithNoDefaults() { $p = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'required' => true, 'properties' => array( 'bar' => array( 'type' => 'object', 'required' => true, 'properties' => array('foo' => array('type' => 'string')) ) ) )); $value = array(); $this->assertFalse($this->validator->validate($p, $value)); $this->assertEquals(array('[foo][bar] is a required object'), $this->validator->getErrors()); } public function testChecksTypes() { $p = new SchemaValidator(); $r = new \ReflectionMethod($p, 'determineType'); $r->setAccessible(true); $this->assertEquals('any', $r->invoke($p, 'any', 'hello')); $this->assertEquals(false, $r->invoke($p, 'foo', 'foo')); $this->assertEquals('string', $r->invoke($p, 'string', 'hello')); $this->assertEquals(false, $r->invoke($p, 'string', false)); $this->assertEquals('integer', $r->invoke($p, 'integer', 1)); $this->assertEquals(false, $r->invoke($p, 'integer', 'abc')); $this->assertEquals('numeric', $r->invoke($p, 'numeric', 1)); $this->assertEquals('numeric', $r->invoke($p, 'numeric', '1')); $this->assertEquals('number', $r->invoke($p, 'number', 1)); $this->assertEquals('number', $r->invoke($p, 'number', '1')); $this->assertEquals(false, $r->invoke($p, 'numeric', 'a')); $this->assertEquals('boolean', $r->invoke($p, 'boolean', true)); $this->assertEquals('boolean', $r->invoke($p, 'boolean', false)); $this->assertEquals(false, $r->invoke($p, 'boolean', 'false')); $this->assertEquals('null', $r->invoke($p, 'null', null)); $this->assertEquals(false, $r->invoke($p, 'null', 'abc')); $this->assertEquals('array', $r->invoke($p, 'array', array())); $this->assertEquals(false, $r->invoke($p, 'array', 'foo')); } public function testValidatesFalseAdditionalProperties() { $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'properties' => array('bar' => array('type' => 'string')), 'additionalProperties' => false )); $value = array('test' => '123'); $this->assertFalse($this->validator->validate($param, $value)); $this->assertEquals(array('[foo][test] is not an allowed property'), $this->validator->getErrors()); $value = array('bar' => '123'); $this->assertTrue($this->validator->validate($param, $value)); } public function testAllowsUndefinedAdditionalProperties() { $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'properties' => array('bar' => array('type' => 'string')) )); $value = array('test' => '123'); $this->assertTrue($this->validator->validate($param, $value)); } public function testValidatesAdditionalProperties() { $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'properties' => array('bar' => array('type' => 'string')), 'additionalProperties' => array('type' => 'integer') )); $value = array('test' => 'foo'); $this->assertFalse($this->validator->validate($param, $value)); $this->assertEquals(array('[foo][test] must be of type integer'), $this->validator->getErrors()); } public function testValidatesAdditionalPropertiesThatArrayArrays() { $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'additionalProperties' => array( 'type' => 'array', 'items' => array('type' => 'string') ) )); $value = array('test' => array(true)); $this->assertFalse($this->validator->validate($param, $value)); $this->assertEquals(array('[foo][test][0] must be of type string'), $this->validator->getErrors()); } public function testIntegersCastToStringWhenTypeMismatch() { $param = new Parameter(array('name' => 'test', 'type' => 'string')); $value = 12; $this->assertTrue($this->validator->validate($param, $value)); $this->assertEquals('12', $value); } public function testRequiredMessageIncludesType() { $param = new Parameter(array('name' => 'test', 'type' => array('string', 'boolean'), 'required' => true)); $value = null; $this->assertFalse($this->validator->validate($param, $value)); $this->assertEquals(array('[test] is a required string or boolean'), $this->validator->getErrors()); } protected function getComplexParam() { return new Parameter(array( 'name' => 'Foo', 'type' => 'array', 'required' => true, 'min' => 1, 'items' => array( 'type' => 'object', 'properties' => array( 'Baz' => array( 'type' => 'string', ), 'Bar' => array( 'required' => true, 'type' => 'boolean', 'default' => true ) ) ) )); } } guzzle/tests/Guzzle/Tests/Service/Description/ParameterTest.php 0000604 00000033360 15173213566 0020776 0 ustar 00 <?php namespace Guzzle\Tests\Service\Description; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Description\ServiceDescription; /** * @covers Guzzle\Service\Description\Parameter */ class ParameterTest extends \Guzzle\Tests\GuzzleTestCase { protected $data = array( 'name' => 'foo', 'type' => 'bar', 'required' => true, 'default' => '123', 'description' => '456', 'minLength' => 2, 'maxLength' => 5, 'location' => 'body', 'static' => 'static!', 'filters' => array('trim', 'json_encode') ); public function testCreatesParamFromArray() { $p = new Parameter($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->getDescription()); $this->assertEquals(2, $p->getMinLength()); $this->assertEquals(5, $p->getMaxLength()); $this->assertEquals('body', $p->getLocation()); $this->assertEquals('static!', $p->getStatic()); $this->assertEquals(array('trim', 'json_encode'), $p->getFilters()); } public function testCanConvertToArray() { $p = new Parameter($this->data); unset($this->data['name']); $this->assertEquals($this->data, $p->toArray()); } public function testUsesStatic() { $d = $this->data; $d['default'] = 'booboo'; $d['static'] = true; $p = new Parameter($d); $this->assertEquals('booboo', $p->getValue('bar')); } public function testUsesDefault() { $d = $this->data; $d['default'] = 'foo'; $d['static'] = null; $p = new Parameter($d); $this->assertEquals('foo', $p->getValue(null)); } public function testReturnsYourValue() { $d = $this->data; $d['static'] = null; $p = new Parameter($d); $this->assertEquals('foo', $p->getValue('foo')); } public function testZeroValueDoesNotCauseDefaultToBeReturned() { $d = $this->data; $d['default'] = '1'; $d['static'] = null; $p = new Parameter($d); $this->assertEquals('0', $p->getValue('0')); } public function testFiltersValues() { $d = $this->data; $d['static'] = null; $d['filters'] = 'strtoupper'; $p = new Parameter($d); $this->assertEquals('FOO', $p->filter('foo')); } public function testConvertsBooleans() { $p = new Parameter(array('type' => 'boolean')); $this->assertEquals(true, $p->filter('true')); $this->assertEquals(false, $p->filter('false')); } public function testUsesArrayByDefaultForFilters() { $d = $this->data; $d['filters'] = null; $p = new Parameter($d); $this->assertEquals(array(), $p->getFilters()); } public function testAllowsSimpleLocationValue() { $p = new Parameter(array('name' => 'myname', 'location' => 'foo', 'sentAs' => 'Hello')); $this->assertEquals('foo', $p->getLocation()); $this->assertEquals('Hello', $p->getSentAs()); } public function testParsesTypeValues() { $p = new Parameter(array('type' => 'foo')); $this->assertEquals('foo', $p->getType()); } /** * @expectedException InvalidArgumentException * @expectedExceptionMessage A [method] value must be specified for each complex filter */ public function testValidatesComplexFilters() { $p = new Parameter(array('filters' => array(array('args' => 'foo')))); } public function testCanBuildUpParams() { $p = new Parameter(array()); $p->setName('foo') ->setDescription('c') ->setFilters(array('d')) ->setLocation('e') ->setSentAs('f') ->setMaxLength(1) ->setMinLength(1) ->setMinimum(2) ->setMaximum(2) ->setMinItems(3) ->setMaxItems(3) ->setRequired(true) ->setStatic(true) ->setDefault('h') ->setType('i'); $p->addFilter('foo'); $this->assertEquals('foo', $p->getName()); $this->assertEquals('h', $p->getDefault()); $this->assertEquals('c', $p->getDescription()); $this->assertEquals(array('d', 'foo'), $p->getFilters()); $this->assertEquals('e', $p->getLocation()); $this->assertEquals('f', $p->getSentAs()); $this->assertEquals(1, $p->getMaxLength()); $this->assertEquals(1, $p->getMinLength()); $this->assertEquals(2, $p->getMaximum()); $this->assertEquals(2, $p->getMinimum()); $this->assertEquals(3, $p->getMaxItems()); $this->assertEquals(3, $p->getMinItems()); $this->assertEquals(true, $p->getRequired()); $this->assertEquals(true, $p->getStatic()); $this->assertEquals('i', $p->getType()); } public function testAllowsNestedShape() { $command = $this->getServiceBuilder()->get('mock')->getCommand('mock_command')->getOperation(); $param = new Parameter(array( 'parent' => $command, 'name' => 'foo', 'type' => 'object', 'location' => 'query', 'properties' => array( 'foo' => array( 'type' => 'object', 'required' => true, 'properties' => array( 'baz' => array( 'name' => 'baz', 'type' => 'bool', ) ) ), 'bar' => array( 'name' => 'bar', 'default' => '123' ) ) )); $this->assertSame($command, $param->getParent()); $this->assertNotEmpty($param->getProperties()); $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('foo')); $this->assertSame($param, $param->getProperty('foo')->getParent()); $this->assertSame($param->getProperty('foo'), $param->getProperty('foo')->getProperty('baz')->getParent()); $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $param->getProperty('bar')); $this->assertSame($param, $param->getProperty('bar')->getParent()); $array = $param->toArray(); $this->assertInternalType('array', $array['properties']); $this->assertArrayHasKey('foo', $array['properties']); $this->assertArrayHasKey('bar', $array['properties']); } public function testAllowsComplexFilters() { $that = $this; $param = new Parameter(array()); $param->setFilters(array(array('method' => function ($a, $b, $c, $d) use ($that, $param) { $that->assertEquals('test', $a); $that->assertEquals('my_value!', $b); $that->assertEquals('bar', $c); $that->assertSame($param, $d); return 'abc' . $b; }, 'args' => array('test', '@value', 'bar', '@api')))); $this->assertEquals('abcmy_value!', $param->filter('my_value!')); } public function testCanChangeParentOfNestedParameter() { $param1 = new Parameter(array('name' => 'parent')); $param2 = new Parameter(array('name' => 'child')); $param2->setParent($param1); $this->assertSame($param1, $param2->getParent()); } public function testCanRemoveFromNestedStructure() { $param1 = new Parameter(array('name' => 'parent')); $param2 = new Parameter(array('name' => 'child')); $param1->addProperty($param2); $this->assertSame($param1, $param2->getParent()); $this->assertSame($param2, $param1->getProperty('child')); // Remove a single child from the structure $param1->removeProperty('child'); $this->assertNull($param1->getProperty('child')); // Remove the entire structure $param1->addProperty($param2); $param1->removeProperty('child'); $this->assertNull($param1->getProperty('child')); } public function testAddsAdditionalProperties() { $p = new Parameter(array( 'type' => 'object', 'additionalProperties' => array('type' => 'string') )); $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getAdditionalProperties()); $this->assertNull($p->getAdditionalProperties()->getAdditionalProperties()); $p = new Parameter(array('type' => 'object')); $this->assertTrue($p->getAdditionalProperties()); } public function testAddsItems() { $p = new Parameter(array( 'type' => 'array', 'items' => array('type' => 'string') )); $this->assertInstanceOf('Guzzle\Service\Description\Parameter', $p->getItems()); $out = $p->toArray(); $this->assertEquals('array', $out['type']); $this->assertInternalType('array', $out['items']); } public function testHasExtraProperties() { $p = new Parameter(); $this->assertEquals(array(), $p->getData()); $p->setData(array('foo' => 'bar')); $this->assertEquals('bar', $p->getData('foo')); $p->setData('baz', 'boo'); $this->assertEquals(array('foo' => 'bar', 'baz' => 'boo'), $p->getData()); } public function testCanRetrieveKnownPropertiesUsingDataMethod() { $p = new Parameter(); $this->assertEquals(null, $p->getData('foo')); $p->setName('test'); $this->assertEquals('test', $p->getData('name')); } public function testHasInstanceOf() { $p = new Parameter(); $this->assertNull($p->getInstanceOf()); $p->setInstanceOf('Foo'); $this->assertEquals('Foo', $p->getInstanceOf()); } public function testHasPattern() { $p = new Parameter(); $this->assertNull($p->getPattern()); $p->setPattern('/[0-9]+/'); $this->assertEquals('/[0-9]+/', $p->getPattern()); } public function testHasEnum() { $p = new Parameter(); $this->assertNull($p->getEnum()); $p->setEnum(array('foo', 'bar')); $this->assertEquals(array('foo', 'bar'), $p->getEnum()); } public function testSerializesItems() { $p = new Parameter(array( 'type' => 'object', 'additionalProperties' => array('type' => 'string') )); $this->assertEquals(array( 'type' => 'object', 'additionalProperties' => array('type' => 'string') ), $p->toArray()); } public function testResolvesRefKeysRecursively() { $description = new ServiceDescription(array( 'models' => array( 'JarJar' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), 'Anakin' => array('type' => 'array', 'items' => array('$ref' => 'JarJar')) ) )); $p = new Parameter(array('$ref' => 'Anakin', 'description' => 'added'), $description); $this->assertEquals(array( 'type' => 'array', 'items' => array('type' => 'string', 'default' => 'Mesa address tha senate!'), 'description' => 'added' ), $p->toArray()); } public function testResolvesExtendsRecursively() { $jarJar = array('type' => 'string', 'default' => 'Mesa address tha senate!', 'description' => 'a'); $anakin = array('type' => 'array', 'items' => array('extends' => 'JarJar', 'description' => 'b')); $description = new ServiceDescription(array( 'models' => array('JarJar' => $jarJar, 'Anakin' => $anakin) )); // Description attribute will be updated, and format added $p = new Parameter(array('extends' => 'Anakin', 'format' => 'date'), $description); $this->assertEquals(array( 'type' => 'array', 'format' => 'date', 'items' => array( 'type' => 'string', 'default' => 'Mesa address tha senate!', 'description' => 'b' ) ), $p->toArray()); } public function testHasKeyMethod() { $p = new Parameter(array('name' => 'foo', 'sentAs' => 'bar')); $this->assertEquals('bar', $p->getWireName()); $p->setSentAs(null); $this->assertEquals('foo', $p->getWireName()); } public function testIncludesNameInToArrayWhenItemsAttributeHasName() { $p = new Parameter(array( 'type' => 'array', 'name' => 'Abc', 'items' => array( 'name' => 'Foo', 'type' => 'object' ) )); $result = $p->toArray(); $this->assertEquals(array( 'type' => 'array', 'items' => array( 'name' => 'Foo', 'type' => 'object', 'additionalProperties' => true ) ), $result); } public function dateTimeProvider() { $d = 'October 13, 2012 16:15:46 UTC'; return array( array($d, 'date-time', '2012-10-13T16:15:46Z'), array($d, 'date', '2012-10-13'), array($d, 'timestamp', strtotime($d)), array(new \DateTime($d), 'timestamp', strtotime($d)) ); } /** * @dataProvider dateTimeProvider */ public function testAppliesFormat($d, $format, $result) { $p = new Parameter(); $p->setFormat($format); $this->assertEquals($format, $p->getFormat()); $this->assertEquals($result, $p->filter($d)); } } guzzle/tests/Guzzle/Tests/Service/Description/SchemaFormatterTest.php 0000604 00000004063 15173213566 0022140 0 ustar 00 <?php namespace Guzzle\Tests\Service\Description; use Guzzle\Service\Description\SchemaFormatter; /** * @covers Guzzle\Service\Description\SchemaFormatter */ class SchemaFormatterTest extends \Guzzle\Tests\GuzzleTestCase { public function dateTimeProvider() { $dateUtc = 'October 13, 2012 16:15:46 UTC'; $dateOffset = 'October 13, 2012 10:15:46 -06:00'; $expectedDateTime = '2012-10-13T16:15:46Z'; return array( array('foo', 'does-not-exist', 'foo'), array($dateUtc, 'date-time', $expectedDateTime), array($dateUtc, 'date-time-http', 'Sat, 13 Oct 2012 16:15:46 GMT'), array($dateUtc, 'date', '2012-10-13'), array($dateUtc, 'timestamp', strtotime($dateUtc)), array(new \DateTime($dateUtc), 'timestamp', strtotime($dateUtc)), array($dateUtc, 'time', '16:15:46'), array(strtotime($dateUtc), 'time', '16:15:46'), array(strtotime($dateUtc), 'timestamp', strtotime($dateUtc)), array('true', 'boolean-string', 'true'), array(true, 'boolean-string', 'true'), array('false', 'boolean-string', 'false'), array(false, 'boolean-string', 'false'), array('1350144946', 'date-time', $expectedDateTime), array(1350144946, 'date-time', $expectedDateTime), array($dateOffset, 'date-time', $expectedDateTime) ); } /** * @dataProvider dateTimeProvider */ public function testFilters($value, $format, $result) { $this->assertEquals($result, SchemaFormatter::format($format, $value)); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testValidatesDateTimeInput() { SchemaFormatter::format('date-time', false); } public function testEnsuresTimestampsAreIntegers() { $t = time(); $result = SchemaFormatter::format('timestamp', $t); $this->assertSame($t, $result); $this->assertInternalType('int', $result); } } guzzle/tests/Guzzle/Tests/Service/AbstractConfigLoaderTest.php 0000604 00000012653 15173213566 0020615 0 ustar 00 <?php namespace Guzzle\Tests\Service; /** * @covers Guzzle\Service\AbstractConfigLoader */ class AbstractConfigLoaderTest extends \Guzzle\Tests\GuzzleTestCase { /** @var \Guzzle\Service\AbstractConfigLoader */ protected $loader; /** @var array Any files that need to be deleted on tear down */ protected $cleanup = array(); public function setUp() { $this->loader = $this->getMockBuilder('Guzzle\Service\AbstractConfigLoader') ->setMethods(array('build')) ->getMockForAbstractClass(); } public function tearDown() { foreach ($this->cleanup as $file) { unlink($file); } } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testOnlyLoadsSupportedTypes() { $this->loader->load(new \stdClass()); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException * @expectedExceptionMessage Unable to open fooooooo.json */ public function testFileMustBeReadable() { $this->loader->load('fooooooo.json'); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException * @expectedExceptionMessage Unknown file extension */ public function testMustBeSupportedExtension() { $this->loader->load(dirname(__DIR__) . '/TestData/FileBody.txt'); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException * @expectedExceptionMessage Error loading JSON data from */ public function testJsonMustBeValue() { $filename = tempnam(sys_get_temp_dir(), 'json') . '.json'; file_put_contents($filename, '{/{./{}foo'); $this->cleanup[] = $filename; $this->loader->load($filename); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException * @expectedExceptionMessage PHP files must return an array */ public function testPhpFilesMustReturnAnArray() { $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; file_put_contents($filename, '<?php $fdr = false;'); $this->cleanup[] = $filename; $this->loader->load($filename); } public function testLoadsPhpFileIncludes() { $filename = tempnam(sys_get_temp_dir(), 'php') . '.php'; file_put_contents($filename, '<?php return array("foo" => "bar");'); $this->cleanup[] = $filename; $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); $config = $this->loader->load($filename); $this->assertEquals(array('foo' => 'bar'), $config); } public function testCanCreateFromJson() { $file = dirname(__DIR__) . '/TestData/services/json1.json'; // The build method will just return the config data $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); $data = $this->loader->load($file); // Ensure that the config files were merged using the includes directives $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']); } public function testUsesAliases() { $file = dirname(__DIR__) . '/TestData/services/json1.json'; $this->loader->addAlias('foo', $file); // The build method will just return the config data $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); $data = $this->loader->load('foo'); $this->assertEquals('bar', $data['services']['foo']['params']['baz']); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException * @expectedExceptionMessage Unable to open foo.json */ public function testCanRemoveAliases() { $file = dirname(__DIR__) . '/TestData/services/json1.json'; $this->loader->addAlias('foo.json', $file); $this->loader->removeAlias('foo.json'); $this->loader->load('foo.json'); } public function testCanLoadArraysWithIncludes() { $file = dirname(__DIR__) . '/TestData/services/json1.json'; $config = array('includes' => array($file)); // The build method will just return the config data $this->loader->expects($this->exactly(1))->method('build')->will($this->returnArgument(0)); $data = $this->loader->load($config); $this->assertEquals('bar', $data['services']['foo']['params']['baz']); } public function testDoesNotEnterInfiniteLoop() { $prefix = $file = dirname(__DIR__) . '/TestData/description'; $this->loader->load("{$prefix}/baz.json"); $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); // Ensure that the internal list of loaded files is reset $this->loader->load("{$prefix}/../test_service2.json"); $this->assertCount(1, $this->readAttribute($this->loader, 'loadedFiles')); // Ensure that previously loaded files will be reloaded when starting fresh $this->loader->load("{$prefix}/baz.json"); $this->assertCount(4, $this->readAttribute($this->loader, 'loadedFiles')); } } guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderTest.php 0000604 00000025553 15173213566 0021075 0 ustar 00 <?php namespace Guzzle\Tests\Service; use Guzzle\Plugin\History\HistoryPlugin; use Guzzle\Service\Builder\ServiceBuilder; use Guzzle\Service\Client; /** * @covers Guzzle\Service\Builder\ServiceBuilder */ class ServiceBuilderTest extends \Guzzle\Tests\GuzzleTestCase { protected $arrayData = array( 'michael.mock' => array( 'class' => 'Guzzle\Tests\Service\Mock\MockClient', 'params' => array( 'username' => 'michael', 'password' => 'testing123', 'subdomain' => 'michael', ), ), 'billy.mock' => array( 'alias' => 'Hello!', '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' ) ); public function testAllowsSerialization() { $builder = ServiceBuilder::factory($this->arrayData); $cached = unserialize(serialize($builder)); $this->assertEquals($cached, $builder); } public function testDelegatesFactoryMethodToAbstractFactory() { $builder = ServiceBuilder::factory($this->arrayData); $c = $builder->get('michael.mock'); $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $c); } /** * @expectedException Guzzle\Service\Exception\ServiceNotFoundException * @expectedExceptionMessage No service is registered as foobar */ public function testThrowsExceptionWhenGettingInvalidClient() { ServiceBuilder::factory($this->arrayData)->get('foobar'); } 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')); } 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')); } 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); } 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']); } 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')); } 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')); } public function testFactoryDoesNotRequireParams() { $b = ServiceBuilder::factory($this->arrayData); $s = $b->get('missing_params'); $this->assertEquals('billy', $s->getConfig('username')); } 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')); } 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' ) ) )); // Add an event listener to pick up client creation events $emits = 0; $builder->getEventDispatcher()->addListener('service_builder.create_client', function($event) use (&$emits) { $emits++; }); // 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); } 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 testAddsGlobalPlugins() { $b = new ServiceBuilder($this->arrayData); $b->addGlobalPlugin(new HistoryPlugin()); $s = $b->get('michael.mock'); $this->assertTrue($s->getEventDispatcher()->hasListeners('request.sent')); } public function testCanGetData() { $b = new ServiceBuilder($this->arrayData); $this->assertEquals($this->arrayData['michael.mock'], $b->getData('michael.mock')); $this->assertNull($b->getData('ewofweoweofe')); } public function testCanGetByAlias() { $b = new ServiceBuilder($this->arrayData); $this->assertSame($b->get('billy.mock'), $b->get('Hello!')); } public function testCanOverwriteParametersForThrowawayClients() { $b = new ServiceBuilder($this->arrayData); $c1 = $b->get('michael.mock'); $this->assertEquals('michael', $c1->getConfig('username')); $c2 = $b->get('michael.mock', array('username' => 'jeremy')); $this->assertEquals('jeremy', $c2->getConfig('username')); } public function testGettingAThrowawayClientWithParametersDoesNotAffectGettingOtherClients() { $b = new ServiceBuilder($this->arrayData); $c1 = $b->get('michael.mock', array('username' => 'jeremy')); $this->assertEquals('jeremy', $c1->getConfig('username')); $c2 = $b->get('michael.mock'); $this->assertEquals('michael', $c2->getConfig('username')); } public function testCanUseArbitraryData() { $b = new ServiceBuilder(); $b['a'] = 'foo'; $this->assertTrue(isset($b['a'])); $this->assertEquals('foo', $b['a']); unset($b['a']); $this->assertFalse(isset($b['a'])); } public function testCanRegisterServiceData() { $b = new ServiceBuilder(); $b['a'] = array( 'class' => 'Guzzle\Tests\Service\Mock\MockClient', 'params' => array( 'username' => 'billy', 'password' => 'passw0rd', 'subdomain' => 'billy', ) ); $this->assertTrue(isset($b['a'])); $this->assertInstanceOf('Guzzle\Tests\Service\Mock\MockClient', $b['a']); $client = $b['a']; unset($b['a']); $this->assertFalse(isset($b['a'])); // Ensure that instantiated clients can be registered $b['mock'] = $client; $this->assertSame($client, $b['mock']); } } guzzle/tests/Guzzle/Tests/Service/Builder/ServiceBuilderLoaderTest.php 0000604 00000012337 15173213566 0022220 0 ustar 00 <?php namespace Guzzle\Tests\Service\Builder; use Guzzle\Service\Builder\ServiceBuilderLoader; /** * @covers Guzzle\Service\Builder\ServiceBuilderLoader */ class ServiceBuilderLoaderTest extends \Guzzle\Tests\GuzzleTestCase { public function testBuildsServiceBuilders() { $arrayFactory = new ServiceBuilderLoader(); $data = array( 'services' => 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->load($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 ServiceBuilderLoader(); $data = array( 'services' => array( 'foo' => array( 'extends' => 'abstract' ) ) ); $builder = $arrayFactory->load($data); } public function testAllowsGlobalParameterOverrides() { $arrayFactory = new ServiceBuilderLoader(); $data = array( 'services' => array( 'foo' => array( 'params' => array( 'foo' => 'baz', 'bar' => 'boo' ) ) ) ); $builder = $arrayFactory->load($data, array( 'bar' => 'jar', 'far' => 'car' )); $compiled = json_decode($builder->serialize(), true); $this->assertEquals(array( 'foo' => 'baz', 'bar' => 'jar', 'far' => 'car' ), $compiled['foo']['params']); } public function tstDoesNotErrorOnCircularReferences() { $arrayFactory = new ServiceBuilderLoader(); $arrayFactory->load(array( 'services' => array( 'too' => array('extends' => 'ball'), 'ball' => array('extends' => 'too'), ) )); } public function configProvider() { $foo = array( 'extends' => 'bar', 'class' => 'stdClass', 'params' => array('a' => 'test', 'b' => '456') ); return array( array( // Does not extend the existing `foo` service but overwrites it array( 'services' => array( 'foo' => $foo, 'bar' => array('params' => array('baz' => '123')) ) ), array( 'services' => array( 'foo' => array('class' => 'Baz') ) ), array( 'services' => array( 'foo' => array('class' => 'Baz'), 'bar' => array('params' => array('baz' => '123')) ) ) ), array( // Extends the existing `foo` service array( 'services' => array( 'foo' => $foo, 'bar' => array('params' => array('baz' => '123')) ) ), array( 'services' => array( 'foo' => array( 'extends' => 'foo', 'params' => array('b' => '123', 'c' => 'def') ) ) ), array( 'services' => array( 'foo' => array( 'extends' => 'bar', 'class' => 'stdClass', 'params' => array('a' => 'test', 'b' => '123', 'c' => 'def') ), 'bar' => array('params' => array('baz' => '123')) ) ) ) ); } /** * @dataProvider configProvider */ public function testCombinesConfigs($a, $b, $c) { $l = new ServiceBuilderLoader(); $m = new \ReflectionMethod($l, 'mergeData'); $m->setAccessible(true); $this->assertEquals($c, $m->invoke($l, $a, $b)); } } guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorTest.php 0000604 00000016772 15173213566 0021673 0 ustar 00 <?php namespace Guzzle\Tests\Service\Resource; use Guzzle\Service\Resource\ResourceIterator; use Guzzle\Tests\Service\Mock\Model\MockCommandIterator; /** * @group server * @covers Guzzle\Service\Resource\ResourceIterator */ class ResourceIteratorTest extends \Guzzle\Tests\GuzzleTestCase { public function testDescribesEvents() { $this->assertInternalType('array', ResourceIterator::getAllEvents()); } 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()); } 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(); foreach ($ri as $data) { $d[] = $data; } $this->assertEquals(array('d', 'e', 'f', 'g', 'h', 'i', 'j'), $d); } 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')); } 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); } 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()); } 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')); } 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')); } public function testWorksWithCustomAppendIterator() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "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')); $a = new \Guzzle\Iterator\AppendIterator(); $a->append($ri); $results = iterator_to_array($a, false); $this->assertEquals(4, $ri->calledNext); } } guzzle/tests/Guzzle/Tests/Service/Resource/MapResourceIteratorFactoryTest.php 0000604 00000002512 15173213566 0023644 0 ustar 00 <?php namespace Guzzle\Tests\Service\Resource; use Guzzle\Service\Resource\MapResourceIteratorFactory; use Guzzle\Tests\Service\Mock\Command\MockCommand; /** * @covers Guzzle\Service\Resource\MapResourceIteratorFactory */ class MapResourceIteratorFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException InvalidArgumentException * @expectedExceptionMessage Iterator was not found for mock_command */ public function testEnsuresIteratorClassExists() { $factory = new MapResourceIteratorFactory(array('Foo', 'Bar')); $factory->build(new MockCommand()); } public function testBuildsResourceIterators() { $factory = new MapResourceIteratorFactory(array( 'mock_command' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' )); $iterator = $factory->build(new MockCommand()); $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); } public function testUsesWildcardMappings() { $factory = new MapResourceIteratorFactory(array( '*' => 'Guzzle\Tests\Service\Mock\Model\MockCommandIterator' )); $iterator = $factory->build(new MockCommand()); $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); } } guzzle/tests/Guzzle/Tests/Service/Resource/ResourceIteratorClassFactoryTest.php 0000604 00000002762 15173213566 0024203 0 ustar 00 <?php namespace Guzzle\Tests\Service\Resource; use Guzzle\Service\Resource\ResourceIteratorClassFactory; use Guzzle\Tests\Service\Mock\Command\MockCommand; /** * @covers Guzzle\Service\Resource\ResourceIteratorClassFactory * @covers Guzzle\Service\Resource\AbstractResourceIteratorFactory */ class ResourceIteratorClassFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Iterator was not found for mock_command */ public function testEnsuresIteratorClassExists() { $factory = new ResourceIteratorClassFactory(array('Foo', 'Bar')); $factory->registerNamespace('Baz'); $command = new MockCommand(); $factory->build($command); } 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); } public function testChecksIfCanBuild() { $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service'); $this->assertFalse($factory->canBuild(new MockCommand())); $factory = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); $this->assertTrue($factory->canBuild(new MockCommand())); } } guzzle/tests/Guzzle/Tests/Service/Resource/CompositeResourceIteratorFactoryTest.php 0000604 00000002470 15173213566 0025074 0 ustar 00 <?php namespace Guzzle\Tests\Service\Resource; use Guzzle\Service\Resource\CompositeResourceIteratorFactory; use Guzzle\Service\Resource\ResourceIteratorClassFactory; use Guzzle\Tests\Service\Mock\Command\MockCommand; /** * @covers Guzzle\Service\Resource\CompositeResourceIteratorFactory */ class CompositeResourceIteratorFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage Iterator was not found for mock_command */ public function testEnsuresIteratorClassExists() { $factory = new CompositeResourceIteratorFactory(array( new ResourceIteratorClassFactory(array('Foo', 'Bar')) )); $cmd = new MockCommand(); $this->assertFalse($factory->canBuild($cmd)); $factory->build($cmd); } public function testBuildsResourceIterators() { $f1 = new ResourceIteratorClassFactory('Guzzle\Tests\Service\Mock\Model'); $factory = new CompositeResourceIteratorFactory(array()); $factory->addFactory($f1); $command = new MockCommand(); $iterator = $factory->build($command, array('client.namespace' => 'Guzzle\Tests\Service\Mock')); $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); } } guzzle/tests/Guzzle/Tests/Service/Resource/ModelTest.php 0000604 00000003714 15173213566 0017422 0 ustar 00 <?php namespace Guzzle\Tests\Service\Resource; use Guzzle\Service\Resource\Model; use Guzzle\Service\Description\Parameter; use Guzzle\Common\Collection; /** * @covers Guzzle\Service\Resource\Model */ class ModelTest extends \Guzzle\Tests\GuzzleTestCase { public function testOwnsStructure() { $param = new Parameter(array('type' => 'object')); $model = new Model(array('foo' => 'bar'), $param); $this->assertSame($param, $model->getStructure()); $this->assertEquals('bar', $model->get('foo')); $this->assertEquals('bar', $model['foo']); } public function testCanBeUsedWithoutStructure() { $model = new Model(array( 'Foo' => 'baz', 'Bar' => array( 'Boo' => 'Bam' ) )); $transform = function ($key, $value) { return ($value && is_array($value)) ? new Collection($value) : $value; }; $model = $model->map($transform); $this->assertInstanceOf('Guzzle\Common\Collection', $model->getPath('Bar')); } public function testAllowsFiltering() { $model = new Model(array( 'Foo' => 'baz', 'Bar' => 'a' )); $model = $model->filter(function ($i, $v) { return $v[0] == 'a'; }); $this->assertEquals(array('Bar' => 'a'), $model->toArray()); } public function testDoesNotIncludeEmptyStructureInString() { $model = new Model(array('Foo' => 'baz')); $str = (string) $model; $this->assertContains('Debug output of model', $str); $this->assertNotContains('Model structure', $str); } public function testDoesIncludeModelStructureInString() { $model = new Model(array('Foo' => 'baz'), new Parameter(array('name' => 'Foo'))); $str = (string) $model; $this->assertContains('Debug output of Foo model', $str); $this->assertContains('Model structure', $str); } } guzzle/tests/Guzzle/Tests/Service/ClientTest.php 0000604 00000025607 15173213566 0016016 0 ustar 00 <?php namespace Guzzle\Tests\Service; use Guzzle\Inflection\Inflector; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Mock\MockPlugin; use Guzzle\Service\Description\Operation; use Guzzle\Service\Client; use Guzzle\Service\Exception\CommandTransferException; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Tests\Service\Mock\Command\MockCommand; use Guzzle\Service\Resource\ResourceIteratorClassFactory; use Guzzle\Service\Command\AbstractCommand; /** * @group server * @covers Guzzle\Service\Client */ class ClientTest extends \Guzzle\Tests\GuzzleTestCase { protected $service; protected $serviceTest; public function setUp() { $this->serviceTest = new ServiceDescription(array( 'test_command' => new Operation(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__ . '/../TestData/test_service.json'); } public function testAllowsCustomClientParameters() { $client = new Mock\MockClient(null, array( Client::COMMAND_PARAMS => array(AbstractCommand::RESPONSE_PROCESSING => 'foo') )); $command = $client->getCommand('mock_command'); $this->assertEquals('foo', $command->get(AbstractCommand::RESPONSE_PROCESSING)); } 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')); } public function testFactoryDoesNotRequireBaseUrl() { $client = Client::factory(); } public function testDescribesEvents() { $this->assertInternalType('array', Client::getAllEvents()); } 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))); } 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()); } /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testThrowsExceptionWhenInvalidCommandIsExecuted() { $client = new Client(); $client->execute(new \stdClass()); } /** * @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'); } 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); $command = $client->getCommand('foo', array('acl' => '123')); $this->assertSame($mockCommand, $command); $this->assertSame($client, $command->getClient()); } 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()); } 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); } 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)); } 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')); } public function testClientCreatesIteratorsWithNoOptions() { $client = new Mock\MockClient(); $iterator = $client->getIterator('mock_command'); $this->assertInstanceOf('Guzzle\Tests\Service\Mock\Model\MockCommandIterator', $iterator); } 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); } public function testClientHoldsInflector() { $client = new Mock\MockClient(); $this->assertInstanceOf('Guzzle\Inflection\MemoizingInflector', $client->getInflector()); $inflector = new Inflector(); $client->setInflector($inflector); $this->assertSame($inflector, $client->getInflector()); } public function testClientAddsGlobalCommandOptions() { $client = new Mock\MockClient('http://www.foo.com', array( Client::COMMAND_PARAMS => array( 'mesa' => 'bar' ) )); $command = $client->getCommand('mock_command'); $this->assertEquals('bar', $command->get('mesa')); } public function testSupportsServiceDescriptionBaseUrls() { $description = new ServiceDescription(array('baseUrl' => 'http://foo.com')); $client = new Client(); $client->setDescription($description); $this->assertEquals('http://foo.com', $client->getBaseUrl()); } public function testMergesDefaultCommandParamsCorrectly() { $client = new Mock\MockClient('http://www.foo.com', array( Client::COMMAND_PARAMS => array( 'mesa' => 'bar', 'jar' => 'jar' ) )); $command = $client->getCommand('mock_command', array('jar' => 'test')); $this->assertEquals('bar', $command->get('mesa')); $this->assertEquals('test', $command->get('jar')); } /** * @expectedException \Guzzle\Http\Exception\BadResponseException */ public function testWrapsSingleCommandExceptions() { $client = new Mock\MockClient('http://foobaz.com'); $mock = new MockPlugin(array(new Response(401))); $client->addSubscriber($mock); $client->execute(new MockCommand()); } public function testWrapsMultipleCommandExceptions() { $client = new Mock\MockClient('http://foobaz.com'); $mock = new MockPlugin(array(new Response(200), new Response(200), new Response(404), new Response(500))); $client->addSubscriber($mock); $cmds = array(new MockCommand(), new MockCommand(), new MockCommand(), new MockCommand()); try { $client->execute($cmds); } catch (CommandTransferException $e) { $this->assertEquals(2, count($e->getFailedRequests())); $this->assertEquals(2, count($e->getSuccessfulRequests())); $this->assertEquals(2, count($e->getFailedCommands())); $this->assertEquals(2, count($e->getSuccessfulCommands())); foreach ($e->getSuccessfulCommands() as $c) { $this->assertTrue($c->getResponse()->isSuccessful()); } foreach ($e->getFailedCommands() as $c) { $this->assertFalse($c->getRequest()->getResponse()->isSuccessful()); } } } public function testGetCommandAfterTwoSetDescriptions() { $service1 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service.json'); $service2 = ServiceDescription::factory(__DIR__ . '/../TestData/test_service_3.json'); $client = new Mock\MockClient(); $client->setDescription($service1); $client->getCommand('foo_bar'); $client->setDescription($service2); $client->getCommand('baz_qux'); } } guzzle/tests/Guzzle/Tests/Service/Command/Factory/CompositeFactoryTest.php 0000604 00000011606 15173213566 0023051 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Service\Command\Factory\CompositeFactory; /** * @covers Guzzle\Service\Command\Factory\CompositeFactory */ class CompositeFactoryTest extends \Guzzle\Tests\GuzzleTestCase { private function getFactory($class = 'Guzzle\\Service\\Command\\Factory\\MapFactory') { return $mock = $this->getMockBuilder($class) ->disableOriginalConstructor() ->getMock(); } 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()))); } 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')); } 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')); } 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()); } 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()); } 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]); } } guzzle/tests/Guzzle/Tests/Service/Command/Factory/ServiceDescriptionFactoryTest.php 0000604 00000005010 15173213566 0024703 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Command\Factory\ServiceDescriptionFactory; use Guzzle\Inflection\Inflector; /** * @covers Guzzle\Service\Command\Factory\ServiceDescriptionFactory */ class ServiceDescriptionFactoryTest extends \Guzzle\Tests\GuzzleTestCase { public function testProvider() { return array( array('foo', null), array('jar_jar', 'Guzzle\Tests\Service\Mock\Command\MockCommand'), array('binks', 'Guzzle\Tests\Service\Mock\Command\OtherCommand') ); } /** * @dataProvider testProvider */ public function testCreatesCommandsUsingServiceDescriptions($key, $result) { $d = $this->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 testUsesUcFirstIfNoExactMatch() { $d = $this->getDescription(); $factory = new ServiceDescriptionFactory($d, new Inflector()); $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('Test')); $this->assertInstanceof('Guzzle\Tests\Service\Mock\Command\OtherCommand', $factory->factory('test')); } 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( 'operations' => array( 'jar_jar' => array('class' => 'Guzzle\Tests\Service\Mock\Command\MockCommand'), 'binks' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand'), 'Test' => array('class' => 'Guzzle\Tests\Service\Mock\Command\OtherCommand') ) )); } } guzzle/tests/Guzzle/Tests/Service/Command/Factory/AliasFactoryTest.php 0000604 00000004543 15173213566 0022142 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Service\Client; use Guzzle\Service\Command\Factory\AliasFactory; use Guzzle\Service\Command\Factory\MapFactory; use Guzzle\Service\Command\Factory\CompositeFactory; /** * @covers Guzzle\Service\Command\Factory\AliasFactory */ class AliasFactoryTest extends \Guzzle\Tests\GuzzleTestCase { private $factory; private $client; public function setup() { $this->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) ); } /** * @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'); } } } } guzzle/tests/Guzzle/Tests/Service/Command/Factory/MapFactoryTest.php 0000604 00000001745 15173213566 0021627 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Service\Command\Factory\MapFactory; /** * @covers Guzzle\Service\Command\Factory\MapFactory */ class MapFactoryTest extends \Guzzle\Tests\GuzzleTestCase { public function mapProvider() { return array( array('foo', null), array('test', 'Guzzle\Tests\Service\Mock\Command\MockCommand'), array('test1', 'Guzzle\Tests\Service\Mock\Command\OtherCommand') ); } /** * @dataProvider mapProvider */ public function testCreatesCommandsUsingMappings($key, $result) { $factory = new MapFactory(array( 'test' => '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)); } } } guzzle/tests/Guzzle/Tests/Service/Command/Factory/ConcreteClassFactoryTest.php 0000604 00000003325 15173213566 0023636 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Tests\Service\Mock\MockClient; use Guzzle\Service\Command\Factory\ConcreteClassFactory; /** * @covers Guzzle\Service\Command\Factory\ConcreteClassFactory */ class ConcreteClassFactoryTest extends \Guzzle\Tests\GuzzleTestCase { public function testProvider() { return array( array('foo', null, 'Guzzle\\Tests\\Service\\Mock\\Command\\'), array('mock_command', 'Guzzle\Tests\Service\Mock\Command\MockCommand', 'Guzzle\\Tests\\Service\\Mock\\Command\\'), array('other_command', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', 'Guzzle\\Tests\\Service\\Mock\\Command\\'), array('sub.sub', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', 'Guzzle\\Tests\\Service\\Mock\\Command\\'), array('sub.sub', null, 'Guzzle\\Foo\\'), array('foo', null, null), array('mock_command', 'Guzzle\Tests\Service\Mock\Command\MockCommand', null), array('other_command', 'Guzzle\Tests\Service\Mock\Command\OtherCommand', null), array('sub.sub', 'Guzzle\Tests\Service\Mock\Command\Sub\Sub', null) ); } /** * @dataProvider testProvider */ public function testCreatesConcreteCommands($key, $result, $prefix) { if (!$prefix) { $client = new MockClient(); } else { $client = new MockClient('', array( 'command.prefix' => $prefix )); } $factory = new ConcreteClassFactory($client); if (is_null($result)) { $this->assertNull($factory->factory($key)); } else { $this->assertInstanceof($result, $factory->factory($key)); } } } guzzle/tests/Guzzle/Tests/Service/Command/ClosureCommandTest.php 0000604 00000003063 15173213566 0021061 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Http\Message\RequestFactory; use Guzzle\Service\Command\ClosureCommand; use Guzzle\Service\Client; /** * @covers Guzzle\Service\Command\ClosureCommand */ class ClosureCommandTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException InvalidArgumentException * @expectedExceptionMessage A closure must be passed in the parameters array */ public function testConstructorValidatesClosure() { $c = new ClosureCommand(); } public function testExecutesClosure() { $c = new ClosureCommand(array( 'closure' => 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()); } /** * @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(); } } guzzle/tests/Guzzle/Tests/Service/Command/OperationCommandTest.php 0000604 00000007465 15173213566 0021417 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Http\Message\EntityEnclosingRequest; use Guzzle\Http\Message\Response; use Guzzle\Service\Client; use Guzzle\Service\Command\OperationCommand; use Guzzle\Service\Description\Operation; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Command\DefaultRequestSerializer; use Guzzle\Service\Resource\Model; use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight; /** * @covers Guzzle\Service\Command\OperationCommand */ class OperationCommandTest extends \Guzzle\Tests\GuzzleTestCase { public function testHasRequestSerializer() { $operation = new OperationCommand(); $a = $operation->getRequestSerializer(); $b = new DefaultRequestSerializer(VisitorFlyweight::getInstance()); $operation->setRequestSerializer($b); $this->assertNotSame($a, $operation->getRequestSerializer()); } public function testPreparesRequestUsingSerializer() { $op = new OperationCommand(array(), new Operation()); $op->setClient(new Client()); $s = $this->getMockBuilder('Guzzle\Service\Command\RequestSerializerInterface') ->setMethods(array('prepare')) ->getMockForAbstractClass(); $s->expects($this->once()) ->method('prepare') ->will($this->returnValue(new EntityEnclosingRequest('POST', 'http://foo.com'))); $op->setRequestSerializer($s); $op->prepare(); } public function testParsesResponsesWithResponseParser() { $op = new OperationCommand(array(), new Operation()); $p = $this->getMockBuilder('Guzzle\Service\Command\ResponseParserInterface') ->setMethods(array('parse')) ->getMockForAbstractClass(); $p->expects($this->once()) ->method('parse') ->will($this->returnValue(array('foo' => 'bar'))); $op->setResponseParser($p); $op->setClient(new Client()); $request = $op->prepare(); $request->setResponse(new Response(200), true); $this->assertEquals(array('foo' => 'bar'), $op->execute()); } public function testParsesResponsesUsingModelParserWhenMatchingModelIsFound() { $description = ServiceDescription::factory(array( 'operations' => array( 'foo' => array('responseClass' => 'bar', 'responseType' => 'model') ), 'models' => array( 'bar' => array( 'type' => 'object', 'properties' => array( 'Baz' => array('type' => 'string', 'location' => 'xml') ) ) ) )); $op = new OperationCommand(array(), $description->getOperation('foo')); $op->setClient(new Client()); $request = $op->prepare(); $request->setResponse(new Response(200, array( 'Content-Type' => 'application/xml' ), '<Foo><Baz>Bar</Baz></Foo>'), true); $result = $op->execute(); $this->assertEquals(new Model(array('Baz' => 'Bar')), $result); } public function testAllowsRawResponses() { $description = new ServiceDescription(array( 'operations' => array('foo' => array('responseClass' => 'bar', 'responseType' => 'model')), 'models' => array('bar' => array()) )); $op = new OperationCommand(array( OperationCommand::RESPONSE_PROCESSING => OperationCommand::TYPE_RAW ), $description->getOperation('foo')); $op->setClient(new Client()); $request = $op->prepare(); $response = new Response(200, array( 'Content-Type' => 'application/xml' ), '<Foo><Baz>Bar</Baz></Foo>'); $request->setResponse($response, true); $this->assertSame($response, $op->execute()); } } guzzle/tests/Guzzle/Tests/Service/Command/DefaultRequestSerializerTest.php 0000604 00000010245 15173213566 0023135 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Service\Command\DefaultRequestSerializer; use Guzzle\Http\Message\EntityEnclosingRequest; use Guzzle\Service\Client; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Description\Operation; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor; use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight; /** * @covers Guzzle\Service\Command\DefaultRequestSerializer */ class DefaultRequestSerializerTest extends \Guzzle\Tests\GuzzleTestCase { /** @var EntityEnclosingRequest */ protected $request; /** @var \Guzzle\Service\Command\AbstractCommand */ protected $command; /** @var Client */ protected $client; /** @var DefaultRequestSerializer */ protected $serializer; /** @var Operation */ protected $operation; public function setUp() { $this->serializer = DefaultRequestSerializer::getInstance(); $this->client = new Client('http://foo.com/baz'); $this->operation = new Operation(array('httpMethod' => 'POST')); $this->command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') ->setConstructorArgs(array(array(), $this->operation)) ->getMockForAbstractClass(); $this->command->setClient($this->client); } public function testAllowsCustomVisitor() { $this->serializer->addVisitor('custom', new HeaderVisitor()); $this->command['test'] = '123'; $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'custom'))); $request = $this->serializer->prepare($this->command); $this->assertEquals('123', (string) $request->getHeader('test')); } public function testUsesRelativePath() { $this->operation->setUri('bar'); $request = $this->serializer->prepare($this->command); $this->assertEquals('http://foo.com/baz/bar', (string) $request->getUrl()); } public function testUsesRelativePathWithUriLocations() { $this->command['test'] = '123'; $this->operation->setUri('bar/{test}'); $this->operation->addParam(new Parameter(array('name' => 'test', 'location' => 'uri'))); $request = $this->serializer->prepare($this->command); $this->assertEquals('http://foo.com/baz/bar/123', (string) $request->getUrl()); } public function testAllowsCustomFactory() { $f = new VisitorFlyweight(); $serializer = new DefaultRequestSerializer($f); $this->assertSame($f, $this->readAttribute($serializer, 'factory')); } public function testMixedParams() { $this->operation->setUri('bar{?limit,fields}'); $this->operation->addParam(new Parameter(array( 'name' => 'limit', 'location' => 'uri', 'required' => false, ))); $this->operation->addParam(new Parameter(array( 'name' => 'fields', 'location' => 'uri', 'required' => true, ))); $this->command['fields'] = array('id', 'name'); $request = $this->serializer->prepare($this->command); $this->assertEquals('http://foo.com/baz/bar?fields='.urlencode('id,name'), (string) $request->getUrl()); } public function testValidatesAdditionalParameters() { $description = ServiceDescription::factory(array( 'operations' => array( 'foo' => array( 'httpMethod' => 'PUT', 'parameters' => array( 'bar' => array('location' => 'header') ), 'additionalParameters' => array( 'location' => 'json' ) ) ) )); $client = new Client(); $client->setDescription($description); $command = $client->getCommand('foo'); $command['bar'] = 'test'; $command['hello'] = 'abc'; $request = $command->prepare(); $this->assertEquals('test', (string) $request->getHeader('bar')); $this->assertEquals('{"hello":"abc"}', (string) $request->getBody()); } } guzzle/tests/Guzzle/Tests/Service/Command/AbstractCommandTest.php 0000604 00000000647 15173213566 0021215 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Service\Client; use Guzzle\Service\Description\ServiceDescription; abstract class AbstractCommandTest extends \Guzzle\Tests\GuzzleTestCase { protected function getClient() { $client = new Client('http://www.google.com/'); return $client->setDescription(ServiceDescription::factory(__DIR__ . '/../../TestData/test_service.json')); } } guzzle/tests/Guzzle/Tests/Service/Command/OperationResponseParserTest.php 0000604 00000033546 15173213566 0023013 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Http\Message\Response; use Guzzle\Service\Client; use Guzzle\Service\Command\OperationResponseParser; use Guzzle\Service\Command\OperationCommand; use Guzzle\Service\Description\Operation; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor; use Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor; use Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor; use Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor; use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight; /** * @covers Guzzle\Service\Command\OperationResponseParser * @covers Guzzle\Service\Command\CreateResponseClassEvent */ class OperationResponseParserTest extends \Guzzle\Tests\GuzzleTestCase { public function testHasVisitors() { $p = new OperationResponseParser(new VisitorFlyweight(array())); $visitor = new BodyVisitor(); $p->addVisitor('foo', $visitor); $this->assertSame($visitor, $this->readAttribute($p, 'factory')->getResponseVisitor('foo')); } public function testUsesParentParser() { $p = new OperationResponseParser(new VisitorFlyweight()); $operation = new Operation(); $operation->setServiceDescription(new ServiceDescription()); $op = new OperationCommand(array(), $operation); $op->setResponseParser($p)->setClient(new Client()); $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/xml'), '<F><B>C</B></F>'), true); $this->assertInstanceOf('SimpleXMLElement', $op->execute()); } public function testVisitsLocations() { $parser = new OperationResponseParser(new VisitorFlyweight(array())); $parser->addVisitor('statusCode', new StatusCodeVisitor()); $parser->addVisitor('reasonPhrase', new ReasonPhraseVisitor()); $parser->addVisitor('json', new JsonVisitor()); $op = new OperationCommand(array(), $this->getDescription()->getOperation('test')); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(201), true); $result = $op->execute(); $this->assertEquals(201, $result['code']); $this->assertEquals('Created', $result['phrase']); } public function testVisitsLocationsForJsonResponse() { $parser = OperationResponseParser::getInstance(); $operation = $this->getDescription()->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(200, array( 'Content-Type' => 'application/json' ), '{"baz":"bar","enigma":"123"}'), true); $result = $op->execute(); $this->assertEquals(array( 'baz' => 'bar', 'enigma' => '123', 'code' => 200, 'phrase' => 'OK' ), $result->toArray()); } public function testSkipsUnkownModels() { $parser = OperationResponseParser::getInstance(); $operation = $this->getDescription()->getOperation('test'); $operation->setResponseClass('Baz')->setResponseType('model'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(201), true); $this->assertInstanceOf('Guzzle\Http\Message\Response', $op->execute()); } public function testAllowsModelProcessingToBeDisabled() { $parser = OperationResponseParser::getInstance(); $operation = $this->getDescription()->getOperation('test'); $op = new OperationCommand(array('command.response_processing' => 'native'), $operation); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(200, array( 'Content-Type' => 'application/json' ), '{"baz":"bar","enigma":"123"}'), true); $result = $op->execute(); $this->assertInstanceOf('Guzzle\Service\Resource\Model', $result); $this->assertEquals(array( 'baz' => 'bar', 'enigma' => '123' ), $result->toArray()); } public function testCanInjectModelSchemaIntoModels() { $parser = new OperationResponseParser(VisitorFlyweight::getInstance(), true); $desc = $this->getDescription(); $operation = $desc->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(200, array( 'Content-Type' => 'application/json' ), '{"baz":"bar","enigma":"123"}'), true); $result = $op->execute(); $this->assertSame($result->getStructure(), $desc->getModel('Foo')); } public function testDoesNotParseXmlWhenNotUsingXmlVisitor() { $parser = OperationResponseParser::getInstance(); $description = ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'Foo')), 'models' => array( 'Foo' => array( 'type' => 'object', 'properties' => array('baz' => array('location' => 'body')) ) ) )); $operation = $description->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $brokenXml = '<broken><><><<xml>>>>>'; $op->prepare()->setResponse(new Response(200, array( 'Content-Type' => 'application/xml' ), $brokenXml), true); $result = $op->execute(); $this->assertEquals(array('baz'), $result->getKeys()); $this->assertEquals($brokenXml, (string) $result['baz']); } public function testVisitsAdditionalProperties() { $parser = OperationResponseParser::getInstance(); $description = ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'Foo')), 'models' => array( 'Foo' => array( 'type' => 'object', 'properties' => array( 'code' => array('location' => 'statusCode') ), 'additionalProperties' => array( 'location' => 'json', 'type' => 'object', 'properties' => array( 'a' => array( 'type' => 'string', 'filters' => 'strtoupper' ) ) ) ) ) )); $operation = $description->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $json = '[{"a":"test"},{"a":"baz"}]'; $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); $result = $op->execute()->toArray(); $this->assertEquals(array( 'code' => 200, array('a' => 'TEST'), array('a' => 'BAZ') ), $result); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/399 */ public function testAdditionalPropertiesDisabledDiscardsData() { $parser = OperationResponseParser::getInstance(); $description = ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'Foo')), 'models' => array( 'Foo' => array( 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'name' => array( 'location' => 'json', 'type' => 'string', ), 'nested' => array( 'location' => 'json', 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'width' => array( 'type' => 'integer' ) ), ), 'code' => array('location' => 'statusCode') ), ) ) )); $operation = $description->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $json = '{"name":"test", "volume":2.0, "nested":{"width":10,"bogus":1}}'; $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); $result = $op->execute()->toArray(); $this->assertEquals(array( 'name' => 'test', 'nested' => array( 'width' => 10, ), 'code' => 200 ), $result); } public function testCreatesCustomResponseClassInterface() { $parser = OperationResponseParser::getInstance(); $description = ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'Guzzle\Tests\Mock\CustomResponseModel')) )); $operation = $description->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); $result = $op->execute(); $this->assertInstanceOf('Guzzle\Tests\Mock\CustomResponseModel', $result); $this->assertSame($op, $result->command); } /** * @expectedException \Guzzle\Service\Exception\ResponseClassException * @expectedExceptionMessage must exist */ public function testEnsuresResponseClassExists() { $parser = OperationResponseParser::getInstance(); $description = ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'Foo\Baz\Bar')) )); $operation = $description->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); $op->execute(); } /** * @expectedException \Guzzle\Service\Exception\ResponseClassException * @expectedExceptionMessage and implement */ public function testEnsuresResponseClassImplementsResponseClassInterface() { $parser = OperationResponseParser::getInstance(); $description = ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => __CLASS__)) )); $operation = $description->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), 'hi!'), true); $op->execute(); } protected function getDescription() { return ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'Foo')), 'models' => array( 'Foo' => array( 'type' => 'object', 'properties' => array( 'baz' => array('type' => 'string', 'location' => 'json'), 'code' => array('location' => 'statusCode'), 'phrase' => array('location' => 'reasonPhrase'), ) ) ) )); } public function testCanAddListenerToParseDomainObjects() { $client = new Client(); $client->setDescription(ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'FooBazBar')) ))); $foo = new \stdClass(); $client->getEventDispatcher()->addListener('command.parse_response', function ($e) use ($foo) { $e['result'] = $foo; }); $command = $client->getCommand('test'); $command->prepare()->setResponse(new Response(200), true); $result = $command->execute(); $this->assertSame($result, $foo); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/501 */ public function testAdditionalPropertiesWithRefAreResolved() { $parser = OperationResponseParser::getInstance(); $description = ServiceDescription::factory(array( 'operations' => array('test' => array('responseClass' => 'Foo')), 'models' => array( 'Baz' => array('type' => 'string'), 'Foo' => array( 'type' => 'object', 'additionalProperties' => array('$ref' => 'Baz', 'location' => 'json') ) ) )); $operation = $description->getOperation('test'); $op = new OperationCommand(array(), $operation); $op->setResponseParser($parser)->setClient(new Client()); $json = '{"a":"a","b":"b","c":"c"}'; $op->prepare()->setResponse(new Response(200, array('Content-Type' => 'application/json'), $json), true); $result = $op->execute()->toArray(); $this->assertEquals(array('a' => 'a', 'b' => 'b', 'c' => 'c'), $result); } } guzzle/tests/Guzzle/Tests/Service/Command/DefaultResponseParserTest.php 0000604 00000003777 15173213566 0022442 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Http\Message\Response; use Guzzle\Service\Client; use Guzzle\Service\Command\DefaultResponseParser; use Guzzle\Service\Command\OperationCommand; use Guzzle\Service\Description\Operation; /** * @covers Guzzle\Service\Command\DefaultResponseParser */ class DefaultResponseParserTest extends \Guzzle\Tests\GuzzleTestCase { public function testParsesXmlResponses() { $op = new OperationCommand(array(), new Operation()); $op->setClient(new Client()); $request = $op->prepare(); $request->setResponse(new Response(200, array( 'Content-Type' => 'application/xml' ), '<Foo><Baz>Bar</Baz></Foo>'), true); $this->assertInstanceOf('SimpleXMLElement', $op->execute()); } public function testParsesJsonResponses() { $op = new OperationCommand(array(), new Operation()); $op->setClient(new Client()); $request = $op->prepare(); $request->setResponse(new Response(200, array( 'Content-Type' => 'application/json' ), '{"Baz":"Bar"}'), true); $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException */ public function testThrowsExceptionWhenParsingJsonFails() { $op = new OperationCommand(array(), new Operation()); $op->setClient(new Client()); $request = $op->prepare(); $request->setResponse(new Response(200, array('Content-Type' => 'application/json'), '{"Baz":ddw}'), true); $op->execute(); } public function testAddsContentTypeWhenExpectsIsSetOnCommand() { $op = new OperationCommand(array(), new Operation()); $op['command.expects'] = 'application/json'; $op->setClient(new Client()); $request = $op->prepare(); $request->setResponse(new Response(200, null, '{"Baz":"Bar"}'), true); $this->assertEquals(array('Baz' => 'Bar'), $op->execute()); } } guzzle/tests/Guzzle/Tests/Service/Command/CommandTest.php 0000604 00000036035 15173213566 0017531 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Plugin\Mock\MockPlugin; use Guzzle\Http\EntityBody; use Guzzle\Http\Message\Response; use Guzzle\Service\Client; use Guzzle\Service\Command\AbstractCommand; use Guzzle\Service\Description\Operation; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Description\SchemaValidator; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Tests\Service\Mock\Command\MockCommand; use Guzzle\Tests\Service\Mock\Command\Sub\Sub; /** * @covers Guzzle\Service\Command\AbstractCommand */ class CommandTest extends AbstractCommandTest { public function testConstructorAddsDefaultParams() { $command = new MockCommand(); $this->assertEquals('123', $command->get('test')); $this->assertFalse($command->isPrepared()); $this->assertFalse($command->isExecuted()); } public function testDeterminesShortName() { $api = new Operation(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()); } /** * @expectedException RuntimeException */ public function testGetRequestThrowsExceptionBeforePreparation() { $command = new MockCommand(); $command->getRequest(); } public function testGetResponseExecutesCommandsWhenNeeded() { $response = new Response(200); $client = $this->getClient(); $this->setMockResponse($client, array($response)); $command = new MockCommand(); $command->setClient($client); $this->assertSame($response, $command->getResponse()); $this->assertSame($response, $command->getResponse()); } public function testGetResultExecutesCommandsWhenNeeded() { $response = new Response(200); $client = $this->getClient(); $this->setMockResponse($client, array($response)); $command = new MockCommand(); $command->setClient($client); $this->assertSame($response, $command->getResult()); $this->assertSame($response, $command->getResult()); } 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()); } public function testExecute() { $client = $this->getClient(); $response = new Response(200, array( 'Content-Type' => 'application/xml' ), '<xml><data>123</data></xml>'); $this->setMockResponse($client, 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); } public function testConvertsJsonResponsesToArray() { $client = $this->getClient(); $this->setMockResponse($client, 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()); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException */ public function testConvertsInvalidJsonResponsesToArray() { $json = '{ "key": "Hi!" }invalid'; // Some implementations of php-json extension are not strict enough // and allow to parse invalid json ignoring invalid parts // See https://github.com/remicollet/pecl-json-c/issues/5 if (json_decode($json) && JSON_ERROR_NONE === json_last_error()) { $this->markTestSkipped('php-pecl-json library regression issues'); } $client = $this->getClient(); $this->setMockResponse($client, array( new \Guzzle\Http\Message\Response(200, array( 'Content-Type' => 'application/json' ), $json ) )); $command = new MockCommand(); $command->setClient($client); $command->execute(); } public function testProcessResponseIsNotXml() { $client = $this->getClient(); $this->setMockResponse($client, 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); } /** * @expectedException RuntimeException */ public function testExecuteThrowsExceptionWhenNoClientIsSet() { $command = new MockCommand(); $command->execute(); } /** * @expectedException RuntimeException */ public function testPrepareThrowsExceptionWhenNoClientIsSet() { $command = new MockCommand(); $command->prepare(); } 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')); } public function testCommandsAllowsCustomRequestHeadersAsArray() { $command = new MockCommand(array(AbstractCommand::HEADERS_OPTION => array('Foo' => 'Bar'))); $this->assertInstanceOf('Guzzle\Common\Collection', $command->getRequestHeaders()); $this->assertEquals('Bar', $command->getRequestHeaders()->get('Foo')); } private function getOperation() { return new Operation(array( 'name' => 'foobar', 'httpMethod' => 'POST', 'class' => 'Guzzle\\Tests\\Service\\Mock\\Command\\MockCommand', 'parameters' => array( 'test' => array( 'default' => '123', 'type' => 'string' ) ))); } public function testCommandsUsesOperation() { $api = $this->getOperation(); $command = new MockCommand(array(), $api); $this->assertSame($api, $command->getOperation()); $command->setClient($this->getClient())->prepare(); $this->assertEquals('123', $command->get('test')); $this->assertSame($api, $command->getOperation($api)); } public function testCloneMakesNewRequest() { $client = $this->getClient(); $command = new MockCommand(array(), $this->getOperation()); $command->setClient($client); $command->prepare(); $this->assertTrue($command->isPrepared()); $command2 = clone $command; $this->assertFalse($command2->isPrepared()); } 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->getOperation()); $command->setClient($client); $command->prepare()->setResponse(new Response(200), true); $command->execute(); $this->assertEquals(1, $called); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testOnCompleteMustBeCallable() { $client = $this->getClient(); $command = new MockCommand(); $command->setOnComplete('foo'); } 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()); } public function testCanInitConfig() { $command = $this->getMockBuilder('Guzzle\\Service\\Command\\AbstractCommand') ->setConstructorArgs(array(array( 'foo' => 'bar' ), new Operation(array( 'parameters' => array( 'baz' => new Parameter(array( 'default' => 'baaar' )) ) )))) ->getMockForAbstractClass(); $this->assertEquals('bar', $command['foo']); $this->assertEquals('baaar', $command['baz']); } public function testAddsCurlOptionsToRequestsWhenPreparing() { $command = new MockCommand(array( 'foo' => 'bar', 'curl.options' => array('CURLOPT_PROXYPORT' => 8080) )); $client = new Client(); $command->setClient($client); $request = $command->prepare(); $this->assertEquals(8080, $request->getCurlOptions()->get(CURLOPT_PROXYPORT)); } public function testIsInvokable() { $client = $this->getClient(); $response = new Response(200); $this->setMockResponse($client, array($response)); $command = new MockCommand(); $command->setClient($client); // Returns the result of the command $this->assertSame($response, $command()); } public function testCreatesDefaultOperation() { $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); $this->assertInstanceOf('Guzzle\Service\Description\Operation', $command->getOperation()); } public function testAllowsValidatorToBeInjected() { $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand')->getMockForAbstractClass(); $v = new SchemaValidator(); $command->setValidator($v); $this->assertSame($v, $this->readAttribute($command, 'validator')); } public function testCanDisableValidation() { $command = new MockCommand(); $command->setClient(new \Guzzle\Service\Client()); $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') ->setMethods(array('validate')) ->getMock(); $v->expects($this->never())->method('validate'); $command->setValidator($v); $command->set(AbstractCommand::DISABLE_VALIDATION, true); $command->prepare(); } public function testValidatorDoesNotUpdateNonDefaultValues() { $command = new MockCommand(array('test' => 123, 'foo' => 'bar')); $command->setClient(new \Guzzle\Service\Client()); $command->prepare(); $this->assertEquals(123, $command->get('test')); $this->assertEquals('bar', $command->get('foo')); } public function testValidatorUpdatesDefaultValues() { $command = new MockCommand(); $command->setClient(new \Guzzle\Service\Client()); $command->prepare(); $this->assertEquals(123, $command->get('test')); $this->assertEquals('abc', $command->get('_internal')); } /** * @expectedException \Guzzle\Service\Exception\ValidationException * @expectedExceptionMessage [Foo] Baz */ public function testValidatesCommandBeforeSending() { $command = new MockCommand(); $command->setClient(new \Guzzle\Service\Client()); $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') ->setMethods(array('validate', 'getErrors')) ->getMock(); $v->expects($this->any())->method('validate')->will($this->returnValue(false)); $v->expects($this->any())->method('getErrors')->will($this->returnValue(array('[Foo] Baz', '[Bar] Boo'))); $command->setValidator($v); $command->prepare(); } /** * @expectedException \Guzzle\Service\Exception\ValidationException * @expectedExceptionMessage Validation errors: [abc] must be of type string */ public function testValidatesAdditionalParameters() { $description = ServiceDescription::factory(array( 'operations' => array( 'foo' => array( 'parameters' => array( 'baz' => array('type' => 'integer') ), 'additionalParameters' => array( 'type' => 'string' ) ) ) )); $client = new Client(); $client->setDescription($description); $command = $client->getCommand('foo', array( 'abc' => false, 'command.headers' => array('foo' => 'bar') )); $command->prepare(); } public function testCanAccessValidationErrorsFromCommand() { $validationErrors = array('[Foo] Baz', '[Bar] Boo'); $command = new MockCommand(); $command->setClient(new \Guzzle\Service\Client()); $this->assertFalse($command->getValidationErrors()); $v = $this->getMockBuilder('Guzzle\Service\Description\SchemaValidator') ->setMethods(array('validate', 'getErrors')) ->getMock(); $v->expects($this->any())->method('getErrors')->will($this->returnValue($validationErrors)); $command->setValidator($v); $this->assertEquals($validationErrors, $command->getValidationErrors()); } public function testCanChangeResponseBody() { $body = EntityBody::factory(); $command = new MockCommand(); $command->setClient(new \Guzzle\Service\Client()); $command->set(AbstractCommand::RESPONSE_BODY, $body); $request = $command->prepare(); $this->assertSame($body, $this->readAttribute($request, 'responseBody')); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/BodyVisitorTest.php 0000604 00000001246 15173213566 0025352 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Response; use Guzzle\Service\Description\Parameter; use Guzzle\Http\Message\Response; use Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Response\BodyVisitor */ class BodyVisitorTest extends AbstractResponseVisitorTest { public function testVisitsLocation() { $visitor = new Visitor(); $param = new Parameter(array('location' => 'body', 'name' => 'foo')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals('Foo', (string) $this->value['foo']); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/XmlVisitorTest.php 0000604 00000036264 15173213566 0025225 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Response; use Guzzle\Service\Description\Parameter; use Guzzle\Http\Message\Response; use Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Response\XmlVisitor */ class XmlVisitorTest extends AbstractResponseVisitorTest { public function testBeforeMethodParsesXml() { $visitor = new Visitor(); $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') ->setMethods(array('getResponse')) ->getMockForAbstractClass(); $command->expects($this->once()) ->method('getResponse') ->will($this->returnValue(new Response(200, null, '<foo><Bar>test</Bar></foo>'))); $result = array(); $visitor->before($command, $result); $this->assertEquals(array('Bar' => 'test'), $result); } public function testBeforeMethodParsesXmlWithNamespace() { $this->markTestSkipped("Response/XmlVisitor cannot accept 'xmlns' in response, see #368 (http://git.io/USa1mA)."); $visitor = new Visitor(); $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') ->setMethods(array('getResponse')) ->getMockForAbstractClass(); $command->expects($this->once()) ->method('getResponse') ->will($this->returnValue(new Response(200, null, '<foo xmlns="urn:foo"><bar:Bar xmlns:bar="urn:bar">test</bar:Bar></foo>'))); $result = array(); $visitor->before($command, $result); $this->assertEquals(array('Bar' => 'test'), $result); } public function testBeforeMethodParsesNestedXml() { $visitor = new Visitor(); $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') ->setMethods(array('getResponse')) ->getMockForAbstractClass(); $command->expects($this->once()) ->method('getResponse') ->will($this->returnValue(new Response(200, null, '<foo><Items><Bar>test</Bar></Items></foo>'))); $result = array(); $visitor->before($command, $result); $this->assertEquals(array('Items' => array('Bar' => 'test')), $result); } public function testCanExtractAndRenameTopLevelXmlValues() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'xml', 'name' => 'foo', 'sentAs' => 'Bar' )); $value = array('Bar' => 'test'); $visitor->visit($this->command, $this->response, $param, $value); $this->assertArrayHasKey('foo', $value); $this->assertEquals('test', $value['foo']); } public function testEnsuresRepeatedArraysAreInCorrectLocations() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'xml', 'name' => 'foo', 'sentAs' => 'Foo', 'type' => 'array', 'items' => array( 'type' => 'object', 'properties' => array( 'Bar' => array('type' => 'string'), 'Baz' => array('type' => 'string'), 'Bam' => array('type' => 'string') ) ) )); $xml = new \SimpleXMLElement('<Test><Foo><Bar>1</Bar><Baz>2</Baz></Foo></Test>'); $value = json_decode(json_encode($xml), true); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals(array( 'foo' => array( array ( 'Bar' => '1', 'Baz' => '2' ) ) ), $value); } public function testEnsuresFlatArraysAreFlat() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'xml', 'name' => 'foo', 'type' => 'array', 'items' => array('type' => 'string') )); $value = array('foo' => array('bar', 'baz')); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals(array('foo' => array('bar', 'baz')), $value); $value = array('foo' => 'bar'); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals(array('foo' => array('bar')), $value); } public function xmlDataProvider() { $param = new Parameter(array( 'location' => 'xml', 'name' => 'Items', 'type' => 'array', 'items' => array( 'type' => 'object', 'name' => 'Item', 'properties' => array( 'Bar' => array('type' => 'string'), 'Baz' => array('type' => 'string') ) ) )); return array( array($param, '<Test><Items><Item><Bar>1</Bar></Item><Item><Bar>2</Bar></Item></Items></Test>', array( 'Items' => array( array('Bar' => 1), array('Bar' => 2) ) )), array($param, '<Test><Items><Item><Bar>1</Bar></Item></Items></Test>', array( 'Items' => array( array('Bar' => 1) ) )), array($param, '<Test><Items /></Test>', array( 'Items' => array() )) ); } /** * @dataProvider xmlDataProvider */ public function testEnsuresWrappedArraysAreInCorrectLocations($param, $xml, $result) { $visitor = new Visitor(); $xml = new \SimpleXMLElement($xml); $value = json_decode(json_encode($xml), true); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals($result, $value); } public function testCanRenameValues() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'TerminatingInstances', 'type' => 'array', 'location' => 'xml', 'sentAs' => 'instancesSet', 'items' => array( 'name' => 'item', 'type' => 'object', 'sentAs' => 'item', 'properties' => array( 'InstanceId' => array( 'type' => 'string', 'sentAs' => 'instanceId', ), 'CurrentState' => array( 'type' => 'object', 'sentAs' => 'currentState', 'properties' => array( 'Code' => array( 'type' => 'numeric', 'sentAs' => 'code', ), 'Name' => array( 'type' => 'string', 'sentAs' => 'name', ), ), ), 'PreviousState' => array( 'type' => 'object', 'sentAs' => 'previousState', 'properties' => array( 'Code' => array( 'type' => 'numeric', 'sentAs' => 'code', ), 'Name' => array( 'type' => 'string', 'sentAs' => 'name', ), ), ), ), ) )); $value = array( 'instancesSet' => array ( 'item' => array ( 'instanceId' => 'i-3ea74257', 'currentState' => array( 'code' => '32', 'name' => 'shutting-down', ), 'previousState' => array( 'code' => '16', 'name' => 'running', ), ), ) ); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals(array( 'TerminatingInstances' => array( array( 'InstanceId' => 'i-3ea74257', 'CurrentState' => array( 'Code' => '32', 'Name' => 'shutting-down', ), 'PreviousState' => array( 'Code' => '16', 'Name' => 'running', ) ) ) ), $value); } public function testCanRenameAttributes() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'RunningQueues', 'type' => 'array', 'location' => 'xml', 'items' => array( 'type' => 'object', 'sentAs' => 'item', 'properties' => array( 'QueueId' => array( 'type' => 'string', 'sentAs' => 'queue_id', 'data' => array( 'xmlAttribute' => true, ), ), 'CurrentState' => array( 'type' => 'object', 'properties' => array( 'Code' => array( 'type' => 'numeric', 'sentAs' => 'code', 'data' => array( 'xmlAttribute' => true, ), ), 'Name' => array( 'sentAs' => 'name', 'data' => array( 'xmlAttribute' => true, ), ), ), ), 'PreviousState' => array( 'type' => 'object', 'properties' => array( 'Code' => array( 'type' => 'numeric', 'sentAs' => 'code', 'data' => array( 'xmlAttribute' => true, ), ), 'Name' => array( 'sentAs' => 'name', 'data' => array( 'xmlAttribute' => true, ), ), ), ), ), ) )); $xml = '<wrap><RunningQueues><item queue_id="q-3ea74257"><CurrentState code="32" name="processing" /><PreviousState code="16" name="wait" /></item></RunningQueues></wrap>'; $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals(array( 'RunningQueues' => array( array( 'QueueId' => 'q-3ea74257', 'CurrentState' => array( 'Code' => '32', 'Name' => 'processing', ), 'PreviousState' => array( 'Code' => '16', 'Name' => 'wait', ), ), ) ), $value); } public function testAddsEmptyArraysWhenValueIsMissing() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'Foo', 'type' => 'array', 'location' => 'xml', 'items' => array( 'type' => 'object', 'properties' => array( 'Baz' => array('type' => 'array'), 'Bar' => array( 'type' => 'object', 'properties' => array( 'Baz' => array('type' => 'array'), ) ) ) ) )); $value = array(); $visitor->visit($this->command, $this->response, $param, $value); $value = array( 'Foo' => array( 'Bar' => array() ) ); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals(array( 'Foo' => array( array( 'Bar' => array() ) ) ), $value); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/399 */ public function testDiscardingUnknownProperties() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'bar' => array( 'type' => 'string', 'name' => 'bar', ), ), )); $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/399 */ public function testDiscardingUnknownPropertiesWithAliasing() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'bar' => array( 'name' => 'bar', 'sentAs' => 'baz', ), ), )); $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); } public function testProperlyHandlesEmptyStringValues() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'properties' => array( 'bar' => array('type' => 'string') ), )); $xml = '<wrapper><foo><bar /></foo></wrapper>'; $value = json_decode(json_encode(new \SimpleXMLElement($xml)), true); $visitor->visit($this->command, $this->response, $param, $value); $this->assertEquals(array('foo' => array('bar' => '')), $value); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/JsonVisitorTest.php 0000604 00000013003 15173213566 0025360 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Response; use Guzzle\Service\Description\Parameter; use Guzzle\Http\Message\Response; use Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor */ class JsonVisitorTest extends AbstractResponseVisitorTest { public function testBeforeMethodParsesXml() { $visitor = new Visitor(); $command = $this->getMockBuilder('Guzzle\Service\Command\AbstractCommand') ->setMethods(array('getResponse')) ->getMockForAbstractClass(); $command->expects($this->once()) ->method('getResponse') ->will($this->returnValue(new Response(200, null, '{"foo":"bar"}'))); $result = array(); $visitor->before($command, $result); $this->assertEquals(array('foo' => 'bar'), $result); } public function testVisitsLocation() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'array', 'items' => array( 'filters' => 'strtoupper', 'type' => 'string' ) )); $this->value = array('foo' => array('a', 'b', 'c')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('A', 'B', 'C'), $this->value['foo']); } public function testRenamesTopLevelValues() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'sentAs' => 'Baz', 'type' => 'string', )); $this->value = array('Baz' => 'test'); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('foo' => 'test'), $this->value); } public function testRenamesDoesNotFailForNonExistentKey() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'properties' => array( 'bar' => array( 'name' => 'bar', 'sentAs' => 'baz', ), ), )); $this->value = array('foo' => array('unknown' => 'Unknown')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('foo' => array('unknown' => 'Unknown')), $this->value); } public function testTraversesObjectsAndAppliesFilters() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'properties' => array( 'foo' => array('filters' => 'strtoupper'), 'bar' => array('filters' => 'strtolower') ) )); $this->value = array('foo' => array('foo' => 'hello', 'bar' => 'THERE')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('foo' => 'HELLO', 'bar' => 'there'), $this->value['foo']); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/399 */ public function testDiscardingUnknownProperties() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'bar' => array( 'type' => 'string', 'name' => 'bar', ), ), )); $this->value = array('foo' => array('bar' => 15, 'unknown' => 'Unknown')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/399 */ public function testDiscardingUnknownPropertiesWithAliasing() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'additionalProperties' => false, 'properties' => array( 'bar' => array( 'name' => 'bar', 'sentAs' => 'baz', ), ), )); $this->value = array('foo' => array('baz' => 15, 'unknown' => 'Unknown')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(array('foo' => array('bar' => 15)), $this->value); } public function testWalksAdditionalProperties() { $visitor = new Visitor(); $param = new Parameter(array( 'name' => 'foo', 'type' => 'object', 'additionalProperties' => array( 'type' => 'object', 'properties' => array( 'bar' => array( 'type' => 'string', 'filters' => array('base64_decode') ) ), ), )); $this->value = array('foo' => array('baz' => array('bar' => 'Zm9v'))); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals('foo', $this->value['foo']['baz']['bar']); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/StatusCodeVisitorTest.php 0000604 00000001265 15173213566 0026534 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Response; use Guzzle\Service\Description\Parameter; use Guzzle\Http\Message\Response; use Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Response\StatusCodeVisitor */ class StatusCodeVisitorTest extends AbstractResponseVisitorTest { public function testVisitsLocation() { $visitor = new Visitor(); $param = new Parameter(array('location' => 'statusCode', 'name' => 'code')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals(200, $this->value['code']); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/HeaderVisitorTest.php 0000604 00000006453 15173213566 0025652 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Response; use Guzzle\Service\Description\Parameter; use Guzzle\Http\Message\Response; use Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Response\HeaderVisitor */ class HeaderVisitorTest extends AbstractResponseVisitorTest { public function testVisitsLocation() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'header', 'name' => 'ContentType', 'sentAs' => 'Content-Type' )); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals('text/plain', $this->value['ContentType']); } public function testVisitsLocationWithFilters() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'header', 'name' => 'Content-Type', 'filters' => array('strtoupper') )); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals('TEXT/PLAIN', $this->value['Content-Type']); } public function testVisitsMappedPrefixHeaders() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'header', 'name' => 'Metadata', 'sentAs' => 'X-Baz-', 'type' => 'object', 'additionalProperties' => array( 'type' => 'string' ) )); $response = new Response(200, array( 'X-Baz-Test' => 'ABC', 'X-Baz-Bar' => array('123', '456'), 'Content-Length' => 3 ), 'Foo'); $visitor->visit($this->command, $response, $param, $this->value); $this->assertEquals(array( 'Metadata' => array( 'Test' => 'ABC', 'Bar' => array('123', '456') ) ), $this->value); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/399 */ public function testDiscardingUnknownHeaders() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'header', 'name' => 'Content-Type', 'additionalParameters' => false )); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals('text/plain', $this->value['Content-Type']); $this->assertArrayNotHasKey('X-Foo', $this->value); } /** * @group issue-399 * @link https://github.com/guzzle/guzzle/issues/399 */ public function testDiscardingUnknownPropertiesWithAliasing() { $visitor = new Visitor(); $param = new Parameter(array( 'location' => 'header', 'name' => 'ContentType', 'sentAs' => 'Content-Type', 'additionalParameters' => false )); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals('text/plain', $this->value['ContentType']); $this->assertArrayNotHasKey('X-Foo', $this->value); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/AbstractResponseVisitorTest.php 0000604 00000001271 15173213566 0027735 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Response; use Guzzle\Tests\Service\Mock\Command\MockCommand; use Guzzle\Http\Message\Response; abstract class AbstractResponseVisitorTest extends \Guzzle\Tests\GuzzleTestCase { /** @var Response */ protected $response; /** @var MockCommand */ protected $command; /** @var array */ protected $value; public function setUp() { $this->value = array(); $this->command = new MockCommand(); $this->response = new Response(200, array( 'X-Foo' => 'bar', 'Content-Length' => 3, 'Content-Type' => 'text/plain' ), 'Foo'); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Response/ReasonPhraseVisitorTest.php 0000604 00000001302 15173213566 0027040 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Response; use Guzzle\Service\Description\Parameter; use Guzzle\Http\Message\Response; use Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Response\ReasonPhraseVisitor */ class ReasonPhraseVisitorTest extends AbstractResponseVisitorTest { public function testVisitsLocation() { $visitor = new Visitor(); $param = new Parameter(array('location' => 'reasonPhrase', 'name' => 'phrase')); $visitor->visit($this->command, $this->response, $param, $this->value); $this->assertEquals('OK', $this->value['phrase']); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/VisitorFlyweightTest.php 0000604 00000003513 15173213566 0024620 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command; use Guzzle\Service\Command\LocationVisitor\VisitorFlyweight; use Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor as JsonRequestVisitor; use Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor as JsonResponseVisitor; /** * @covers Guzzle\Service\Command\LocationVisitor\VisitorFlyweight */ class VisitorFlyweightTest extends \Guzzle\Tests\GuzzleTestCase { public function testUsesDefaultMappingsWithGetInstance() { $f = VisitorFlyweight::getInstance(); $this->assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor', $f->getRequestVisitor('json')); $this->assertInstanceOf('Guzzle\Service\Command\LocationVisitor\Response\JsonVisitor', $f->getResponseVisitor('json')); } public function testCanUseCustomMappings() { $f = new VisitorFlyweight(array()); $this->assertEquals(array(), $this->readAttribute($f, 'mappings')); } /** * @expectedException \InvalidArgumentException * @expectedExceptionMessage No request visitor has been mapped for foo */ public function testThrowsExceptionWhenRetrievingUnknownVisitor() { VisitorFlyweight::getInstance()->getRequestVisitor('foo'); } public function testCachesVisitors() { $f = new VisitorFlyweight(); $v1 = $f->getRequestVisitor('json'); $this->assertSame($v1, $f->getRequestVisitor('json')); } public function testAllowsAddingVisitors() { $f = new VisitorFlyweight(); $j1 = new JsonRequestVisitor(); $j2 = new JsonResponseVisitor(); $f->addRequestVisitor('json', $j1); $f->addResponseVisitor('json', $j2); $this->assertSame($j1, $f->getRequestVisitor('json')); $this->assertSame($j2, $f->getResponseVisitor('json')); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/AbstractVisitorTestCase.php 0000604 00000007604 15173213566 0026652 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Http\Message\EntityEnclosingRequest; use Guzzle\Service\Description\Operation; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Description\SchemaValidator; use Guzzle\Service\Command\OperationCommand; use Guzzle\Tests\Service\Mock\Command\MockCommand; use Guzzle\Tests\Service\Mock\MockClient; abstract class AbstractVisitorTestCase extends \Guzzle\Tests\GuzzleTestCase { protected $command; protected $request; protected $param; protected $validator; public function setUp() { $this->command = new MockCommand(); $this->request = new EntityEnclosingRequest('POST', 'http://www.test.com/some/path.php'); $this->validator = new SchemaValidator(); } protected function getCommand($location) { $command = new OperationCommand(array(), $this->getNestedCommand($location)); $command->setClient(new MockClient()); return $command; } protected function getNestedCommand($location) { return new Operation(array( 'httpMethod' => 'POST', 'parameters' => array( 'foo' => new Parameter(array( 'type' => 'object', 'location' => $location, 'sentAs' => 'Foo', 'required' => true, 'properties' => array( 'test' => array( 'type' => 'object', 'required' => true, 'properties' => array( 'baz' => array( 'type' => 'boolean', 'default' => true ), 'jenga' => array( 'type' => 'string', 'default' => 'hello', 'sentAs' => 'Jenga_Yall!', 'filters' => array('strtoupper') ) ) ), 'bar' => array('default' => 123) ), 'additionalProperties' => array( 'type' => 'string', 'filters' => array('strtoupper'), 'location' => $location ) )), 'arr' => new Parameter(array( 'type' => 'array', 'location' => $location, 'items' => array( 'type' => 'string', 'filters' => array('strtoupper') ) )), ) )); } protected function getCommandWithArrayParamAndFilters() { $operation = new Operation(array( 'httpMethod' => 'POST', 'parameters' => array( 'foo' => new Parameter(array( 'type' => 'string', 'location' => 'query', 'sentAs' => 'Foo', 'required' => true, 'default' => 'bar', 'filters' => array('strtoupper') )), 'arr' => new Parameter(array( 'type' => 'array', 'location' => 'query', 'sentAs' => 'Arr', 'required' => true, 'default' => array(123, 456, 789), 'filters' => array(array('method' => 'implode', 'args' => array(',', '@value'))) )) ) )); $command = new OperationCommand(array(), $operation); $command->setClient(new MockClient()); return $command; } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFileVisitorTest.php 0000604 00000004054 15173213566 0026034 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Service\Client; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Http\Message\PostFile; use Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\PostFileVisitor */ class PostFileVisitorTest extends AbstractVisitorTestCase { public function testVisitsLocation() { $visitor = new Visitor(); $param = $this->getNestedCommand('postFile')->getParam('foo'); // Test using a path to a file $visitor->visit($this->command, $this->request, $param->setSentAs('test_3'), __FILE__); $this->assertInternalType('array', $this->request->getPostFile('test_3')); // Test with a PostFile $visitor->visit($this->command, $this->request, $param->setSentAs(null), new PostFile('baz', __FILE__)); $this->assertInternalType('array', $this->request->getPostFile('baz')); } public function testVisitsLocationWithMultipleFiles() { $description = ServiceDescription::factory(array( 'operations' => array( 'DoPost' => array( 'httpMethod' => 'POST', 'parameters' => array( 'foo' => array( 'location' => 'postFile', 'type' => array('string', 'array') ) ) ) ) )); $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()); $client->setDescription($description); $command = $client->getCommand('DoPost', array('foo' => array(__FILE__, __FILE__))); $command->execute(); $received = $this->getServer()->getReceivedRequests(); $this->assertContains('name="foo[0]";', $received[0]); $this->assertContains('name="foo[1]";', $received[0]); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/QueryVisitorTest.php 0000604 00000003256 15173213566 0025417 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor */ class QueryVisitorTest extends AbstractVisitorTestCase { public function testVisitsLocation() { $visitor = new Visitor(); $param = $this->getNestedCommand('query')->getParam('foo')->setSentAs('test'); $visitor->visit($this->command, $this->request, $param, '123'); $this->assertEquals('123', $this->request->getQuery()->get('test')); } /** * @covers Guzzle\Service\Command\LocationVisitor\Request\QueryVisitor * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively */ public function testRecursivelyBuildsQueryStrings() { $command = $this->getCommand('query'); $command->getOperation()->getParam('foo')->setSentAs('Foo'); $request = $command->prepare(); $this->assertEquals( 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', rawurldecode($request->getQuery()) ); } /** * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively */ public function testFiltersAreAppliedToArrayParamType() { $command = $this->getCommandWithArrayParamAndFilters(); $request = $command->prepare(); $query = $request->getQuery(); // param type 'string' $this->assertEquals('BAR', $query->get('Foo')); // param type 'array' $this->assertEquals('123,456,789', $query->get('Arr')); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/HeaderVisitorTest.php 0000604 00000003346 15173213566 0025502 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\HeaderVisitor */ class HeaderVisitorTest extends AbstractVisitorTestCase { /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testValidatesHeaderMapsAreArrays() { $visitor = new Visitor(); $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); $param->setAdditionalProperties(new Parameter(array())); $visitor->visit($this->command, $this->request, $param, 'test'); } public function testVisitsLocation() { $visitor = new Visitor(); $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); $param->setAdditionalProperties(false); $visitor->visit($this->command, $this->request, $param, '123'); $this->assertEquals('123', (string) $this->request->getHeader('test')); } public function testVisitsMappedPrefixHeaders() { $visitor = new Visitor(); $param = $this->getNestedCommand('header')->getParam('foo')->setSentAs('test'); $param->setSentAs('x-foo-'); $param->setAdditionalProperties(new Parameter(array( 'type' => 'string' ))); $visitor->visit($this->command, $this->request, $param, array( 'bar' => 'test', 'baz' => '123' )); $this->assertEquals('test', (string) $this->request->getHeader('x-foo-bar')); $this->assertEquals('123', (string) $this->request->getHeader('x-foo-baz')); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/XmlVisitorTest.php 0000604 00000052265 15173213566 0025056 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor; use Guzzle\Service\Client; use Guzzle\Service\Description\Parameter; use Guzzle\Service\Description\Operation; use Guzzle\Http\Message\EntityEnclosingRequest; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\XmlVisitor */ class XmlVisitorTest extends AbstractVisitorTestCase { public function xmlProvider() { return array( array( array( 'data' => array( 'xmlRoot' => array( 'name' => 'test', 'namespaces' => 'http://foo.com' ) ), 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string'), 'Baz' => array('location' => 'xml', 'type' => 'string') ) ), array('Foo' => 'test', 'Baz' => 'bar'), '<test xmlns="http://foo.com"><Foo>test</Foo><Baz>bar</Baz></test>' ), // Ensure that the content-type is not added array(array('parameters' => array('Foo' => array('location' => 'xml', 'type' => 'string'))), array(), ''), // Test with adding attributes and no namespace array( array( 'data' => array( 'xmlRoot' => array( 'name' => 'test' ) ), 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string', 'data' => array('xmlAttribute' => true)) ) ), array('Foo' => 'test', 'Baz' => 'bar'), '<test Foo="test"/>' ), // Test adding with an array array( array( 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string'), 'Baz' => array( 'type' => 'array', 'location' => 'xml', 'items' => array( 'type' => 'numeric', 'sentAs' => 'Bar' ) ) ) ), array('Foo' => 'test', 'Baz' => array(1, 2)), '<Request><Foo>test</Foo><Baz><Bar>1</Bar><Bar>2</Bar></Baz></Request>' ), // Test adding an object array( array( 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string'), 'Baz' => array( 'type' => 'object', 'location' => 'xml', 'properties' => array( 'Bar' => array('type' => 'string'), 'Bam' => array() ) ) ) ), array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), '<Request><Foo>test</Foo><Baz><Bar>abc</Bar><Bam>foo</Bam></Baz></Request>' ), // Add an array that contains an object array( array( 'parameters' => array( 'Baz' => array( 'type' => 'array', 'location' => 'xml', 'items' => array( 'type' => 'object', 'sentAs' => 'Bar', 'properties' => array('A' => array(), 'B' => array()) ) ) ) ), array('Baz' => array( array('A' => '1', 'B' => '2'), array('A' => '3', 'B' => '4') )), '<Request><Baz><Bar><A>1</A><B>2</B></Bar><Bar><A>3</A><B>4</B></Bar></Baz></Request>' ), // Add an object of attributes array( array( 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string'), 'Baz' => array( 'type' => 'object', 'location' => 'xml', 'properties' => array( 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), 'Bam' => array() ) ) ) ), array('Foo' => 'test', 'Baz' => array('Bar' => 'abc', 'Bam' => 'foo')), '<Request><Foo>test</Foo><Baz Bar="abc"><Bam>foo</Bam></Baz></Request>' ), // Check order doesn't matter array( array( 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string'), 'Baz' => array( 'type' => 'object', 'location' => 'xml', 'properties' => array( 'Bar' => array('type' => 'string', 'data' => array('xmlAttribute' => true)), 'Bam' => array() ) ) ) ), array('Foo' => 'test', 'Baz' => array('Bam' => 'foo', 'Bar' => 'abc')), '<Request><Foo>test</Foo><Baz Bar="abc"><Bam>foo</Bam></Baz></Request>' ), // Add values with custom namespaces array( array( 'parameters' => array( 'Foo' => array( 'location' => 'xml', 'type' => 'string', 'data' => array( 'xmlNamespace' => 'http://foo.com' ) ) ) ), array('Foo' => 'test'), '<Request><Foo xmlns="http://foo.com">test</Foo></Request>' ), // Add attributes with custom namespace prefix array( array( 'parameters' => array( 'Wrap' => array( 'type' => 'object', 'location' => 'xml', 'properties' => array( 'Foo' => array( 'type' => 'string', 'sentAs' => 'xsi:baz', 'data' => array( 'xmlNamespace' => 'http://foo.com', 'xmlAttribute' => true ) ) ) ), ) ), array('Wrap' => array( 'Foo' => 'test' )), '<Request><Wrap xsi:baz="test" xmlns:xsi="http://foo.com"/></Request>' ), // Add nodes with custom namespace prefix array( array( 'parameters' => array( 'Wrap' => array( 'type' => 'object', 'location' => 'xml', 'properties' => array( 'Foo' => array( 'type' => 'string', 'sentAs' => 'xsi:Foo', 'data' => array( 'xmlNamespace' => 'http://foobar.com' ) ) ) ), ) ), array('Wrap' => array( 'Foo' => 'test' )), '<Request><Wrap><xsi:Foo xmlns:xsi="http://foobar.com">test</xsi:Foo></Wrap></Request>' ), array( array( 'parameters' => array( 'Foo' => array( 'location' => 'xml', 'type' => 'string', 'data' => array( 'xmlNamespace' => 'http://foo.com' ) ) ) ), array('Foo' => '<h1>This is a title</h1>'), '<Request><Foo xmlns="http://foo.com"><![CDATA[<h1>This is a title</h1>]]></Foo></Request>' ), // Flat array at top level array( array( 'parameters' => array( 'Bars' => array( 'type' => 'array', 'data' => array('xmlFlattened' => true), 'location' => 'xml', 'items' => array( 'type' => 'object', 'sentAs' => 'Bar', 'properties' => array( 'A' => array(), 'B' => array() ) ) ), 'Boos' => array( 'type' => 'array', 'data' => array('xmlFlattened' => true), 'location' => 'xml', 'items' => array( 'sentAs' => 'Boo', 'type' => 'string' ) ) ) ), array( 'Bars' => array( array('A' => '1', 'B' => '2'), array('A' => '3', 'B' => '4') ), 'Boos' => array('test', '123') ), '<Request><Bar><A>1</A><B>2</B></Bar><Bar><A>3</A><B>4</B></Bar><Boo>test</Boo><Boo>123</Boo></Request>' ), // Nested flat arrays array( array( 'parameters' => array( 'Delete' => array( 'type' => 'object', 'location' => 'xml', 'properties' => array( 'Items' => array( 'type' => 'array', 'data' => array('xmlFlattened' => true), 'items' => array( 'type' => 'object', 'sentAs' => 'Item', 'properties' => array( 'A' => array(), 'B' => array() ) ) ) ) ) ) ), array( 'Delete' => array( 'Items' => array( array('A' => '1', 'B' => '2'), array('A' => '3', 'B' => '4') ) ) ), '<Request><Delete><Item><A>1</A><B>2</B></Item><Item><A>3</A><B>4</B></Item></Delete></Request>' ) ); } /** * @dataProvider xmlProvider */ public function testSerializesXml(array $operation, array $input, $xml) { $operation = new Operation($operation); $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') ->setConstructorArgs(array($input, $operation)) ->getMockForAbstractClass(); $command->setClient(new Client('http://www.test.com/some/path.php')); $request = $command->prepare(); if (!empty($input)) { $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); } else { $this->assertNull($request->getHeader('Content-Type')); } $body = str_replace(array("\n", "<?xml version=\"1.0\"?>"), '', (string) $request->getBody()); $this->assertEquals($xml, $body); } public function testAddsContentTypeAndTopLevelValues() { $operation = new Operation(array( 'data' => array( 'xmlRoot' => array( 'name' => 'test', 'namespaces' => array( 'xsi' => 'http://foo.com' ) ) ), 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string'), 'Baz' => array('location' => 'xml', 'type' => 'string') ) )); $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') ->setConstructorArgs(array(array( 'Foo' => 'test', 'Baz' => 'bar' ), $operation)) ->getMockForAbstractClass(); $command->setClient(new Client()); $request = $command->prepare(); $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); $this->assertEquals( '<?xml version="1.0"?>' . "\n" . '<test xmlns:xsi="http://foo.com"><Foo>test</Foo><Baz>bar</Baz></test>' . "\n", (string) $request->getBody() ); } public function testCanChangeContentType() { $visitor = new XmlVisitor(); $visitor->setContentTypeHeader('application/foo'); $this->assertEquals('application/foo', $this->readAttribute($visitor, 'contentType')); } public function testCanAddArrayOfSimpleTypes() { $request = new EntityEnclosingRequest('POST', 'http://foo.com'); $visitor = new XmlVisitor(); $param = new Parameter(array( 'type' => 'object', 'location' => 'xml', 'name' => 'Out', 'properties' => array( 'Nodes' => array( 'required' => true, 'type' => 'array', 'min' => 1, 'items' => array('type' => 'string', 'sentAs' => 'Node') ) ) )); $param->setParent(new Operation(array( 'data' => array( 'xmlRoot' => array( 'name' => 'Test', 'namespaces' => array( 'https://foo/' ) ) ) ))); $value = array('Nodes' => array('foo', 'baz')); $this->assertTrue($this->validator->validate($param, $value)); $visitor->visit($this->command, $request, $param, $value); $visitor->after($this->command, $request); $this->assertEquals( "<?xml version=\"1.0\"?>\n" . "<Test xmlns=\"https://foo/\"><Out><Nodes><Node>foo</Node><Node>baz</Node></Nodes></Out></Test>\n", (string) $request->getBody() ); } public function testCanAddMultipleNamespacesToRoot() { $operation = new Operation(array( 'data' => array( 'xmlRoot' => array( 'name' => 'Hi', 'namespaces' => array( 'xsi' => 'http://foo.com', 'foo' => 'http://foobar.com' ) ) ), 'parameters' => array( 'Foo' => array('location' => 'xml', 'type' => 'string') ) )); $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') ->setConstructorArgs(array(array( 'Foo' => 'test' ), $operation)) ->getMockForAbstractClass(); $command->setClient(new Client()); $request = $command->prepare(); $this->assertEquals('application/xml', (string) $request->getHeader('Content-Type')); $this->assertEquals( '<?xml version="1.0"?>' . "\n" . '<Hi xmlns:xsi="http://foo.com" xmlns:foo="http://foobar.com"><Foo>test</Foo></Hi>' . "\n", (string) $request->getBody() ); } public function testValuesAreFiltered() { $operation = new Operation(array( 'parameters' => array( 'Foo' => array( 'location' => 'xml', 'type' => 'string', 'filters' => array('strtoupper') ), 'Bar' => array( 'location' => 'xml', 'type' => 'object', 'properties' => array( 'Baz' => array( 'filters' => array('strtoupper') ) ) ) ) )); $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') ->setConstructorArgs(array(array( 'Foo' => 'test', 'Bar' => array( 'Baz' => 'abc' ) ), $operation)) ->getMockForAbstractClass(); $command->setClient(new Client()); $request = $command->prepare(); $this->assertEquals( '<?xml version="1.0"?>' . "\n" . '<Request><Foo>TEST</Foo><Bar><Baz>ABC</Baz></Bar></Request>' . "\n", (string) $request->getBody() ); } public function testSkipsNullValues() { $operation = new Operation(array( 'parameters' => array( 'Foo' => array( 'location' => 'xml', 'type' => 'string' ), 'Bar' => array( 'location' => 'xml', 'type' => 'object', 'properties' => array( 'Baz' => array(), 'Bam' => array(), ) ), 'Arr' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ) ) ) )); $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') ->setConstructorArgs(array(array( 'Foo' => null, 'Bar' => array( 'Bar' => null, 'Bam' => 'test' ), 'Arr' => array(null) ), $operation)) ->getMockForAbstractClass(); $command->setClient(new Client()); $request = $command->prepare(); $this->assertEquals( '<?xml version="1.0"?>' . "\n" . '<Request><Bar><Bam>test</Bam></Bar></Request>' . "\n", (string) $request->getBody() ); } public function testAllowsXmlEncoding() { $operation = new Operation(array( 'data' => array( 'xmlEncoding' => 'UTF-8' ), 'parameters' => array( 'Foo' => array('location' => 'xml') ) )); $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') ->setConstructorArgs(array(array('Foo' => 'test'), $operation)) ->getMockForAbstractClass(); $command->setClient(new Client()); $request = $command->prepare(); $this->assertEquals( '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . '<Request><Foo>test</Foo></Request>' . "\n", (string) $request->getBody() ); } public function testAllowsSendingXmlPayloadIfNoXmlParamsWereSet() { $operation = new Operation(array( 'httpMethod' => 'POST', 'data' => array('xmlAllowEmpty' => true), 'parameters' => array('Foo' => array('location' => 'xml')) )); $command = $this->getMockBuilder('Guzzle\Service\Command\OperationCommand') ->setConstructorArgs(array(array(), $operation)) ->getMockForAbstractClass(); $command->setClient(new Client('http://foo.com')); $request = $command->prepare(); $this->assertEquals( '<?xml version="1.0"?>' . "\n" . '<Request/>' . "\n", (string) $request->getBody() ); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/JsonVisitorTest.php 0000604 00000004546 15173213566 0025226 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\JsonVisitor * @covers Guzzle\Service\Command\LocationVisitor\Request\AbstractRequestVisitor::resolveRecursively */ class JsonVisitorTest extends AbstractVisitorTestCase { public function testVisitsLocation() { $visitor = new Visitor(); // Test after when no body query values were found $visitor->after($this->command, $this->request); $param = $this->getNestedCommand('json')->getParam('foo'); $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); $visitor->visit($this->command, $this->request, $param->setSentAs('test2'), 'abc'); $visitor->after($this->command, $this->request); $this->assertEquals('{"test":"123","test2":"abc"}', (string) $this->request->getBody()); } public function testAddsJsonHeader() { $visitor = new Visitor(); $visitor->setContentTypeHeader('application/json-foo'); $param = $this->getNestedCommand('json')->getParam('foo'); $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); $visitor->after($this->command, $this->request); $this->assertEquals('application/json-foo', (string) $this->request->getHeader('Content-Type')); } public function testRecursivelyBuildsJsonBodies() { $command = $this->getCommand('json'); $request = $command->prepare(); $this->assertEquals('{"Foo":{"test":{"baz":true,"Jenga_Yall!":"HELLO"},"bar":123}}', (string) $request->getBody()); } public function testAppliesFiltersToAdditionalProperties() { $command = $this->getCommand('json'); $command->set('foo', array('not_set' => 'abc')); $request = $command->prepare(); $result = json_decode($request->getBody(), true); $this->assertEquals('ABC', $result['Foo']['not_set']); } public function testAppliesFiltersToArrayItemValues() { $command = $this->getCommand('json'); $command->set('arr', array('a', 'b')); $request = $command->prepare(); $result = json_decode($request->getBody(), true); $this->assertEquals(array('A', 'B'), $result['arr']); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/ResponseBodyVisitorTest.php 0000604 00000001273 15173213566 0026723 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\ResponseBodyVisitor */ class ResponseBodyVisitorTest extends AbstractVisitorTestCase { public function testVisitsLocation() { $visitor = new Visitor(); $param = $this->getNestedCommand('response_body')->getParam('foo'); $visitor->visit($this->command, $this->request, $param, sys_get_temp_dir() . '/foo.txt'); $body = $this->readAttribute($this->request, 'responseBody'); $this->assertContains('/foo.txt', $body->getUri()); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/PostFieldVisitorTest.php 0000604 00000002226 15173213566 0026177 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\PostFieldVisitor */ class PostFieldVisitorTest extends AbstractVisitorTestCase { public function testVisitsLocation() { $visitor = new Visitor(); $param = $this->getNestedCommand('postField')->getParam('foo'); $visitor->visit($this->command, $this->request, $param->setSentAs('test'), '123'); $this->assertEquals('123', (string) $this->request->getPostField('test')); } public function testRecursivelyBuildsPostFields() { $command = $this->getCommand('postField'); $request = $command->prepare(); $visitor = new Visitor(); $param = $command->getOperation()->getParam('foo'); $visitor->visit($command, $request, $param, $command['foo']); $visitor->after($command, $request); $this->assertEquals( 'Foo[test][baz]=1&Foo[test][Jenga_Yall!]=HELLO&Foo[bar]=123', rawurldecode((string) $request->getPostFields()) ); } } guzzle/tests/Guzzle/Tests/Service/Command/LocationVisitor/Request/BodyVisitorTest.php 0000604 00000004764 15173213566 0025214 0 ustar 00 <?php namespace Guzzle\Tests\Service\Command\LocationVisitor\Request; use Guzzle\Http\EntityBody; use Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor as Visitor; /** * @covers Guzzle\Service\Command\LocationVisitor\Request\BodyVisitor */ class BodyVisitorTest extends AbstractVisitorTestCase { public function testVisitsLocation() { $visitor = new Visitor(); $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); $visitor->visit($this->command, $this->request, $param, '123'); $this->assertEquals('123', (string) $this->request->getBody()); $this->assertNull($this->request->getHeader('Expect')); } public function testAddsExpectHeaderWhenSetToTrue() { $visitor = new Visitor(); $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); $param->setData('expect_header', true); $visitor->visit($this->command, $this->request, $param, '123'); $this->assertEquals('123', (string) $this->request->getBody()); } public function testCanDisableExpectHeader() { $visitor = new Visitor(); $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); $param->setData('expect_header', false); $visitor->visit($this->command, $this->request, $param, '123'); $this->assertNull($this->request->getHeader('Expect')); } public function testCanSetExpectHeaderBasedOnSize() { $visitor = new Visitor(); $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); // The body is less than the cutoff $param->setData('expect_header', 5); $visitor->visit($this->command, $this->request, $param, '123'); $this->assertNull($this->request->getHeader('Expect')); // Now check when the body is greater than the cutoff $param->setData('expect_header', 2); $visitor->visit($this->command, $this->request, $param, '123'); $this->assertEquals('100-Continue', (string) $this->request->getHeader('Expect')); } public function testAddsContentEncodingWhenSetOnBody() { $visitor = new Visitor(); $param = $this->getNestedCommand('body')->getParam('foo')->setSentAs('Foo'); $body = EntityBody::factory('foo'); $body->compress(); $visitor->visit($this->command, $this->request, $param, $body); $this->assertEquals('gzip', (string) $this->request->getHeader('Content-Encoding')); } } guzzle/tests/Guzzle/Tests/Service/CachingConfigLoaderTest.php 0000604 00000002744 15173213566 0020406 0 ustar 00 <?php namespace Guzzle\Tests\Service; use Guzzle\Cache\DoctrineCacheAdapter; use Guzzle\Service\CachingConfigLoader; use Doctrine\Common\Cache\ArrayCache; /** * @covers Guzzle\Service\CachingConfigLoader */ class CachingConfigLoaderTest extends \Guzzle\Tests\GuzzleTestCase { public function testLoadsPhpFileIncludes() { $cache = new DoctrineCacheAdapter(new ArrayCache()); $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface') ->setMethods(array('load')) ->getMockForAbstractClass(); $data = array('foo' => 'bar'); $loader->expects($this->once()) ->method('load') ->will($this->returnValue($data)); $cache = new CachingConfigLoader($loader, $cache); $this->assertEquals($data, $cache->load('foo')); $this->assertEquals($data, $cache->load('foo')); } public function testDoesNotCacheArrays() { $cache = new DoctrineCacheAdapter(new ArrayCache()); $loader = $this->getMockBuilder('Guzzle\Service\ConfigLoaderInterface') ->setMethods(array('load')) ->getMockForAbstractClass(); $data = array('foo' => 'bar'); $loader->expects($this->exactly(2)) ->method('load') ->will($this->returnValue($data)); $cache = new CachingConfigLoader($loader, $cache); $this->assertEquals($data, $cache->load(array())); $this->assertEquals($data, $cache->load(array())); } } guzzle/tests/Guzzle/Tests/Service/Exception/ValidationExceptionTest.php 0000604 00000000626 15173213566 0022501 0 ustar 00 <?php namespace Guzzle\Tests\Service\Exception; use Guzzle\Service\Exception\ValidationException; class ValidationExceptionTest extends \Guzzle\Tests\GuzzleTestCase { public function testCanSetAndRetrieveErrors() { $errors = array('foo', 'bar'); $e = new ValidationException('Foo'); $e->setErrors($errors); $this->assertEquals($errors, $e->getErrors()); } } guzzle/tests/Guzzle/Tests/Service/Exception/InconsistentClientTransferExceptionTest.php 0000604 00000000635 15173213566 0025733 0 ustar 00 <?php namespace Guzzle\Tests\Service\Exception; use Guzzle\Service\Exception\InconsistentClientTransferException; class InconsistentClientTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase { public function testStoresCommands() { $items = array('foo', 'bar'); $e = new InconsistentClientTransferException($items); $this->assertEquals($items, $e->getCommands()); } } guzzle/tests/Guzzle/Tests/Service/Exception/CommandTransferExceptionTest.php 0000604 00000005034 15173213566 0023470 0 ustar 00 <?php namespace Guzzle\Tests\Service\Exception; use Guzzle\Http\Exception\MultiTransferException; use Guzzle\Http\Message\Request; use Guzzle\Service\Exception\CommandTransferException; use Guzzle\Tests\Service\Mock\Command\MockCommand; /** * @covers Guzzle\Service\Exception\CommandTransferException */ class CommandTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase { public function testStoresCommands() { $c1 = new MockCommand(); $c2 = new MockCommand(); $e = new CommandTransferException('Test'); $e->addSuccessfulCommand($c1)->addFailedCommand($c2); $this->assertSame(array($c1), $e->getSuccessfulCommands()); $this->assertSame(array($c2), $e->getFailedCommands()); $this->assertSame(array($c1, $c2), $e->getAllCommands()); } public function testConvertsMultiExceptionIntoCommandTransfer() { $r1 = new Request('GET', 'http://foo.com'); $r2 = new Request('GET', 'http://foobaz.com'); $e = new MultiTransferException('Test', 123); $e->addSuccessfulRequest($r1)->addFailedRequest($r2); $ce = CommandTransferException::fromMultiTransferException($e); $this->assertInstanceOf('Guzzle\Service\Exception\CommandTransferException', $ce); $this->assertEquals('Test', $ce->getMessage()); $this->assertEquals(123, $ce->getCode()); $this->assertSame(array($r1), $ce->getSuccessfulRequests()); $this->assertSame(array($r2), $ce->getFailedRequests()); } public function testCanRetrieveExceptionForCommand() { $r1 = new Request('GET', 'http://foo.com'); $e1 = new \Exception('foo'); $c1 = $this->getMockBuilder('Guzzle\Tests\Service\Mock\Command\MockCommand') ->setMethods(array('getRequest')) ->getMock(); $c1->expects($this->once())->method('getRequest')->will($this->returnValue($r1)); $e = new MultiTransferException('Test', 123); $e->addFailedRequestWithException($r1, $e1); $ce = CommandTransferException::fromMultiTransferException($e); $ce->addFailedCommand($c1); $this->assertSame($e1, $ce->getExceptionForFailedCommand($c1)); } public function testAddsNonRequestExceptions() { $e = new MultiTransferException(); $e->add(new \Exception('bar')); $e->addFailedRequestWithException(new Request('GET', 'http://www.foo.com'), new \Exception('foo')); $ce = CommandTransferException::fromMultiTransferException($e); $this->assertEquals(2, count($ce)); } } guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserProvider.php 0000604 00000033744 15173213566 0021107 0 ustar 00 <?php namespace Guzzle\Tests\Parser\Cookie; use Guzzle\Http\Url; /** * @covers Guzzle\Parser\Cookie\CookieParser */ class CookieParserProvider extends \Guzzle\Tests\GuzzleTestCase { /** * Provides the parsed information from a cookie * * @return array */ public function cookieParserDataProvider() { return array( array( 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/";', array( 'domain' => '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', '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%2Cef' ), '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 ) ), // rfc6265#section-5.1.4 array( 'cookie=value', array( 'cookies' => array( 'cookie' => 'value' ), 'domain' => 'example.com', 'data' => array(), 'discard' => null, 'expires' => null, 'max_age' => null, 'path' => '/some/path', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ), 'http://example.com/some/path/test.html' ), array( 'empty=path', array( 'cookies' => array( 'empty' => 'path' ), 'domain' => 'example.com', 'data' => array(), 'discard' => null, 'expires' => null, 'max_age' => null, 'path' => '/', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ), 'http://example.com/test.html' ), array( 'baz=qux', array( 'cookies' => array( 'baz' => 'qux' ), 'domain' => 'example.com', 'data' => array(), 'discard' => null, 'expires' => null, 'max_age' => null, 'path' => '/', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ), 'http://example.com?query=here' ), array( 'test=noSlashPath; path=someString', array( 'cookies' => array( 'test' => 'noSlashPath' ), 'domain' => 'example.com', 'data' => array(), 'discard' => null, 'expires' => null, 'max_age' => null, 'path' => '/real/path', 'port' => null, 'secure' => null, 'version' => null, 'comment' => null, 'comment_url' => null, 'http_only' => false ), 'http://example.com/real/path/' ), ); } /** * @dataProvider cookieParserDataProvider */ 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 by allowing a 5 minute difference if ($p['expires'] != $parsed['expires']) { if (abs($p['expires'] - $parsed['expires']) < 300) { 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); } } } } guzzle/tests/Guzzle/Tests/Parser/Cookie/CookieParserTest.php 0000604 00000001025 15173213566 0020217 0 ustar 00 <?php namespace Guzzle\Tests\Parser\Cookie; use Guzzle\Parser\Cookie\CookieParser; /** * @covers Guzzle\Parser\Cookie\CookieParser */ class CookieParserTest extends CookieParserProvider { protected $cookieParserClass = 'Guzzle\Parser\Cookie\CookieParser'; public function testUrlDecodesCookies() { $parser = new CookieParser(); $result = $parser->parseCookie('foo=baz+bar', null, null, true); $this->assertEquals(array( 'foo' => 'baz bar' ), $result['cookies']); } } guzzle/tests/Guzzle/Tests/Parser/ParserRegistryTest.php 0000604 00000001453 15173213566 0017412 0 ustar 00 <?php namespace Guzzle\Tests\Parser; use Guzzle\Parser\ParserRegistry; /** * @covers Guzzle\Parser\ParserRegistry */ class ParserRegistryTest extends \Guzzle\Tests\GuzzleTestCase { public function testStoresObjects() { $r = new ParserRegistry(); $c = new \stdClass(); $r->registerParser('foo', $c); $this->assertSame($c, $r->getParser('foo')); } public function testReturnsNullWhenNotFound() { $r = new ParserRegistry(); $this->assertNull($r->getParser('FOO')); } public function testReturnsLazyLoadedDefault() { $r = new ParserRegistry(); $c = $r->getParser('cookie'); $this->assertInstanceOf('Guzzle\Parser\Cookie\CookieParser', $c); $this->assertSame($c, $r->getParser('cookie')); } } guzzle/tests/Guzzle/Tests/Parser/Message/PeclHttpMessageParserTest.php 0000604 00000001606 15173213566 0022216 0 ustar 00 <?php namespace Guzzle\Tests\Parser\Message; use Guzzle\Parser\Message\PeclHttpMessageParser; /** * @covers Guzzle\Parser\Message\PeclHttpMessageParser */ class PeclHttpMessageParserTest extends MessageParserProvider { protected function setUp() { if (!function_exists('http_parse_message')) { $this->markTestSkipped('pecl_http is not available.'); } } /** * @dataProvider requestProvider */ public function testParsesRequests($message, $parts) { $parser = new PeclHttpMessageParser(); $this->compareRequestResults($parts, $parser->parseRequest($message)); } /** * @dataProvider responseProvider */ public function testParsesResponses($message, $parts) { $parser = new PeclHttpMessageParser(); $this->compareResponseResults($parts, $parser->parseResponse($message)); } } guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserProvider.php 0000604 00000020102 15173213566 0021415 0 ustar 00 <?php namespace Guzzle\Tests\Parser\Message; class MessageParserProvider extends \Guzzle\Tests\GuzzleTestCase { public function requestProvider() { $auth = base64_encode('michael:foo'); return array( // Empty request array('', false), // Converts casing of request. Does not require host header. array("GET / HTTP/1.1\r\n\r\n", array( 'method' => '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); } } guzzle/tests/Guzzle/Tests/Parser/Message/MessageParserTest.php 0000604 00000003476 15173213566 0020561 0 ustar 00 <?php namespace Guzzle\Tests\Parser\Message; use Guzzle\Parser\Message\MessageParser; /** * @covers Guzzle\Parser\Message\AbstractMessageParser * @covers Guzzle\Parser\Message\MessageParser */ class MessageParserTest extends MessageParserProvider { /** * @dataProvider requestProvider */ public function testParsesRequests($message, $parts) { $parser = new MessageParser(); $this->compareRequestResults($parts, $parser->parseRequest($message)); } /** * @dataProvider responseProvider */ public function testParsesResponses($message, $parts) { $parser = new MessageParser(); $this->compareResponseResults($parts, $parser->parseResponse($message)); } public function testParsesRequestsWithMissingProtocol() { $parser = new MessageParser(); $parts = $parser->parseRequest("GET /\r\nHost: Foo.com\r\n\r\n"); $this->assertEquals('GET', $parts['method']); $this->assertEquals('HTTP', $parts['protocol']); $this->assertEquals('1.1', $parts['version']); } public function testParsesRequestsWithMissingVersion() { $parser = new MessageParser(); $parts = $parser->parseRequest("GET / HTTP\r\nHost: Foo.com\r\n\r\n"); $this->assertEquals('GET', $parts['method']); $this->assertEquals('HTTP', $parts['protocol']); $this->assertEquals('1.1', $parts['version']); } public function testParsesResponsesWithMissingReasonPhrase() { $parser = new MessageParser(); $parts = $parser->parseResponse("HTTP/1.1 200\r\n\r\n"); $this->assertEquals('200', $parts['code']); $this->assertEquals('', $parts['reason_phrase']); $this->assertEquals('HTTP', $parts['protocol']); $this->assertEquals('1.1', $parts['version']); } } guzzle/tests/Guzzle/Tests/Parser/UriTemplate/AbstractUriTemplateTest.php 0000604 00000013132 15173213566 0022574 0 ustar 00 <?php namespace Guzzle\Tests\Parsers\UriTemplate; abstract class AbstractUriTemplateTest extends \Guzzle\Tests\GuzzleTestCase { /** * @return array */ public function templateProvider() { $t = array(); $params = array( 'var' => 'value', 'hello' => 'Hello World!', 'empty' => '', 'path' => '/foo/bar', 'x' => '1024', 'y' => '768', 'null' => null, 'list' => array('red', 'green', 'blue'), 'keys' => array( "semi" => ';', "dot" => '.', "comma" => ',' ), 'empty_keys' => array(), ); 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'), array('{.null}', ''), array('{.null,var}', '.value'), array('X{.empty_keys*}', 'X'), array('X{.empty_keys}', 'X'), // 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 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') )); } } guzzle/tests/Guzzle/Tests/Parser/UriTemplate/UriTemplateTest.php 0000604 00000006254 15173213566 0021117 0 ustar 00 <?php namespace Guzzle\Tests\Parsers\UriTemplate; use Guzzle\Parser\UriTemplate\UriTemplate; /** * @covers Guzzle\Parser\UriTemplate\UriTemplate */ class UriTemplateTest extends AbstractUriTemplateTest { /** * @dataProvider templateProvider */ public function testExpandsUriTemplates($template, $expansion, $params) { $uri = new UriTemplate($template); $this->assertEquals($expansion, $uri->expand($template, $params)); } 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); } /** * @ticket https://github.com/guzzle/guzzle/issues/426 */ public function testSetRegex() { $template = new UriTemplate(); $template->setRegex('/\<\$(.+)\>/'); $this->assertSame('/foo', $template->expand('/<$a>', array('a' => 'foo'))); } } guzzle/tests/Guzzle/Tests/Parser/UriTemplate/PeclUriTemplateTest.php 0000604 00000001273 15173213566 0021717 0 ustar 00 <?php namespace Guzzle\Tests\Parsers\UriTemplate; use Guzzle\Parser\UriTemplate\PeclUriTemplate; /** * @covers Guzzle\Parser\UriTemplate\PeclUriTemplate */ class PeclUriTemplateTest extends AbstractUriTemplateTest { protected function setUp() { if (!extension_loaded('uri_template')) { $this->markTestSkipped('uri_template PECL extension must be installed to test PeclUriTemplate'); } } /** * @dataProvider templateProvider */ public function testExpandsUriTemplates($template, $expansion, $params) { $uri = new PeclUriTemplate($template); $this->assertEquals($expansion, $uri->expand($template, $params)); } } guzzle/tests/Guzzle/Tests/TestData/test_service_3.json 0000604 00000002225 15173213566 0017143 0 ustar 00 { "includes": [ "test_service2.json" ], "operations": { "test": { "uri": "/path" }, "concrete": { "extends": "abstract" }, "baz_qux": { "uri": "/testing", "parameters": { "other": { "location": "json", "location_key": "Other" }, "test": { "type": "object", "location": "json", "properties": { "baz": { "type": "boolean", "default": true }, "bar": { "type": "string", "filters": [ { "method": "strtolower", "args": ["test", "@value"] }, "strtoupper" ] } } } } } } } guzzle/tests/Guzzle/Tests/TestData/services/json1.json 0000604 00000000653 15173213566 0017102 0 ustar 00 { "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" } } } } guzzle/tests/Guzzle/Tests/TestData/services/services.json 0000604 00000003336 15173213566 0017674 0 ustar 00 { "abstract": { "access_key": "xyz", "secret": "abc" }, "mock": { "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", "extends": "abstract", "params": { "username": "foo", "password": "baz", "subdomain": "bar" } }, "test.abstract.aws": { "params": { "access_key": "12345", "secret_key": "abcd" } }, "test.s3": { "class": "Guzzle\\Service\\Aws\\S3Client", "extends": "test.abstract.aws", "params": { "devpay_product_token": "", "devpay_user_token": "" } }, "test.simple_db": { "class": "Guzzle\\Service\\Aws\\SimpleDb\\SimpleDbClient", "extends": "test.abstract.aws" }, "test.sqs": { "class": "Guzzle\\Service\\Aws\\Sqs\\SqsClient", "extends": "test.abstract.aws" }, "test.centinel": { "class": "Guzzle\\Service\\CardinalCommerce\\Centinel.CentinelClient", "params": { "password": "test", "processor_id": "123", "merchant_id": "456" } }, "test.mws": { "class": "Guzzle\\Service\\Mws\\MwsClient", "extends": "test.abstract.aws", "params": { "merchant_id": "ABCDE", "marketplace_id": "FGHIJ", "application_name": "GuzzleTest", "application_version": "0.1", "base_url": "https://mws.amazonservices.com" } }, "mock": { "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", "params": { "username": "test_user", "password": "****", "subdomain": "test" } } } guzzle/tests/Guzzle/Tests/TestData/services/json2.json 0000604 00000000336 15173213566 0017101 0 ustar 00 { "services": { "foo": { "class": "Guzzle\\Tests\\Service\\Mock\\MockClient", "extends": "abstract", "params": { "baz": "bar" } } } } guzzle/tests/Guzzle/Tests/TestData/test_service2.json 0000604 00000000135 15173213566 0017001 0 ustar 00 { "operations": { "abstract": { "uri": "/abstract" } } } guzzle/tests/Guzzle/Tests/TestData/test_service.json 0000604 00000002225 15173213566 0016721 0 ustar 00 { "includes": [ "test_service2.json" ], "operations": { "test": { "uri": "/path" }, "concrete": { "extends": "abstract" }, "foo_bar": { "uri": "/testing", "parameters": { "other": { "location": "json", "location_key": "Other" }, "test": { "type": "object", "location": "json", "properties": { "baz": { "type": "boolean", "default": true }, "bar": { "type": "string", "filters": [ { "method": "strtolower", "args": ["test", "@value"] }, "strtoupper" ] } } } } } } } guzzle/tests/Guzzle/Tests/TestData/FileBody.txt 0000604 00000000000 15173213566 0015552 0 ustar 00 guzzle/tests/Guzzle/Tests/TestData/description/bar.json 0000604 00000000041 15173213566 0017303 0 ustar 00 { "includes": ["foo.json"] } guzzle/tests/Guzzle/Tests/TestData/description/foo.json 0000604 00000000203 15173213566 0017322 0 ustar 00 { "includes": ["recursive.json"], "operations": { "abstract": { "httpMethod": "POST" } } } guzzle/tests/Guzzle/Tests/TestData/description/baz.json 0000604 00000000055 15173213566 0017320 0 ustar 00 { "includes": ["foo.json", "bar.json"] } guzzle/tests/Guzzle/Tests/TestData/description/recursive.json 0000604 00000000041 15173213566 0020546 0 ustar 00 { "includes": ["foo.json"] } guzzle/tests/Guzzle/Tests/TestData/mock_response 0000604 00000000043 15173213566 0016115 0 ustar 00 HTTP/1.1 200 OK Content-Length: 0 guzzle/tests/Guzzle/Tests/Http/EntityBodyTest.php 0000604 00000015207 15173213566 0016204 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\EntityBody; use Guzzle\Http\QueryString; /** * @group server * @covers Guzzle\Http\EntityBody */ class EntityBodyTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testFactoryThrowsException() { $body = EntityBody::factory(false); } public function testFactory() { $body = EntityBody::factory('data'); $this->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)); } 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()); } public function testFactoryCanCreateFromObject() { $body = EntityBody::factory(new QueryString(array('foo' => 'bar'))); $this->assertEquals('foo=bar', (string) $body); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testFactoryEnsuresObjectsHaveToStringMethod() { EntityBody::factory(new \stdClass('a')); } 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'); } public function testDeterminesContentType() { // Test using a string/temp stream $body = EntityBody::factory('testing 123...testing 123'); $this->assertNull($body->getContentType()); // Use a local file $body = EntityBody::factory(fopen(__FILE__, 'r')); $this->assertContains('text/x-', $body->getContentType()); } 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()); } public function testSeeksToOriginalPosAfterMd5() { $body = EntityBody::factory('testing 123'); $body->seek(4); $this->assertEquals(md5('testing 123'), $body->getContentMd5()); $this->assertEquals(4, $body->ftell()); $this->assertEquals('ing 123', $body->read(1000)); } public function testGetTypeFormBodyFactoring() { $body = EntityBody::factory(array('key1' => 'val1', 'key2' => 'val2')); $this->assertEquals('key1=val1&key2=val2', (string) $body); } public function testAllowsCustomRewind() { $body = EntityBody::factory('foo'); $rewound = false; $body->setRewindFunction(function ($body) use (&$rewound) { $rewound = true; return $body->seek(0); }); $body->seek(2); $this->assertTrue($body->rewind()); $this->assertTrue($rewound); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testCustomRewindFunctionMustBeCallable() { $body = EntityBody::factory(); $body->setRewindFunction('foo'); } } guzzle/tests/Guzzle/Tests/Http/StaticClientTest.php 0000604 00000004441 15173213567 0016477 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Redirect; use Guzzle\Http\Client; use Guzzle\Http\StaticClient; use Guzzle\Plugin\Mock\MockPlugin; use Guzzle\Http\Message\Response; use Guzzle\Stream\Stream; /** * @covers Guzzle\Http\StaticClient */ class StaticClientTest extends \Guzzle\Tests\GuzzleTestCase { public function testMountsClient() { $client = new Client(); StaticClient::mount('FooBazBar', $client); $this->assertTrue(class_exists('FooBazBar')); $this->assertSame($client, $this->readAttribute('Guzzle\Http\StaticClient', 'client')); } public function requestProvider() { return array_map( function ($m) { return array($m); }, array('GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS') ); } /** * @dataProvider requestProvider */ public function testSendsRequests($method) { $mock = new MockPlugin(array(new Response(200))); call_user_func('Guzzle\Http\StaticClient::' . $method, 'http://foo.com', array( 'plugins' => array($mock) )); $requests = $mock->getReceivedRequests(); $this->assertCount(1, $requests); $this->assertEquals($method, $requests[0]->getMethod()); } public function testCanCreateStreamsUsingDefaultFactory() { $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); $stream = StaticClient::get($this->getServer()->getUrl(), array('stream' => true)); $this->assertInstanceOf('Guzzle\Stream\StreamInterface', $stream); $this->assertEquals('test', (string) $stream); } public function testCanCreateStreamsUsingCustomFactory() { $stream = $this->getMockBuilder('Guzzle\Stream\StreamRequestFactoryInterface') ->setMethods(array('fromRequest')) ->getMockForAbstractClass(); $resource = new Stream(fopen('php://temp', 'r+')); $stream->expects($this->once()) ->method('fromRequest') ->will($this->returnValue($resource)); $this->getServer()->enqueue(array("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest")); $result = StaticClient::get($this->getServer()->getUrl(), array('stream' => $stream)); $this->assertSame($resource, $result); } } guzzle/tests/Guzzle/Tests/Http/Server.php 0000604 00000013026 15173213567 0014516 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Common\Exception\RuntimeException; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\RequestInterface; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Client; /** * The Server class is used to control a scripted webserver using node.js that * will respond to HTTP requests with queued responses. * * Queued responses will be served to requests using a FIFO order. All requests * received by the server are stored on the node.js server and can be retrieved * by calling {@see Server::getReceivedRequests()}. * * Mock responses that don't require data to be transmitted over HTTP a great * for testing. Mock response, however, cannot test the actual sending of an * HTTP request using cURL. This test server allows the simulation of any * number of HTTP request response transactions to test the actual sending of * requests over the wire without having to leave an internal network. */ class Server { const DEFAULT_PORT = 8124; const REQUEST_DELIMITER = "\n----[request]\n"; /** @var int Port that the server will listen on */ private $port; /** @var bool Is the server running */ private $running = false; /** @var Client */ private $client; /** * Create a new scripted server * * @param int $port Port to listen on (defaults to 8124) */ public function __construct($port = null) { $this->port = $port ?: self::DEFAULT_PORT; $this->client = new Client($this->getUrl()); register_shutdown_function(array($this, 'stop')); } /** * Flush the received requests from the server * @throws RuntimeException */ public function flush() { $this->client->delete('guzzle-server/requests')->send(); } /** * 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 * @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()->toArray(), 'body' => $response->getBody(true) ); } $request = $this->client->put('guzzle-server/responses', null, json_encode($data)); $request->send(); } /** * Check if the server is running * * @return bool */ public function isRunning() { if ($this->running) { return true; } try { $this->client->get('guzzle-server/perf', array(), array('timeout' => 5))->send(); $this->running = true; return true; } catch (\Exception $e) { return false; } } /** * 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) { $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->running) { throw new RuntimeException( 'Unable to contact server.js. Have you installed node.js v0.5.0+? node must be in your path.' ); } } } /** * Stop running the node.js server */ public function stop() { if (!$this->isRunning()) { return false; } $this->running = false; $this->client->delete('guzzle-server')->send(); } } guzzle/tests/Guzzle/Tests/Http/UrlTest.php 0000604 00000027667 15173213567 0014672 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\QueryString; use Guzzle\Http\Url; /** * @covers Guzzle\Http\Url */ class UrlTest extends \Guzzle\Tests\GuzzleTestCase { public function testEmptyUrl() { $url = Url::factory(''); $this->assertEquals('', (string) $url); } public function testPortIsDeterminedFromScheme() { $this->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()); } public function testCloneCreatesNewInternalObjects() { $u1 = Url::factory('http://www.test.com/'); $u2 = clone $u1; $this->assertNotSame($u1->getQuery(), $u2->getQuery()); } 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()); } public function testAllowsFalsyUrlParts() { $url = Url::factory('http://0:50/0?0#0'); $this->assertSame('0', $url->getHost()); $this->assertEquals(50, $url->getPort()); $this->assertSame('/0', $url->getPath()); $this->assertEquals('0', (string) $url->getQuery()); $this->assertSame('0', $url->getFragment()); $this->assertEquals('http://0:50/0?0#0', (string) $url); $url = Url::factory(''); $this->assertSame('', (string) $url); $url = Url::factory('0'); $this->assertSame('0', (string) $url); } public function testBuildsRelativeUrlsWithFalsyParts() { $url = Url::buildUrl(array( 'host' => '0', 'path' => '0', )); $this->assertSame('//0/0', $url); $url = Url::buildUrl(array( 'path' => '0', )); $this->assertSame('0', $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', 'scheme' => 'http', 'user' => 'test' ), $url->getParts()); } 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)); } public function testAddsQueryStringIfPresent() { $this->assertEquals('?foo=bar', Url::buildUrl(array( 'query' => 'foo=bar' ))); } 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(null)); $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(array())); $this->assertEquals('http://e.com/base?a=1', (string) Url::factory('http://e.com/base?a=1')->addPath(new \stdClass())); $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/baz/foo', (string) Url::factory('http://e.com/baz/')->addPath('foo')); $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')); $this->assertEquals('http://e.com/base/0', (string) Url::factory('http://e.com/base')->addPath('0')); $this->assertEquals('http://e.com/base/0/1', (string) Url::factory('http://e.com/base')->addPath('0')->addPath('1')); } /** * 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'), array('http://www.example.com:8080/path', 'http://test.com', 'http://test.com'), 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/'), array('/path?q=2', 'http://www.test.com/', 'http://www.test.com/path?q=2'), array('http://api.flickr.com/services/', 'http://www.flickr.com/services/oauth/access_token', 'http://www.flickr.com/services/oauth/access_token'), array('http://www.example.com/?foo=bar', 'some/path', 'http://www.example.com/some/path?foo=bar'), array('http://www.example.com/?foo=bar', 'some/path?boo=moo', 'http://www.example.com/some/path?boo=moo&foo=bar'), array('http://www.example.com/some/', 'path?foo=bar&foo=baz', 'http://www.example.com/some/path?foo=bar&foo=baz'), ); } /** * @dataProvider urlCombineDataProvider */ public function testCombinesUrls($a, $b, $c) { $this->assertEquals($c, (string) Url::factory($a)->combine($b)); } 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); } 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); } public 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('*', '*'), array('/foo', '/foo'), array('/abc/123/../foo/', '/abc/foo/'), array('/a/b/c/./../../g', '/a/g'), array('/b/c/./../../g', '/g'), array('/b/c/./../../g', '/g'), array('/c/./../../g', '/g'), array('/./../../g', '/g'), ); } /** * @dataProvider urlProvider */ public function testNormalizesPaths($path, $result) { $url = Url::factory('http://www.example.com/'); $url->setPath($path)->normalizePath(); $this->assertEquals($result, $url->getPath()); } public function testSettingHostWithPortModifiesPort() { $url = Url::factory('http://www.example.com'); $url->setHost('foo:8983'); $this->assertEquals('foo', $url->getHost()); $this->assertEquals(8983, $url->getPort()); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testValidatesUrlCanBeParsed() { Url::factory('foo:////'); } public function testConvertsSpecialCharsInPathWhenCastingToString() { $url = Url::factory('http://foo.com/baz bar?a=b'); $url->addPath('?'); $this->assertEquals('http://foo.com/baz%20bar/%3F?a=b', (string) $url); } /** * @link http://tools.ietf.org/html/rfc3986#section-5.4.1 */ public function rfc3986UrlProvider() { $result = array( array('g', 'http://a/b/c/g'), array('./g', 'http://a/b/c/g'), array('g/', 'http://a/b/c/g/'), array('/g', 'http://a/g'), array('?y', 'http://a/b/c/d;p?y'), array('g?y', 'http://a/b/c/g?y'), array('#s', 'http://a/b/c/d;p?q#s'), array('g#s', 'http://a/b/c/g#s'), array('g?y#s', 'http://a/b/c/g?y#s'), array(';x', 'http://a/b/c/;x'), array('g;x', 'http://a/b/c/g;x'), array('g;x?y#s', 'http://a/b/c/g;x?y#s'), array('', 'http://a/b/c/d;p?q'), array('.', 'http://a/b/c'), array('./', 'http://a/b/c/'), array('..', 'http://a/b'), array('../', 'http://a/b/'), array('../g', 'http://a/b/g'), array('../..', 'http://a/'), array('../../', 'http://a/'), array('../../g', 'http://a/g') ); // This support was added in PHP 5.4.7: https://bugs.php.net/bug.php?id=62844 if (version_compare(PHP_VERSION, '5.4.7', '>=')) { $result[] = array('//g', 'http://g'); } return $result; } /** * @dataProvider rfc3986UrlProvider */ public function testCombinesUrlsUsingRfc3986($relative, $result) { $a = Url::factory('http://a/b/c/d;p?q'); $b = Url::factory($relative); $this->assertEquals($result, trim((string) $a->combine($b, true), '=')); } } guzzle/tests/Guzzle/Tests/Http/CachingEntityBodyTest.php 0000604 00000021432 15173213567 0017457 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\EntityBody; use Guzzle\Http\CachingEntityBody; /** * @covers Guzzle\Http\CachingEntityBody */ class CachingEntityBodyTest extends \Guzzle\Tests\GuzzleTestCase { /** @var CachingEntityBody */ protected $body; /** @var EntityBody */ protected $decorated; public function setUp() { $this->decorated = EntityBody::factory('testing'); $this->body = new CachingEntityBody($this->decorated); } public function testUsesRemoteSizeIfPossible() { $body = EntityBody::factory('test'); $caching = new CachingEntityBody($body); $this->assertEquals(4, $caching->getSize()); $this->assertEquals(4, $caching->getContentLength()); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException * @expectedExceptionMessage does not support custom stream rewind */ public function testDoesNotAllowRewindFunction() { $this->body->setRewindFunction(true); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException * @expectedExceptionMessage Cannot seek to byte 10 */ public function testCannotSeekPastWhatHasBeenRead() { $this->body->seek(10); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException * @expectedExceptionMessage supports only SEEK_SET and SEEK_CUR */ public function testCannotUseSeekEnd() { $this->body->seek(2, SEEK_END); } public function testChangingUnderlyingStreamUpdatesSizeAndStream() { $size = filesize(__FILE__); $s = fopen(__FILE__, 'r'); $this->body->setStream($s, $size); $this->assertEquals($size, $this->body->getSize()); $this->assertEquals($size, $this->decorated->getSize()); $this->assertSame($s, $this->body->getStream()); $this->assertSame($s, $this->decorated->getStream()); } public function testRewindUsesSeek() { $a = EntityBody::factory('foo'); $d = $this->getMockBuilder('Guzzle\Http\CachingEntityBody') ->setMethods(array('seek')) ->setConstructorArgs(array($a)) ->getMock(); $d->expects($this->once()) ->method('seek') ->with(0) ->will($this->returnValue(true)); $d->rewind(); } public function testCanSeekToReadBytes() { $this->assertEquals('te', $this->body->read(2)); $this->body->seek(0); $this->assertEquals('test', $this->body->read(4)); $this->assertEquals(4, $this->body->ftell()); $this->body->seek(2); $this->assertEquals(2, $this->body->ftell()); $this->body->seek(2, SEEK_CUR); $this->assertEquals(4, $this->body->ftell()); $this->assertEquals('ing', $this->body->read(3)); } public function testWritesToBufferStream() { $this->body->read(2); $this->body->write('hi'); $this->body->rewind(); $this->assertEquals('tehiing', (string) $this->body); } public function testReadLinesFromBothStreams() { $this->body->seek($this->body->ftell()); $this->body->write("test\n123\nhello\n1234567890\n"); $this->body->rewind(); $this->assertEquals("test\n", $this->body->readLine(7)); $this->assertEquals("123\n", $this->body->readLine(7)); $this->assertEquals("hello\n", $this->body->readLine(7)); $this->assertEquals("123456", $this->body->readLine(7)); $this->assertEquals("7890\n", $this->body->readLine(7)); // We overwrote the decorated stream, so no more data $this->assertEquals('', $this->body->readLine(7)); } public function testSkipsOverwrittenBytes() { $decorated = EntityBody::factory( implode("\n", array_map(function ($n) { return str_pad($n, 4, '0', STR_PAD_LEFT); }, range(0, 25))) ); $body = new CachingEntityBody($decorated); $this->assertEquals("0000\n", $body->readLine()); $this->assertEquals("0001\n", $body->readLine()); // Write over part of the body yet to be read, so skip some bytes $this->assertEquals(5, $body->write("TEST\n")); $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); // Read, which skips bytes, then reads $this->assertEquals("0003\n", $body->readLine()); $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); $this->assertEquals("0004\n", $body->readLine()); $this->assertEquals("0005\n", $body->readLine()); // Overwrite part of the cached body (so don't skip any bytes) $body->seek(5); $this->assertEquals(5, $body->write("ABCD\n")); $this->assertEquals(0, $this->readAttribute($body, 'skipReadBytes')); $this->assertEquals("TEST\n", $body->readLine()); $this->assertEquals("0003\n", $body->readLine()); $this->assertEquals("0004\n", $body->readLine()); $this->assertEquals("0005\n", $body->readLine()); $this->assertEquals("0006\n", $body->readLine()); $this->assertEquals(5, $body->write("1234\n")); $this->assertEquals(5, $this->readAttribute($body, 'skipReadBytes')); // Seek to 0 and ensure the overwritten bit is replaced $body->rewind(); $this->assertEquals("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", $body->read(50)); // Ensure that casting it to a string does not include the bit that was overwritten $this->assertContains("0000\nABCD\nTEST\n0003\n0004\n0005\n0006\n1234\n0008\n0009\n", (string) $body); } public function testWrapsContentType() { $a = $this->getMockBuilder('Guzzle\Http\EntityBody') ->setMethods(array('getContentType')) ->setConstructorArgs(array(fopen(__FILE__, 'r'))) ->getMock(); $a->expects($this->once()) ->method('getContentType') ->will($this->returnValue('foo')); $d = new CachingEntityBody($a); $this->assertEquals('foo', $d->getContentType()); } public function testWrapsContentEncoding() { $a = $this->getMockBuilder('Guzzle\Http\EntityBody') ->setMethods(array('getContentEncoding')) ->setConstructorArgs(array(fopen(__FILE__, 'r'))) ->getMock(); $a->expects($this->once()) ->method('getContentEncoding') ->will($this->returnValue('foo')); $d = new CachingEntityBody($a); $this->assertEquals('foo', $d->getContentEncoding()); } public function testWrapsMetadata() { $a = $this->getMockBuilder('Guzzle\Http\EntityBody') ->setMethods(array('getMetadata', 'getWrapper', 'getWrapperData', 'getStreamType', 'getUri')) ->setConstructorArgs(array(fopen(__FILE__, 'r'))) ->getMock(); $a->expects($this->once()) ->method('getMetadata') ->will($this->returnValue(array())); // Called twice for getWrapper and getWrapperData $a->expects($this->exactly(1)) ->method('getWrapper') ->will($this->returnValue('wrapper')); $a->expects($this->once()) ->method('getWrapperData') ->will($this->returnValue(array())); $a->expects($this->once()) ->method('getStreamType') ->will($this->returnValue('baz')); $a->expects($this->once()) ->method('getUri') ->will($this->returnValue('path/to/foo')); $d = new CachingEntityBody($a); $this->assertEquals(array(), $d->getMetaData()); $this->assertEquals('wrapper', $d->getWrapper()); $this->assertEquals(array(), $d->getWrapperData()); $this->assertEquals('baz', $d->getStreamType()); $this->assertEquals('path/to/foo', $d->getUri()); } public function testWrapsCustomData() { $a = $this->getMockBuilder('Guzzle\Http\EntityBody') ->setMethods(array('getCustomData', 'setCustomData')) ->setConstructorArgs(array(fopen(__FILE__, 'r'))) ->getMock(); $a->expects($this->exactly(1)) ->method('getCustomData') ->with('foo') ->will($this->returnValue('bar')); $a->expects($this->exactly(1)) ->method('setCustomData') ->with('foo', 'bar') ->will($this->returnSelf()); $d = new CachingEntityBody($a); $this->assertSame($d, $d->setCustomData('foo', 'bar')); $this->assertEquals('bar', $d->getCustomData('foo')); } public function testClosesBothStreams() { $s = fopen('php://temp', 'r'); $a = EntityBody::factory($s); $d = new CachingEntityBody($a); $d->close(); $this->assertFalse(is_resource($s)); } } guzzle/tests/Guzzle/Tests/Http/Curl/CurlVersionTest.php 0000604 00000002227 15173213567 0017271 0 ustar 00 <?php namespace Guzzle\Tests\Http\Curl; use Guzzle\Http\Curl\CurlVersion; /** * @covers Guzzle\Http\Curl\CurlVersion */ class CurlVersionTest extends \Guzzle\Tests\GuzzleTestCase { public function testCachesCurlInfo() { $info = curl_version(); $instance = CurlVersion::getInstance(); // Clear out the info cache $refObject = new \ReflectionObject($instance); $refProperty = $refObject->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 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()); } } guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiProxyTest.php 0000604 00000007411 15173213567 0020000 0 ustar 00 <?php namespace Guzzle\Tests\Http\Curl; use Guzzle\Http\Client; use Guzzle\Http\Message\Request; use Guzzle\Http\Curl\CurlMultiProxy; /** * @group server * @covers Guzzle\Http\Curl\CurlMultiProxy */ class CurlMultiProxyTest extends \Guzzle\Tests\GuzzleTestCase { const SELECT_TIMEOUT = 23.1; const MAX_HANDLES = 2; /** @var \Guzzle\Http\Curl\CurlMultiProxy */ private $multi; protected function setUp() { parent::setUp(); $this->multi = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); } public function tearDown() { unset($this->multi); } public function testConstructorSetsMaxHandles() { $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); $this->assertEquals(self::MAX_HANDLES, $this->readAttribute($m, 'maxHandles')); } public function testConstructorSetsSelectTimeout() { $m = new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT); $this->assertEquals(self::SELECT_TIMEOUT, $this->readAttribute($m, 'selectTimeout')); } public function testAddingRequestsAddsToQueue() { $r = new Request('GET', 'http://www.foo.com'); $this->assertSame($this->multi, $this->multi->add($r)); $this->assertEquals(1, count($this->multi)); $this->assertEquals(array($r), $this->multi->all()); $this->assertTrue($this->multi->remove($r)); $this->assertFalse($this->multi->remove($r)); $this->assertEquals(0, count($this->multi)); } public function testResetClearsState() { $r = new Request('GET', 'http://www.foo.com'); $this->multi->add($r); $this->multi->reset(); $this->assertEquals(0, count($this->multi)); } public function testSendWillSendQueuedRequestsFirst() { $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: 0\r\n\r\n" )); $client = new Client($this->getServer()->getUrl()); $events = array(); $client->getCurlMulti()->getEventDispatcher()->addListener( CurlMultiProxy::ADD_REQUEST, function ($e) use (&$events) { $events[] = $e; } ); $request = $client->get(); $request->getEventDispatcher()->addListener('request.complete', function () use ($client) { $client->get('/foo')->send(); }); $request->send(); $received = $this->getServer()->getReceivedRequests(true); $this->assertEquals(2, count($received)); $this->assertEquals($this->getServer()->getUrl(), $received[0]->getUrl()); $this->assertEquals($this->getServer()->getUrl() . 'foo', $received[1]->getUrl()); $this->assertEquals(2, count($events)); } public function testTrimsDownMaxHandleCount() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 307 OK\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 307 OK\r\nLocation: /foo\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()); $client->setCurlMulti(new CurlMultiProxy(self::MAX_HANDLES, self::SELECT_TIMEOUT)); $request = $client->get(); $request->send(); $this->assertEquals(200, $request->getResponse()->getStatusCode()); $handles = $this->readAttribute($client->getCurlMulti(), 'handles'); $this->assertEquals(2, count($handles)); } } guzzle/tests/Guzzle/Tests/Http/Curl/RequestMediatorTest.php 0000604 00000005022 15173213567 0020127 0 ustar 00 <?php namespace Guzzle\Tests\Http\Curl; use Guzzle\Http\Client; use Guzzle\Http\Message\EntityEnclosingRequest; use Guzzle\Http\Message\Response; use Guzzle\Http\Curl\RequestMediator; /** * @covers Guzzle\Http\Curl\RequestMediator */ class RequestMediatorTest extends \Guzzle\Tests\GuzzleTestCase { public $events = array(); public function event($event) { $this->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->getCurlOptions()->set('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']); } public function testDoesNotUseRequestResponseBodyWhenNotCustom() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 307 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nHI", "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 2\r\n\r\nFI", "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", )); $client = new Client($this->getServer()->getUrl()); $response = $client->get()->send(); $this->assertEquals('test', $response->getBody(true)); } } guzzle/tests/Guzzle/Tests/Http/Curl/CurlHandleTest.php 0000604 00000113342 15173213567 0017040 0 ustar 00 <?php namespace Guzzle\Tests\Http\Curl; use Guzzle\Common\Collection; use Guzzle\Common\Event; use Guzzle\Http\EntityBody; use Guzzle\Http\QueryString; use Guzzle\Http\Client; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Message\RequestInterface; use Guzzle\Http\Curl\CurlHandle; /** * @group server * @covers Guzzle\Http\Curl\CurlHandle */ class CurlHandleTest extends \Guzzle\Tests\GuzzleTestCase { public $requestHandle; protected function updateForHandle(RequestInterface $request) { $that = $this; $request->getEventDispatcher()->addListener('request.sent', function (Event $e) use ($that) { $that->requestHandle = $e['handle']; }); return $request; } public function setUp() { $this->requestHandle = null; } /** * @expectedException \InvalidArgumentException */ public function testConstructorExpectsCurlResource() { $h = new CurlHandle(false, array()); } public function testConstructorExpectsProperOptions() { $h = curl_init($this->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)); } 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)); } 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)); } 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()); } 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)); } 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()); } 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( "couldn't connect to host", 'timeout was reached', 'connection time-out', 'connect() timed out!', 'failed connect to 127.0.0.1:123; connection refused', 'failed to connect to 127.0.0.1 port 123: connection refused' ); $this->assertTrue(in_array(strtolower($h->getError()), $errors), $h->getError() . ' was not the error'); $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')); } public function testGetInfoWithoutDebugMode() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $client = new Client($this->getServer()->getUrl()); $request = $client->get($this->getServer()->getUrl()); $response = $request->send(); $info = $response->getInfo(); $this->assertFalse(empty($info)); $this->assertEquals($this->getServer()->getUrl(), $info['url']); } 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' )); $client = new Client(); $userAgent = $client->getDefaultUserAgent(); $auth = base64_encode('michael:123'); $testFileSize = filesize($testFile); $tests = array( // Send a regular GET array('GET', 'http://www.google.com/', null, null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_HTTPHEADER => array('Accept:', '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_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_PORT => 8080, CURLOPT_HTTPHEADER => array('Accept:', '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_CONNECTTIMEOUT => 150, CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), CURLOPT_NOBODY => 1 )), // Send a GET using basic auth array('GET', 'https://michael:123@127.0.0.1/index.html?q=2', null, null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_HTTPHEADER => array( 'Accept:', 'Host: 127.0.0.1', 'Authorization: Basic ' . $auth, 'User-Agent: ' . $userAgent ), CURLOPT_PORT => 443 )), // Send a GET request with custom headers array('GET', 'http://127.0.0.1:8124/', array( 'x-test-data' => 'Guzzle' ), null, array( CURLOPT_PORT => 8124, CURLOPT_HTTPHEADER => array( 'Accept:', 'Host: 127.0.0.1:8124', 'x-test-data: Guzzle', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', 'x-test-data' => 'Guzzle' )), // Send a POST using a query string array('POST', 'http://127.0.0.1:8124/post.php', null, $qs, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_POSTFIELDS => 'x=y&z=a', CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Length' => '7', '!Expect' => null, 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', '!Transfer-Encoding' => null )), // Send a PUT using raw data array('PUT', 'http://127.0.0.1:8124/put.php', null, EntityBody::factory(fopen($testFile, 'r+')), array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_READFUNCTION => 'callback', CURLOPT_INFILESIZE => filesize($testFile), CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', '!Expect' => null, 'Content-Length' => $testFileSize, '!Transfer-Encoding' => null )), // Send a POST request using an array of fields array('POST', 'http://127.0.0.1:8124/post.php', null, array( 'x' => 'y', 'a' => 'b' ), array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_POST => 1, CURLOPT_POSTFIELDS => 'x=y&a=b', CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'Content-Type: application/x-www-form-urlencoded; charset=utf-8', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Length' => '7', '!Expect' => null, 'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8', '!Transfer-Encoding' => null )), // Send a POST request with raw POST data and a custom content-type array('POST', 'http://127.0.0.1:8124/post.php', array( 'Content-Type' => 'application/json' ), '{"hi":"there"}', array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_UPLOAD => true, CURLOPT_INFILESIZE => 14, CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'Content-Type: application/json', 'User-Agent: ' . $userAgent ), ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Type' => 'application/json', '!Expect' => null, '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://127.0.0.1:8124/post.php', array( 'Content-Type' => 'application/json', 'Transfer-Encoding' => 'chunked' ), '{"hi":"there"}', array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_UPLOAD => true, CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'Transfer-Encoding: chunked', 'Content-Type: application/json', 'User-Agent: ' . $userAgent ), ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Type' => 'application/json', '!Expect' => null, 'Transfer-Encoding' => 'chunked', '!Content-Length' => '' )), // Send a POST request with no body array('POST', 'http://127.0.0.1:8124/post.php', null, '', array( CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Length' => '0', '!Transfer-Encoding' => null )), // Send a POST request with empty post fields array('POST', 'http://127.0.0.1:8124/post.php', null, array(), array( CURLOPT_CUSTOMREQUEST => 'POST', CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Length' => '0', '!Transfer-Encoding' => null )), // Send a PATCH request array('PATCH', 'http://127.0.0.1:8124/patch.php', null, 'body', array( CURLOPT_INFILESIZE => 4, CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'User-Agent: ' . $userAgent ) )), // Send a DELETE request with a body array('DELETE', 'http://127.0.0.1:8124/delete.php', null, 'body', array( CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_INFILESIZE => 4, CURLOPT_HTTPHEADER => array ( 'Expect:', 'Accept:', 'Host: 127.0.0.1:8124', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Length' => '4', '!Expect' => null, '!Transfer-Encoding' => null )), /** * Send a request with empty path and a fragment - the fragment must be * stripped out before sending it to curl * * @issue 453 * @link https://github.com/guzzle/guzzle/issues/453 */ array('GET', 'http://www.google.com#head', null, null, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_HTTPHEADER => array('Accept:', 'Host: www.google.com', 'User-Agent: ' . $userAgent), )), ); $postTest = array('POST', 'http://127.0.0.1:8124/post.php', null, $postBody, array( CURLOPT_RETURNTRANSFER => 0, CURLOPT_HEADER => 0, CURLOPT_CONNECTTIMEOUT => 150, CURLOPT_WRITEFUNCTION => 'callback', CURLOPT_HEADERFUNCTION => 'callback', CURLOPT_POST => 1, CURLOPT_POSTFIELDS => array( 'file' => '@' . $testFile . ';filename=phpunit.xml.dist;type=application/octet-stream' ), CURLOPT_HTTPHEADER => array ( 'Accept:', 'Host: 127.0.0.1:8124', 'Content-Type: multipart/form-data', 'Expect: 100-Continue', 'User-Agent: ' . $userAgent ) ), array( 'Host' => '*', 'User-Agent' => '*', 'Content-Length' => '*', 'Expect' => '100-Continue', 'Content-Type' => 'multipart/form-data; boundary=*', '!Transfer-Encoding' => null )); if (version_compare(phpversion(), '5.5.0', '>=')) { $postTest[4][CURLOPT_POSTFIELDS] = array( 'file' => new \CurlFile($testFile, 'application/octet-stream', 'phpunit.xml.dist') ); } $tests[] = $postTest; return $tests; } /** * @dataProvider dataProvider */ public function testFactoryCreatesCurlBasedOnRequest($method, $url, $headers, $body, $options, $expectedHeaders = null) { $client = new Client(); $request = $client->createRequest($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 = $handle->getOptions()->getAll(); // 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')); } } } public function testFactoryUsesSpecifiedProtocol() { $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:8124/'); $request->setProtocolVersion('1.1'); $handle = CurlHandle::factory($request); $options = $handle->getOptions(); $this->assertEquals(CURL_HTTP_VERSION_1_1, $options[CURLOPT_HTTP_VERSION]); } 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->assertFalse($r[0]->hasHeader('Transfer-Encoding')); $this->assertEquals(4, (string) $r[0]->getHeader('Content-Length')); $sent = strtolower($r[0]); $this->assertContains('put / http/1.1', $sent); $this->assertContains('host: 127.0.0.1', $sent); $this->assertContains('user-agent:', $sent); $this->assertContains('content-type: text/plain', $sent); } 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')); } 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('/', array('Transfer-Encoding' => 'chunked'), 'hi!'); $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)); } 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(); // Make sure that the request was sent correctly $r = $this->getServer()->getReceivedRequests(true); $this->assertEquals('a=b&c=ay%21%20~This%20is%20a%20test%2C%20isn%27t%20it%3F', (string) $r[0]->getBody()); $this->assertFalse($r[0]->hasHeader('Transfer-Encoding')); $this->assertEquals(56, (string) $r[0]->getHeader('Content-Length')); $sent = strtolower($r[0]); $this->assertContains('post / http/1.1', $sent); $this->assertContains('content-type: application/x-www-form-urlencoded; charset=utf-8', $sent); } 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), )); $this->updateForHandle($request); $request->send(); // Ensure the CURLOPT_POSTFIELDS option was set properly $options = $this->requestHandle->getOptions()->getAll(); if (version_compare(phpversion(), '5.5.0', '<')) { $this->assertContains('@' . __FILE__ . ';filename=CurlHandleTest.php;type=text/x-', $options[CURLOPT_POSTFIELDS]['foo']); } else{ $this->assertInstanceOf('CURLFile', $options[CURLOPT_POSTFIELDS]['foo']); } $this->assertEquals('baz', $options[CURLOPT_POSTFIELDS]['bar']); $this->assertEquals('1', $options[CURLOPT_POSTFIELDS]['arr[a]']); $this->assertEquals('2', $options[CURLOPT_POSTFIELDS]['arr[b]']); // Ensure that a Content-Length header was sent by cURL $this->assertTrue($request->hasHeader('Content-Length')); } public function testCurlConfigurationOptionsAreSet() { $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); $request->setClient(new Client('http://www.example.com')); $request->getCurlOptions()->set(CURLOPT_CONNECTTIMEOUT, 99); $request->getCurlOptions()->set('curl.fake_opt', 99); $request->getCurlOptions()->set(CURLOPT_PORT, 8181); $handle = CurlHandle::factory($request); $this->assertEquals(99, $handle->getOptions()->get(CURLOPT_CONNECTTIMEOUT)); $this->assertEquals(8181, $handle->getOptions()->get(CURLOPT_PORT)); $this->assertNull($handle->getOptions()->get('curl.fake_opt')); $this->assertNull($handle->getOptions()->get('fake_opt')); } public function testEnsuresRequestsHaveResponsesWhenUpdatingFromTransfer() { $request = RequestFactory::getInstance()->create('PUT', $this->getServer()->getUrl()); $handle = CurlHandle::factory($request); $handle->updateRequestFromTransfer($request); } public function testCanSendBodyAsString() { $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()); $request = $client->put('/', null, 'foo'); $request->getCurlOptions()->set('body_as_string', true); $request->send(); $requests = $this->getServer()->getReceivedRequests(false); $this->assertContains('PUT /', $requests[0]); $this->assertContains("\nfoo", $requests[0]); $this->assertContains('content-length: 3', $requests[0]); $this->assertNotContains('content-type', $requests[0]); } public function testCanSendPostBodyAsString() { $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()); $request = $client->post('/', null, 'foo'); $request->getCurlOptions()->set('body_as_string', true); $request->send(); $requests = $this->getServer()->getReceivedRequests(false); $this->assertContains('POST /', $requests[0]); $this->assertContains("\nfoo", $requests[0]); $this->assertContains('content-length: 3', $requests[0]); $this->assertNotContains('content-type', $requests[0]); } 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-', $body); $this->assertContains('Content-Disposition: form-data; name="foo[1]"; filename="', $body); } public function requestMethodProvider() { return array(array('POST'), array('PUT'), array('PATCH')); } /** * @dataProvider requestMethodProvider */ public function testSendsRequestsWithNoBodyUsingContentLengthZero($method) { $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()); $client->createRequest($method)->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertFalse($requests[0]->hasHeader('Transfer-Encoding')); $this->assertTrue($requests[0]->hasHeader('Content-Length')); $this->assertEquals('0', (string) $requests[0]->getHeader('Content-Length')); } /** * @dataProvider provideCurlConfig */ public function testParseCurlConfigConvertsStringKeysToConstantKeys($options, $expected) { $actual = CurlHandle::parseCurlConfig($options); $this->assertEquals($expected, $actual); } /** * Data provider for curl configurations * * @return array */ public function provideCurlConfig() { return array( // Conversion of option name to constant value array( array( 'CURLOPT_PORT' => 10, 'CURLOPT_TIMEOUT' => 99 ), array( CURLOPT_PORT => 10, CURLOPT_TIMEOUT => 99 ) ), // Keeps non constant options array( array('debug' => true), array('debug' => true) ), // Conversion of constant names to constant values array( array('debug' => 'CURLPROXY_HTTP'), array('debug' => CURLPROXY_HTTP) ) ); } public function testSeeksToBeginningOfStreamWhenSending() { $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: 0\r\n\r\n" )); $client = new Client($this->getServer()->getUrl()); $request = $client->put('/', null, 'test'); $request->send(); $request->send(); $received = $this->getServer()->getReceivedRequests(true); $this->assertEquals(2, count($received)); $this->assertEquals('test', (string) $received[0]->getBody()); $this->assertEquals('test', (string) $received[1]->getBody()); } public function testAllowsCurloptEncodingToBeSet() { $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()); $request = $client->get('/', null); $request->getCurlOptions()->set(CURLOPT_ENCODING, ''); $this->updateForHandle($request); $request->send(); $options = $this->requestHandle->getOptions()->getAll(); $this->assertSame('', $options[CURLOPT_ENCODING]); $received = $this->getServer()->getReceivedRequests(false); $this->assertContainsIns('accept: */*', $received[0]); $this->assertContainsIns('accept-encoding: ', $received[0]); } public function testSendsExpectHeaderWhenSizeIsGreaterThanCutoff() { $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()); $request = $client->put('/', null, 'test'); // Start sending the expect header to 2 bytes $this->updateForHandle($request); $request->setExpectHeaderCutoff(2)->send(); $options = $this->requestHandle->getOptions()->getAll(); $this->assertContains('Expect: 100-Continue', $options[CURLOPT_HTTPHEADER]); $received = $this->getServer()->getReceivedRequests(false); $this->assertContainsIns('expect: 100-continue', $received[0]); } public function testSetsCurloptEncodingWhenAcceptEncodingHeaderIsSet() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ndata"); $client = new Client($this->getServer()->getUrl()); $request = $client->get('/', array( 'Accept' => 'application/json', 'Accept-Encoding' => 'gzip, deflate', )); $this->updateForHandle($request); $request->send(); $options = $this->requestHandle->getOptions()->getAll(); $this->assertSame('gzip, deflate', $options[CURLOPT_ENCODING]); $received = $this->getServer()->getReceivedRequests(false); $this->assertContainsIns('accept: application/json', $received[0]); $this->assertContainsIns('accept-encoding: gzip, deflate', $received[0]); } public function testSendsPostFieldsForNonPostRequests() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); $client = new Client(); $request = $client->put($this->getServer()->getUrl(), null, array( 'foo' => 'baz', 'baz' => 'bar' )); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('PUT', $requests[0]->getMethod()); $this->assertEquals( 'application/x-www-form-urlencoded; charset=utf-8', (string) $requests[0]->getHeader('Content-Type') ); $this->assertEquals(15, (string) $requests[0]->getHeader('Content-Length')); $this->assertEquals('foo=baz&baz=bar', (string) $requests[0]->getBody()); } public function testSendsPostFilesForNonPostRequests() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\n\r\nContent-Length: 0\r\n\r\n"); $client = new Client(); $request = $client->put($this->getServer()->getUrl(), null, array( 'foo' => '@' . __FILE__ )); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('PUT', $requests[0]->getMethod()); $this->assertContains('multipart/form-data', (string) $requests[0]->getHeader('Content-Type')); $this->assertContains('testSendsPostFilesForNonPostRequests', (string) $requests[0]->getBody()); } } guzzle/tests/Guzzle/Tests/Http/Curl/CurlMultiTest.php 0000604 00000042227 15173213567 0016742 0 ustar 00 <?php namespace Guzzle\Tests\Http\Curl; use Guzzle\Common\Event; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Http\Exception\MultiTransferException; use Guzzle\Http\Client; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Curl\CurlMulti; use Guzzle\Http\Exception\CurlException; use Guzzle\Tests\Mock\MockMulti; /** * @group server * @covers Guzzle\Http\Curl\CurlMulti */ class CurlMultiTest extends \Guzzle\Tests\GuzzleTestCase { /** @var \Guzzle\Http\Curl\CurlMulti */ private $multi; /** * Prepares the environment before running a test. */ protected function setUp() { parent::setUp(); $this->multi = new MockMulti(); } public function tearDown() { unset($this->multi); } public function testConstructorCreateMultiHandle() { $this->assertInternalType('resource', $this->multi->getHandle()); $this->assertEquals('curl_multi', get_resource_type($this->multi->getHandle())); } public function testDestructorClosesMultiHandle() { $handle = $this->multi->getHandle(); $this->multi->__destruct(); $this->assertFalse(is_resource($handle)); } public function testRequestsCanBeAddedAndCounted() { $multi = new CurlMulti(); $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)); } 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->assertTrue($this->multi->remove($request1)); $this->assertFalse($this->multi->remove($request1)); $this->assertEquals(array($request2), $this->multi->all()); } public function testsResetRemovesRequestsAndResetsState() { $this->multi->add(new Request('GET', 'http://www.google.com/')); $this->multi->reset(); $this->assertEquals(array(), $this->multi->all()); } 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()); $request2 = new Request('GET', $this->getServer()->getUrl()); $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); } 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('MultiTransferException not thrown when aggregating request exceptions'); } catch (MultiTransferException $e) { $this->assertTrue($e->containsRequest($request1)); $this->assertTrue($e->containsRequest($request2)); $this->assertTrue($e->containsRequest($request3)); $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()); } $this->assertEquals(1, count($e->getFailedRequests())); $this->assertEquals(2, count($e->getSuccessfulRequests())); $this->assertEquals(3, count($e->getAllRequests())); } } 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->assertInternalType('array', $e->getCurlInfo()); } } public function testRemovesQueuedRequests() { $request = RequestFactory::getInstance()->create('GET', 'http://127.0.0.1:9876/'); $r = new Response(200); $request->setClient(new Client()); $request->setResponse($r, true); $this->multi->add($request); $this->multi->send(); $this->assertSame($r, $request->getResponse()); } 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))); } public function testCatchesExceptionsBeforeSendingSingleRequest() { $client = new Client($this->getServer()->getUrl()); $multi = new CurlMulti(); $client->setCurlMulti($multi); $request = $client->get(); $request->getEventDispatcher()->addListener('request.before_send', function() { throw new \RuntimeException('Testing!'); }); try { $request->send(); $this->fail('Did not throw'); } catch (\RuntimeException $e) { // Ensure it was removed $this->assertEquals(0, count($multi)); } } /** * @expectedException \Guzzle\Common\Exception\ExceptionCollection * @expectedExceptionMessage Thrown before sending! */ public function testCatchesExceptionsBeforeSendingMultipleRequests() { $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)); } 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(); }); try { $r->send(); $this->fail('Did not throw'); } catch (BadResponseException $e) { $this->assertCount(0, $client->getCurlMulti()); } } 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(); }); try { $r->send(); $this->fail('Did not throw'); } catch (BadResponseException $e) { $this->assertCount(0, $client->getCurlMulti()); } } /** * @expectedException \RuntimeException * @expectedExceptionMessage test */ public function testDoesNotCatchRandomExceptionsThrownDuringPerform() { $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 \RuntimeException('test'))); $multi->add($client->get()); $multi->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://127.0.0.1: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']); } }); try { $multi->send(); $this->fail('Did not throw an exception at all!?!'); } catch (\Exception $e) { $this->assertEquals(1, $request->getParams()->get('retries')); } } 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))); } public function testDoesNotThrowExceptionsWhenRequestsRecoverWithSuccess() { // Attempt a port that 99.9% is not listening $client = new Client('http://127.0.0.1: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()); } 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" )); $stream = fopen('php://temp', 'w+'); $client = new Client($this->getServer()->getUrl()); $client->getConfig()->set('curl.CURLOPT_VERBOSE', true)->set('curl.CURLOPT_STDERR', $stream); $request = $client->get(); $multi = new CurlMulti(); $multi->add($request); $multi->send(); $multi->reset(true); $multi->add($request); $multi->send(); rewind($stream); $this->assertNotContains('Re-using existing connection', stream_get_contents($stream)); } 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()); } } public function testRequestBeforeSendIncludesContentLengthHeaderIfEmptyBody() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $request = new Request('PUT', $this->getServer()->getUrl()); $that = $this; $request->getEventDispatcher()->addListener('request.before_send', function ($event) use ($that) { $that->assertEquals(0, $event['request']->getHeader('Content-Length')); }); $this->multi->add($request); $this->multi->send(); } public function testRemovesConflictingTransferEncodingHeader() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" )); $client = new Client($this->getServer()->getUrl()); $request = $client->put('/', null, fopen($this->getServer()->getUrl(), 'r')); $request->setHeader('Content-Length', 4); $request->send(); $received = $this->getServer()->getReceivedRequests(true); $this->assertFalse($received[1]->hasHeader('Transfer-Encoding')); $this->assertEquals(4, (string) $received[1]->getHeader('Content-Length')); } } guzzle/tests/Guzzle/Tests/Http/MimetypesTest.php 0000604 00000001420 15173213567 0016057 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\Mimetypes; /** * @covers Guzzle\Http\Mimetypes */ class MimetypesTest extends \Guzzle\Tests\GuzzleTestCase { public function testGetsFromExtension() { $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromExtension('php')); } public function testGetsFromFilename() { $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(__FILE__)); } public function testGetsFromCaseInsensitiveFilename() { $this->assertEquals('text/x-php', Mimetypes::getInstance()->fromFilename(strtoupper(__FILE__))); } public function testReturnsNullWhenNoMatchFound() { $this->assertNull(Mimetypes::getInstance()->fromExtension('foobar')); } } guzzle/tests/Guzzle/Tests/Http/ClientTest.php 0000604 00000054661 15173213567 0015340 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Common\Collection; use Guzzle\Log\ClosureLogAdapter; use Guzzle\Parser\UriTemplate\UriTemplate; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Log\LogPlugin; use Guzzle\Plugin\Mock\MockPlugin; use Guzzle\Http\Curl\CurlMulti; use Guzzle\Http\Client; use Guzzle\Common\Version; /** * @group server * @covers Guzzle\Http\Client */ class ClientTest extends \Guzzle\Tests\GuzzleTestCase { /** * @return LogPlugin */ private function getLogPlugin() { return new LogPlugin(new ClosureLogAdapter( function($message, $priority, $extras = null) { echo $message . ' ' . $priority . ' ' . implode(' - ', (array) $extras) . "\n"; } )); } public function testAcceptsConfig() { $client = new Client('http://www.google.com/'); $this->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) { } } public function testDescribesEvents() { $this->assertEquals(array('client.create_request'), Client::getAllEvents()); } public function testConstructorCanAcceptConfig() { $client = new Client('http://www.test.com/', array( 'data' => '123' )); $this->assertEquals('123', $client->getConfig('data')); } 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')); } public function testExpandsUriTemplatesUsingConfig() { $client = new Client('http://www.google.com/'); $client->setConfig(array('api' => 'v1', 'key' => 'value', 'foo' => 'bar')); $ref = new \ReflectionMethod($client, 'expandTemplate'); $ref->setAccessible(true); $this->assertEquals('Testing...api/v1/key/value', $ref->invoke($client, 'Testing...api/{api}/key/{key}')); } 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)); } 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()); } public function testClientAddsCurlOptionsToRequests() { $client = new Client('http://www.test.com/', array( 'api' => 'v1', // Adds the option using the curl values 'curl.options' => array( 'CURLOPT_HTTPAUTH' => 'CURLAUTH_DIGEST', 'abc' => 'foo', 'blacklist' => 'abc', 'debug' => true ) )); $request = $client->createRequest(); $options = $request->getCurlOptions(); $this->assertEquals(CURLAUTH_DIGEST, $options->get(CURLOPT_HTTPAUTH)); $this->assertEquals('foo', $options->get('abc')); $this->assertEquals('abc', $options->get('blacklist')); } public function testClientAllowsFineGrainedSslControlButIsSecureByDefault() { $client = new Client('https://www.secure.com/'); // secure by default $request = $client->createRequest(); $options = $request->getCurlOptions(); $this->assertTrue($options->get(CURLOPT_SSL_VERIFYPEER)); // set a capath if you prefer $client = new Client('https://www.secure.com/'); $client->setSslVerification(__DIR__); $request = $client->createRequest(); $options = $request->getCurlOptions(); $this->assertSame(__DIR__, $options->get(CURLOPT_CAPATH)); } public function testConfigSettingsControlSslConfiguration() { // Use the default ca certs on the system $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => 'system')); $this->assertNull($client->getConfig('curl.options')); // Can set the cacert value as well $client = new Client('https://www.secure.com/', array('ssl.certificate_authority' => false)); $options = $client->getConfig('curl.options'); $this->assertArrayNotHasKey(CURLOPT_CAINFO, $options); $this->assertSame(false, $options[CURLOPT_SSL_VERIFYPEER]); $this->assertSame(0, $options[CURLOPT_SSL_VERIFYHOST]); } public function testClientAllowsUnsafeOperationIfRequested() { // be really unsafe if you insist $client = new Client('https://www.secure.com/', array( 'api' => 'v1' )); $client->setSslVerification(false); $request = $client->createRequest(); $options = $request->getCurlOptions(); $this->assertFalse($options->get(CURLOPT_SSL_VERIFYPEER)); $this->assertNull($options->get(CURLOPT_CAINFO)); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException */ public function testThrowsExceptionForInvalidCertificate() { $client = new Client('https://www.secure.com/'); $client->setSslVerification('/path/to/missing/file'); } public function testClientAllowsSettingSpecificSslCaInfo() { // set a file other than the provided cacert.pem $client = new Client('https://www.secure.com/', array( 'api' => 'v1' )); $client->setSslVerification(__FILE__); $request = $client->createRequest(); $options = $request->getCurlOptions(); $this->assertSame(__FILE__, $options->get(CURLOPT_CAINFO)); } /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testClientPreventsInadvertentInsecureVerifyHostSetting() { // set a file other than the provided cacert.pem $client = new Client('https://www.secure.com/', array( 'api' => 'v1' )); $client->setSslVerification(__FILE__, true, true); } /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testClientPreventsInvalidVerifyPeerSetting() { // set a file other than the provided cacert.pem $client = new Client('https://www.secure.com/', array( 'api' => 'v1' )); $client->setSslVerification(__FILE__, 'yes'); } public function testClientAddsParamsToRequests() { Version::$emitWarnings = false; $client = new Client('http://www.example.com', array( 'api' => 'v1', 'request.params' => array( 'foo' => 'bar', 'baz' => 'jar' ) )); $request = $client->createRequest(); $this->assertEquals('bar', $request->getParams()->get('foo')); $this->assertEquals('jar', $request->getParams()->get('baz')); Version::$emitWarnings = true; } 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&z=1'), 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?another=query&z=1') ); } /** * @dataProvider urlProvider */ public function testBuildsRelativeUrls($baseUrl, $url, $result) { $client = new Client($baseUrl); $this->assertEquals($result, $client->get($url)->getUrl()); } 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()); } 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'); } 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()); } 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()); } 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(); } public function testAllowsCustomCurlMultiObjects() { $mock = $this->getMock('Guzzle\\Http\\Curl\\CurlMulti', array('add', 'send')); $mock->expects($this->once()) ->method('add') ->will($this->returnSelf()); $mock->expects($this->once()) ->method('send') ->will($this->returnSelf()); $client = new Client(); $client->setCurlMulti($mock); $request = $client->get(); $request->setResponse(new Response(200), true); $client->send($request); } 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)); } 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())); } /** * @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()); } /** * @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())); } 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')); } 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')); } public function testAllowsUriTemplateInjection() { $client = new Client('http://test.com'); $ref = new \ReflectionMethod($client, 'getUriTemplate'); $ref->setAccessible(true); $a = $ref->invoke($client); $this->assertSame($a, $ref->invoke($client)); $client->setUriTemplate(new UriTemplate()); $this->assertNotSame($a, $ref->invoke($client)); } public function testAllowsCustomVariablesWhenExpandingTemplates() { $client = new Client('http://test.com', array('test' => 'hi')); $ref = new \ReflectionMethod($client, 'expandTemplate'); $ref->setAccessible(true); $uri = $ref->invoke($client, 'http://{test}{?query*}', array('query' => array('han' => 'solo'))); $this->assertEquals('http://hi?han=solo', $uri); } 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()); } public function testAllowsDefaultHeaders() { Version::$emitWarnings = false; $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')); Version::$emitWarnings = true; } public function testDontReuseCurlMulti() { $client1 = new Client(); $client2 = new Client(); $this->assertNotSame($client1->getCurlMulti(), $client2->getCurlMulti()); } public function testGetDefaultUserAgent() { $client = new Client(); $agent = $this->readAttribute($client, 'userAgent'); $version = curl_version(); $testAgent = sprintf('Guzzle/%s curl/%s PHP/%s', Version::VERSION, $version['version'], PHP_VERSION); $this->assertEquals($agent, $testAgent); $client->setUserAgent('foo'); $this->assertEquals('foo', $this->readAttribute($client, 'userAgent')); } public function testOverwritesUserAgent() { $client = new Client(); $request = $client->createRequest('GET', 'http://www.foo.com', array('User-agent' => 'foo')); $this->assertEquals('foo', (string) $request->getHeader('User-Agent')); } public function testUsesDefaultUserAgent() { $client = new Client(); $request = $client->createRequest('GET', 'http://www.foo.com'); $this->assertContains('Guzzle/', (string) $request->getHeader('User-Agent')); } public function testCanSetDefaultRequestOptions() { $client = new Client(); $client->getConfig()->set('request.options', array( 'query' => array('test' => '123', 'other' => 'abc'), 'headers' => array('Foo' => 'Bar', 'Baz' => 'Bam') )); $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test')); // Explicit options on a request should overrule default options $this->assertEquals('Test', (string) $request->getHeader('Foo')); $this->assertEquals('hello', $request->getQuery()->get('test')); // Default options should still be set $this->assertEquals('abc', $request->getQuery()->get('other')); $this->assertEquals('Bam', (string) $request->getHeader('Baz')); } public function testCanSetSetOptionsOnRequests() { $client = new Client(); $request = $client->createRequest('GET', 'http://www.foo.com?test=hello', array('Foo' => 'Test'), null, array( 'cookies' => array('michael' => 'test') )); $this->assertEquals('test', $request->getCookie('michael')); } public function testHasDefaultOptionsHelperMethods() { $client = new Client(); // With path $client->setDefaultOption('headers/foo', 'bar'); $this->assertEquals('bar', $client->getDefaultOption('headers/foo')); // With simple key $client->setDefaultOption('allow_redirects', false); $this->assertFalse($client->getDefaultOption('allow_redirects')); $this->assertEquals(array( 'headers' => array('foo' => 'bar'), 'allow_redirects' => false ), $client->getConfig('request.options')); $request = $client->get('/'); $this->assertEquals('bar', $request->getHeader('foo')); } public function testHeadCanUseOptions() { $client = new Client(); $head = $client->head('http://www.foo.com', array(), array('query' => array('foo' => 'bar'))); $this->assertEquals('bar', $head->getQuery()->get('foo')); } public function testCanSetRelativeUrlStartingWithHttp() { $client = new Client('http://www.foo.com'); $this->assertEquals( 'http://www.foo.com/httpfoo', $client->createRequest('GET', 'httpfoo')->getUrl() ); } } guzzle/tests/Guzzle/Tests/Http/RedirectPluginTest.php 0000604 00000027754 15173213567 0017045 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Redirect; use Guzzle\Http\Client; use Guzzle\Http\EntityBody; use Guzzle\Http\RedirectPlugin; use Guzzle\Http\Exception\TooManyRedirectsException; use Guzzle\Plugin\History\HistoryPlugin; /** * @covers Guzzle\Http\RedirectPlugin */ class RedirectPluginTest extends \Guzzle\Tests\GuzzleTestCase { public function testRedirectsRequests() { // Flush the server and queue up a redirect followed by a successful response $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n", )); // Create a client that uses the default redirect behavior $client = new Client($this->getServer()->getUrl()); $history = new HistoryPlugin(); $client->addSubscriber($history); $request = $client->get('/foo'); $response = $request->send(); $this->assertEquals(200, $response->getStatusCode()); $this->assertContains('/redirect2', $response->getEffectiveUrl()); // Ensure that two requests were sent $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('/foo', $requests[0]->getResource()); $this->assertEquals('GET', $requests[0]->getMethod()); $this->assertEquals('/redirect1', $requests[1]->getResource()); $this->assertEquals('GET', $requests[1]->getMethod()); $this->assertEquals('/redirect2', $requests[2]->getResource()); $this->assertEquals('GET', $requests[2]->getMethod()); // Ensure that the redirect count was incremented $this->assertEquals(2, $request->getParams()->get(RedirectPlugin::REDIRECT_COUNT)); $this->assertCount(3, $history); $requestHistory = $history->getAll(); $this->assertEquals(301, $requestHistory[0]['response']->getStatusCode()); $this->assertEquals('/redirect1', (string) $requestHistory[0]['response']->getHeader('Location')); $this->assertEquals(301, $requestHistory[1]['response']->getStatusCode()); $this->assertEquals('/redirect2', (string) $requestHistory[1]['response']->getHeader('Location')); $this->assertEquals(200, $requestHistory[2]['response']->getStatusCode()); } public function testCanLimitNumberOfRedirects() { // Flush the server and queue up a redirect followed by a successful response $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect3\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect4\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect5\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect6\r\nContent-Length: 0\r\n\r\n" )); try { $client = new Client($this->getServer()->getUrl()); $client->get('/foo')->send(); $this->fail('Did not throw expected exception'); } catch (TooManyRedirectsException $e) { $this->assertContains( "5 redirects were issued for this request:\nGET /foo HTTP/1.1\r\n", $e->getMessage() ); } } public function testDefaultBehaviorIsToRedirectWithGetForEntityEnclosingRequests() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\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()); $client->post('/foo', array('X-Baz' => 'bar'), 'testing')->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('POST', $requests[0]->getMethod()); $this->assertEquals('GET', $requests[1]->getMethod()); $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); $this->assertEquals('GET', $requests[2]->getMethod()); } public function testCanRedirectWithStrictRfcCompliance() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\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()); $request = $client->post('/foo', array('X-Baz' => 'bar'), 'testing'); $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('POST', $requests[0]->getMethod()); $this->assertEquals('POST', $requests[1]->getMethod()); $this->assertEquals('bar', (string) $requests[1]->getHeader('X-Baz')); $this->assertEquals('POST', $requests[2]->getMethod()); } public function testRedirect303WithGet() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\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()); $request = $client->post('/foo'); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('POST', $requests[0]->getMethod()); $this->assertEquals('GET', $requests[1]->getMethod()); } public function testRedirect303WithGetWithStrictRfcCompliance() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 303 Moved Permanently\r\nLocation: /redirect\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()); $request = $client->post('/foo'); $request->getParams()->set(RedirectPlugin::STRICT_REDIRECTS, true); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('POST', $requests[0]->getMethod()); $this->assertEquals('GET', $requests[1]->getMethod()); } public function testRewindsStreamWhenRedirectingIfNeeded() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\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()); $request = $client->put(); $request->configureRedirects(true); $body = EntityBody::factory('foo'); $body->read(1); $request->setBody($body); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('foo', (string) $requests[0]->getBody()); } /** * @expectedException \Guzzle\Http\Exception\CouldNotRewindStreamException */ public function testThrowsExceptionWhenStreamCannotBeRewound() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect\r\nContent-Length: 0\r\n\r\n" )); $client = new Client($this->getServer()->getUrl()); $request = $client->put(); $request->configureRedirects(true); $body = EntityBody::factory(fopen($this->getServer()->getUrl(), 'r')); $body->read(1); $request->setBody($body)->send(); } public function testRedirectsCanBeDisabledPerRequest() { $this->getServer()->flush(); $this->getServer()->enqueue(array("HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n")); $client = new Client($this->getServer()->getUrl()); $request = $client->put(); $request->configureRedirects(false, 0); $this->assertEquals(301, $request->send()->getStatusCode()); } public function testCanRedirectWithNoLeadingSlashAndQuery() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect?foo=bar\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()); $request = $client->get('?foo=bar'); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $requests[0]->getUrl()); $this->assertEquals($this->getServer()->getUrl() . 'redirect?foo=bar', $requests[1]->getUrl()); // Ensure that the history on the actual request is correct $this->assertEquals($this->getServer()->getUrl() . '?foo=bar', $request->getUrl()); } public function testRedirectWithStrictRfc386Compliance() { // Flush the server and queue up a redirect followed by a successful response $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: redirect\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()); $request = $client->get('/foo'); $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('/redirect', $requests[1]->getResource()); } public function testResetsHistoryEachSend() { // Flush the server and queue up a redirect followed by a successful response $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect1\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect2\r\nContent-Length: 0\r\n\r\n", "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" )); // Create a client that uses the default redirect behavior $client = new Client($this->getServer()->getUrl()); $history = new HistoryPlugin(); $client->addSubscriber($history); $request = $client->get('/foo'); $response = $request->send(); $this->assertEquals(3, count($history)); $this->assertTrue($request->getParams()->hasKey('redirect.count')); $this->assertContains('/redirect2', $response->getEffectiveUrl()); $request->send(); $this->assertFalse($request->getParams()->hasKey('redirect.count')); } public function testHandlesRedirectsWithSpacesProperly() { // Flush the server and queue up a redirect followed by a successful response $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 301 Moved Permanently\r\nLocation: /redirect 1\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()); $request = $client->get('/foo'); $request->send(); $reqs = $this->getServer()->getReceivedRequests(true); $this->assertEquals('/redirect%201', $reqs[1]->getResource()); } } guzzle/tests/Guzzle/Tests/Http/Message/HeaderTest.php 0000604 00000011565 15173213567 0016672 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message; use Guzzle\Http\Message\Header; use Guzzle\Http\Message\Response; /** * @covers Guzzle\Http\Message\Header */ class HeaderTest extends \Guzzle\Tests\GuzzleTestCase { protected $test = array( 'zoo' => array('foo', 'Foo'), 'Zoo' => 'bar', ); 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 testNormalizesGluedHeaders() { $h = new Header('Zoo', array('foo, Faz', 'bar')); $result = $h->normalize(true)->toArray(); natsort($result); $this->assertEquals(array('bar', 'foo', 'Faz'), $result); } 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')); } 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); // Allows empty string $h = new Header('Foo', ''); $this->assertEquals('', (string) $h); $this->assertEquals(1, count($h)); $this->assertEquals(1, count($h->normalize()->toArray())); } 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')); } public function testAllowsArrayInConstructor() { $h = new Header('Foo', array('Testing', '123', 'Foo=baz')); $this->assertEquals(array('Testing', '123', 'Foo=baz'), $h->toArray()); } public function parseParamsProvider() { $res1 = array( array( '<http:/.../front.jpeg>' => '', 'rel' => 'front', 'type' => 'image/jpeg', ), array( '<http://.../back.jpeg>' => '', 'rel' => 'back', 'type' => 'image/jpeg', ), ); return array( array( '<http:/.../front.jpeg>; rel="front"; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg"', $res1 ), array( '<http:/.../front.jpeg>; rel="front"; type="image/jpeg",<http://.../back.jpeg>; rel=back; type="image/jpeg"', $res1 ), array( 'foo="baz"; bar=123, boo, test="123", foobar="foo;bar"', array( array('foo' => 'baz', 'bar' => '123'), array('boo' => ''), array('test' => '123'), array('foobar' => 'foo;bar') ) ), array( '<http://.../side.jpeg?test=1>; rel="side"; type="image/jpeg",<http://.../side.jpeg?test=2>; rel=side; type="image/jpeg"', array( array('<http://.../side.jpeg?test=1>' => '', 'rel' => 'side', 'type' => 'image/jpeg'), array('<http://.../side.jpeg?test=2>' => '', 'rel' => 'side', 'type' => 'image/jpeg') ) ), array( '', array() ) ); } /** * @dataProvider parseParamsProvider */ public function testParseParams($header, $result) { $response = new Response(200, array('Link' => $header)); $this->assertEquals($result, $response->getHeader('Link')->parseParams()); } } guzzle/tests/Guzzle/Tests/Http/Message/ResponseTest.php 0000604 00000056673 15173213567 0017311 0 ustar 00 <?php namespace Guzzle\Tests\Message; use Guzzle\Common\Collection; use Guzzle\Http\ClientInterface; use Guzzle\Http\EntityBody; use Guzzle\Http\HttpException; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Http\Message\Response; /** * @group server * @covers Guzzle\Http\Message\Response */ class ResponseTest extends \Guzzle\Tests\GuzzleTestCase { /** @var Response The response object to test */ protected $response; public function setup() { $this->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'); } public function tearDown() { unset($this->response); } 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)); $response = new Response(200, null, '0'); $this->assertSame('0', $response->getBody(true), 'getBody(true) should return "0" if response body is "0".'); // 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) { } } 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); } 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, (string) $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: 4\r\n\r\ntest"); $this->assertEquals(4, (string) $response->getContentLength()); $this->assertEquals('test', $response->getBody(true)); } public function testFactoryCanCreateHeadResponses() { $response = Response::fromMessage("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n"); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('OK', $response->getReasonPhrase()); $this->assertEquals(4, (string) $response->getContentLength()); $this->assertEquals('', $response->getBody(true)); } public function testFactoryRequiresMessage() { $this->assertFalse(Response::fromMessage('')); } public function testGetBody() { $body = EntityBody::factory(''); $response = new Response(403, new Collection(), $body); $this->assertEquals($body, $response->getBody()); $response->setBody('foo'); $this->assertEquals('foo', $response->getBody(true)); } public function testManagesStatusCode() { $response = new Response(403); $this->assertEquals(403, $response->getStatusCode()); } 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()); } 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()); } 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()); } public function testIsClientError() { $response = new Response(403); $this->assertTrue($response->isClientError()); $response = new Response(200); $this->assertFalse($response->isClientError()); } 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()); } public function testIsInformational() { $response = new Response(100); $this->assertTrue($response->isInformational()); $response = new Response(200); $this->assertFalse($response->isInformational()); } public function testIsRedirect() { $response = new Response(301); $this->assertTrue($response->isRedirect()); $response = new Response(200); $this->assertFalse($response->isRedirect()); } public function testIsServerError() { $response = new Response(500); $this->assertTrue($response->isServerError()); $response = new Response(400); $this->assertFalse($response->isServerError()); } public function testIsSuccessful() { $response = new Response(200); $this->assertTrue($response->isSuccessful()); $response = new Response(403); $this->assertFalse($response->isSuccessful()); } public function testGetAcceptRanges() { $this->assertEquals('bytes', $this->response->getAcceptRanges()); } public function testCalculatesAge() { $this->assertEquals(12, $this->response->calculateAge()); $this->response->removeHeader('Age'); $this->response->removeHeader('Date'); $this->assertNull($this->response->calculateAge()); $this->response->setHeader('Date', gmdate(ClientInterface::HTTP_DATE, strtotime('-1 minute'))); // If the test runs slowly, still pass with a +5 second allowance $this->assertTrue($this->response->getAge() - 60 <= 5); } public function testGetAllow() { $this->assertEquals('GET, HEAD', $this->response->getAllow()); } public function testGetCacheControl() { $this->assertEquals('no-cache', $this->response->getCacheControl()); } public function testGetContentEncoding() { $this->assertEquals('gzip', $this->response->getContentEncoding()); } public function testGetContentLanguage() { $this->assertEquals('da', $this->response->getContentLanguage()); } public function testGetContentLength() { $this->assertEquals('348', $this->response->getContentLength()); } public function testGetContentLocation() { $this->assertEquals('/index.htm', $this->response->getContentLocation()); } public function testGetContentDisposition() { $this->assertEquals('attachment; filename=fname.ext', $this->response->getContentDisposition()); } public function testGetContentMd5() { $this->assertEquals('Q2hlY2sgSW50ZWdyaXR5IQ==', $this->response->getContentMd5()); } public function testGetContentRange() { $this->assertEquals('bytes 21010-47021/47022', $this->response->getContentRange()); } public function testGetContentType() { $this->assertEquals('text/html; charset=utf-8', $this->response->getContentType()); } public function testGetDate() { $this->assertEquals('Tue, 15 Nov 1994 08:12:31 GMT', $this->response->getDate()); } public function testGetEtag() { $this->assertEquals('737060cd8c284d8af7ad3082f209582d', $this->response->getEtag()); } public function testGetExpires() { $this->assertEquals('Thu, 01 Dec 1994 16:00:00 GMT', $this->response->getExpires()); } public function testGetLastModified() { $this->assertEquals('Tue, 15 Nov 1994 12:45:26 GMT', $this->response->getLastModified()); } public function testGetLocation() { $this->assertEquals('http://www.w3.org/pub/WWW/People.html', $this->response->getLocation()); } public function testGetPragma() { $this->assertEquals('no-cache', $this->response->getPragma()); } public function testGetProxyAuthenticate() { $this->assertEquals('Basic', $this->response->getProxyAuthenticate()); } public function testGetServer() { $this->assertEquals('Apache/1.3.27 (Unix) (Red-Hat/Linux)', $this->response->getServer()); } public function testGetSetCookie() { $this->assertEquals('UserID=JohnDoe; Max-Age=3600; Version=1', $this->response->getSetCookie()); } public function testGetMultipleSetCookie() { $this->response->addHeader('Set-Cookie', 'UserID=Mike; Max-Age=200'); $this->assertEquals(array( 'UserID=JohnDoe; Max-Age=3600; Version=1', 'UserID=Mike; Max-Age=200', ), $this->response->getHeader('Set-Cookie')->toArray()); } 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->getHeader('Set-Cookie')->toArray()); $this->response->addHeaders(array( 'set-cookie' => 'fubu' )); $this->assertEquals( array('UserID=JohnDoe; Max-Age=3600; Version=1', 'foo', 'fubu'), $this->response->getHeader('Set-Cookie')->toArray() ); } public function testGetTrailer() { $this->assertEquals('Max-Forwards', $this->response->getTrailer()); } public function testGetTransferEncoding() { $this->assertEquals('chunked', $this->response->getTransferEncoding()); } public function testGetVary() { $this->assertEquals('*', $this->response->getVary()); } public function testReturnsViaHeader() { $this->assertEquals('1.0 fred, 1.1 nowhere.com (Apache/1.1)', $this->response->getVia()); } public function testGetWarning() { $this->assertEquals('199 Miscellaneous warning', $this->response->getWarning()); } public function testReturnsWwwAuthenticateHeader() { $this->assertEquals('Basic', $this->response->getWwwAuthenticate()); } public function testReturnsConnectionHeader() { $this->assertEquals(null, $this->response->getConnection()); $this->response->setHeader('Connection', 'close'); $this->assertEquals('close', $this->response->getConnection()); } public function testReturnsHeaders() { $this->assertEquals('Basic', $this->response->getHeader('WWW-Authenticate', null, true)); $this->assertEquals('chunked', $this->response->getHeader('Transfer-Encoding', null, false)); } 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); } 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); } 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' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) ))->getMaxAge()); // Uses the Expires date $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) ))->getMaxAge()); // Uses the Expires date $this->assertGreaterThanOrEqual(82400, $this->getResponse(200, array( 'Expires' => gmdate(ClientInterface::HTTP_DATE, strtotime('+1 day')) ))->getMaxAge()); } 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()); } public function testCalculatesFreshness() { $response = new Response(200); $this->assertNull($response->isFresh()); $this->assertNull($response->getFreshness()); $response->setHeader('Cache-Control', 'max-age=120'); $response->setHeader('Age', 100); $this->assertEquals(20, $response->getFreshness()); $this->assertTrue($response->isFresh()); $response->setHeader('Age', 120); $this->assertEquals(0, $response->getFreshness()); $this->assertTrue($response->isFresh()); $response->setHeader('Age', 150); $this->assertEquals(-30, $response->getFreshness()); $this->assertFalse($response->isFresh()); } 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()); } 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')); } 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')); } public function testParsesJsonResponses() { $response = new Response(200, array(), '{"foo": "bar"}'); $this->assertEquals(array('foo' => 'bar'), $response->json()); // Return array when null is a service response $response = new Response(200); $this->assertEquals(array(), $response->json()); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException * @expectedExceptionMessage Unable to parse response body into JSON: 4 */ public function testThrowsExceptionWhenFailsToParseJsonResponse() { $response = new Response(200, array(), '{"foo": "'); $response->json(); } public function testParsesXmlResponses() { $response = new Response(200, array(), '<abc><foo>bar</foo></abc>'); $this->assertEquals('bar', (string) $response->xml()->foo); // Always return a SimpleXMLElement from the xml method $response = new Response(200); $this->assertEmpty((string) $response->xml()->foo); } /** * @expectedException \Guzzle\Common\Exception\RuntimeException * @expectedExceptionMessage Unable to parse response body into XML: String could not be parsed as XML */ public function testThrowsExceptionWhenFailsToParseXmlResponse() { $response = new Response(200, array(), '<abc'); $response->xml(); } public function testResponseIsSerializable() { $response = new Response(200, array('Foo' => 'bar'), 'test'); $r = unserialize(serialize($response)); $this->assertEquals(200, $r->getStatusCode()); $this->assertEquals('bar', (string) $r->getHeader('Foo')); $this->assertEquals('test', (string) $r->getBody()); } public function testPreventsComplexExternalEntities() { $xml = '<?xml version="1.0"?><!DOCTYPE scan[<!ENTITY test SYSTEM "php://filter/read=convert.base64-encode/resource=ResponseTest.php">]><scan>&test;</scan>'; $response = new Response(200, array(), $xml); $oldCwd = getcwd(); chdir(__DIR__); try { $xml = $response->xml(); chdir($oldCwd); $this->markTestIncomplete('Did not throw the expected exception! XML resolved as: ' . $xml->asXML()); } catch (\Exception $e) { chdir($oldCwd); } } } guzzle/tests/Guzzle/Tests/Http/Message/RequestFactoryTest.php 0000604 00000063415 15173213567 0020463 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message; use Guzzle\Common\Collection; use Guzzle\Http\Client; use Guzzle\Http\Message\Response; use Guzzle\Http\Url; use Guzzle\Http\EntityBody; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Message\Request; use Guzzle\Http\QueryString; use Guzzle\Parser\Message\MessageParser; use Guzzle\Plugin\Log\LogPlugin; use Guzzle\Plugin\Mock\MockPlugin; /** * @group server * @covers Guzzle\Http\Message\RequestFactory */ class HttpRequestFactoryTest extends \Guzzle\Tests\GuzzleTestCase { public function testCachesSingletonInstance() { $factory = RequestFactory::getInstance(); $this->assertSame($factory, 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()); } 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()); } 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()); } public function testCreatesOptionsRequests() { $request = RequestFactory::getInstance()->create('OPTIONS', 'http://www.example.com/'); $this->assertEquals('OPTIONS', $request->getMethod()); $this->assertInstanceOf('Guzzle\\Http\\Message\\EntityEnclosingRequest', $request); } 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()); } 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()); $files = $request->getPostFiles(); $this->assertInstanceOf('Guzzle\Http\Message\PostFile', $files['file'][0]); } 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())); } 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("Basic {$auth}", (string) $request->getHeader('Authorization')); $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("Basic {$auth}", (string) $request->getHeader('Authorization')); $this->assertEquals(80, $request->getPort()); } 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()); } 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')); } 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( 'abc', '123', 'HI', '456' ), $request->getHeader('zoo')->toArray()); } 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')); } public function testBugPathIncorrectlyHandled() { $message = "POST /foo\r\n\r\nBODY"; $request = RequestFactory::getInstance()->fromMessage($message); $this->assertSame('POST', $request->getMethod()); $this->assertSame('/foo', $request->getPath()); $this->assertSame('BODY', (string) $request->getBody()); } 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')); } public function testClonesRequestsWithMethodWithoutClient() { $f = RequestFactory::getInstance(); $request = $f->create('GET', 'http://www.test.com', array('X-Foo' => 'Bar')); $request->getParams()->replace(array('test' => '123')); $request->getCurlOptions()->set('foo', 'bar'); $cloned = $f->cloneRequestWithMethod($request, 'PUT'); $this->assertEquals('PUT', $cloned->getMethod()); $this->assertEquals('Bar', (string) $cloned->getHeader('X-Foo')); $this->assertEquals('http://www.test.com', $cloned->getUrl()); // Ensure params are cloned and cleaned up $this->assertEquals(1, count($cloned->getParams()->getAll())); $this->assertEquals('123', $cloned->getParams()->get('test')); // Ensure curl options are cloned $this->assertEquals('bar', $cloned->getCurlOptions()->get('foo')); // Ensure event dispatcher is cloned $this->assertNotSame($request->getEventDispatcher(), $cloned->getEventDispatcher()); } public function testClonesRequestsWithMethodWithClient() { $f = RequestFactory::getInstance(); $client = new Client(); $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); $cloned = $f->cloneRequestWithMethod($request, 'GET'); $this->assertEquals('GET', $cloned->getMethod()); $this->assertNull($cloned->getHeader('Content-Length')); $this->assertEquals('http://www.test.com', $cloned->getUrl()); $this->assertSame($request->getClient(), $cloned->getClient()); } public function testClonesRequestsWithMethodWithClientWithEntityEnclosingChange() { $f = RequestFactory::getInstance(); $client = new Client(); $request = $client->put('http://www.test.com', array('Content-Length' => 4), 'test'); $cloned = $f->cloneRequestWithMethod($request, 'POST'); $this->assertEquals('POST', $cloned->getMethod()); $this->assertEquals('test', (string) $cloned->getBody()); } public function testCanDisableRedirects() { $this->getServer()->enqueue(array( "HTTP/1.1 307\r\nLocation: " . $this->getServer()->getUrl() . "\r\nContent-Length: 0\r\n\r\n" )); $client = new Client($this->getServer()->getUrl()); $response = $client->get('/', array(), array('allow_redirects' => false))->send(); $this->assertEquals(307, $response->getStatusCode()); } public function testCanAddCookies() { $client = new Client($this->getServer()->getUrl()); $request = $client->get('/', array(), array('cookies' => array('Foo' => 'Bar'))); $this->assertEquals('Bar', $request->getCookie('Foo')); } public function testCanAddQueryString() { $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( 'query' => array('Foo' => 'Bar') )); $this->assertEquals('Bar', $request->getQuery()->get('Foo')); } public function testCanSetDefaultQueryString() { $request = new Request('GET', 'http://www.foo.com?test=abc'); RequestFactory::getInstance()->applyOptions($request, array( 'query' => array('test' => '123', 'other' => 't123') ), RequestFactory::OPTIONS_AS_DEFAULTS); $this->assertEquals('abc', $request->getQuery()->get('test')); $this->assertEquals('t123', $request->getQuery()->get('other')); } public function testCanAddBasicAuth() { $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( 'auth' => array('michael', 'test') )); $this->assertEquals('michael', $request->getUsername()); $this->assertEquals('test', $request->getPassword()); } public function testCanAddDigestAuth() { $request = RequestFactory::getInstance()->create('GET', 'http://foo.com', array(), null, array( 'auth' => array('michael', 'test', 'digest') )); $this->assertEquals(CURLAUTH_DIGEST, $request->getCurlOptions()->get(CURLOPT_HTTPAUTH)); $this->assertEquals('michael', $request->getUsername()); $this->assertEquals('test', $request->getPassword()); } public function testCanAddEvents() { $foo = null; $client = new Client(); $client->addSubscriber(new MockPlugin(array(new Response(200)))); $request = $client->get($this->getServer()->getUrl(), array(), array( 'events' => array( 'request.before_send' => function () use (&$foo) { $foo = true; } ) )); $request->send(); $this->assertTrue($foo); } public function testCanAddEventsWithPriority() { $foo = null; $client = new Client(); $client->addSubscriber(new MockPlugin(array(new Response(200)))); $request = $client->get($this->getServer()->getUrl(), array(), array( 'events' => array( 'request.before_send' => array(function () use (&$foo) { $foo = true; }, 100) ) )); $request->send(); $this->assertTrue($foo); } public function testCanAddPlugins() { $mock = new MockPlugin(array( new Response(200), new Response(200) )); $client = new Client(); $client->addSubscriber($mock); $request = $client->get('/', array(), array( 'plugins' => array($mock) )); $request->send(); } public function testCanDisableExceptions() { $client = new Client(); $request = $client->get('/', array(), array( 'plugins' => array(new MockPlugin(array(new Response(500)))), 'exceptions' => false )); $this->assertEquals(500, $request->send()->getStatusCode()); } public function testCanDisableExceptionsWithErrorListener() { $client = new Client(); $client->getEventDispatcher()->addListener('request.error', function () {}); $request = $client->get('/', array(), array( 'plugins' => array(new MockPlugin(array(new Response(500)))), 'exceptions' => false )); $this->assertEquals(500, $request->send()->getStatusCode()); } public function testCanChangeSaveToLocation() { $r = EntityBody::factory(); $client = new Client(); $request = $client->get('/', array(), array( 'plugins' => array(new MockPlugin(array(new Response(200, array(), 'testing')))), 'save_to' => $r )); $request->send(); $this->assertEquals('testing', (string) $r); } public function testCanSetProxy() { $client = new Client(); $request = $client->get('/', array(), array('proxy' => '192.168.16.121')); $this->assertEquals('192.168.16.121', $request->getCurlOptions()->get(CURLOPT_PROXY)); } public function testCanSetHeadersOption() { $client = new Client(); $request = $client->get('/', array(), array('headers' => array('Foo' => 'Bar'))); $this->assertEquals('Bar', (string) $request->getHeader('Foo')); } public function testCanSetDefaultHeadersOptions() { $request = new Request('GET', 'http://www.foo.com', array('Foo' => 'Bar')); RequestFactory::getInstance()->applyOptions($request, array( 'headers' => array('Foo' => 'Baz', 'Bam' => 't123') ), RequestFactory::OPTIONS_AS_DEFAULTS); $this->assertEquals('Bar', (string) $request->getHeader('Foo')); $this->assertEquals('t123', (string) $request->getHeader('Bam')); } public function testCanSetBodyOption() { $client = new Client(); $request = $client->put('/', array(), null, array('body' => 'test')); $this->assertEquals('test', (string) $request->getBody()); } /** * @expectedException \InvalidArgumentException */ public function testValidatesBodyOption() { $client = new Client(); $client->get('/', array(), array('body' => 'test')); } public function testCanSetTimeoutOption() { $client = new Client(); $request = $client->get('/', array(), array('timeout' => 1.5)); $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_TIMEOUT_MS)); } public function testCanSetConnectTimeoutOption() { $client = new Client(); $request = $client->get('/', array(), array('connect_timeout' => 1.5)); $this->assertEquals(1500, $request->getCurlOptions()->get(CURLOPT_CONNECTTIMEOUT_MS)); } public function testCanSetDebug() { $client = new Client(); $request = $client->get('/', array(), array('debug' => true)); $this->assertTrue($request->getCurlOptions()->get(CURLOPT_VERBOSE)); } public function testCanSetVerifyToOff() { $client = new Client(); $request = $client->get('/', array(), array('verify' => false)); $this->assertNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); $this->assertSame(0, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); $this->assertFalse($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); } public function testCanSetVerifyToOn() { $client = new Client(); $request = $client->get('/', array(), array('verify' => true)); $this->assertNotNull($request->getCurlOptions()->get(CURLOPT_CAINFO)); $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); } public function testCanSetVerifyToPath() { $client = new Client(); $request = $client->get('/', array(), array('verify' => '/foo.pem')); $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_CAINFO)); $this->assertSame(2, $request->getCurlOptions()->get(CURLOPT_SSL_VERIFYHOST)); $this->assertTrue($request->getCurlOptions()->get(CURLOPT_SSL_VERIFYPEER)); } public function inputValidation() { return array_map(function ($option) { return array($option); }, array( 'headers', 'query', 'cookies', 'auth', 'events', 'plugins', 'params' )); } /** * @dataProvider inputValidation * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testValidatesInput($option) { $client = new Client(); $client->get('/', array(), array($option => 'foo')); } public function testCanAddRequestParams() { $client = new Client(); $request = $client->put('/', array(), null, array('params' => array('foo' => 'test'))); $this->assertEquals('test', $request->getParams()->get('foo')); } public function testCanAddSslKey() { $client = new Client(); $request = $client->get('/', array(), array('ssl_key' => '/foo.pem')); $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); } public function testCanAddSslKeyPassword() { $client = new Client(); $request = $client->get('/', array(), array('ssl_key' => array('/foo.pem', 'bar'))); $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLKEY)); $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLKEYPASSWD)); } public function testCanAddSslCert() { $client = new Client(); $request = $client->get('/', array(), array('cert' => '/foo.pem')); $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); } public function testCanAddSslCertPassword() { $client = new Client(); $request = $client->get('/', array(), array('cert' => array('/foo.pem', 'bar'))); $this->assertEquals('/foo.pem', $request->getCurlOptions()->get(CURLOPT_SSLCERT)); $this->assertEquals('bar', $request->getCurlOptions()->get(CURLOPT_SSLCERTPASSWD)); } public function testCreatesBodyWithoutZeroString() { $request = RequestFactory::getInstance()->create('PUT', 'http://test.com', array(), '0'); $this->assertSame('0', (string) $request->getBody()); } } guzzle/tests/Guzzle/Tests/Http/Message/AbstractMessageTest.php 0000604 00000010257 15173213567 0020547 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message; use Guzzle\Http\Message\Header; use Guzzle\Http\Message\Request; use Guzzle\Common\Collection; /** * @covers Guzzle\Http\Message\AbstractMessage */ class AbstractMessageTest extends \Guzzle\Tests\GuzzleTestCase { /** @var Request Request object */ private $request; /** @var AbstractMessage */ private $mock; public function setUp() { parent::setUp(); $this->mock = $this->getMockForAbstractClass('Guzzle\Http\Message\AbstractMessage'); } public function tearDown() { $this->mock = $this->request = null; } public function testGetParams() { $request = new Request('GET', 'http://example.com'); $this->assertInstanceOf('Guzzle\\Common\\Collection', $request->getParams()); } 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); } public function testAllowsHeaderToSetAsHeader() { $h = new Header('A', 'B'); $this->mock->setHeader('A', $h); $this->assertSame($h, $this->mock->getHeader('A')); } public function testGetHeader() { $this->mock->setHeader('Test', '123'); $this->assertEquals('123', $this->mock->getHeader('Test')); } public function testGetHeaders() { $this->assertSame($this->mock, $this->mock->setHeaders(array('a' => 'b', 'c' => 'd'))); $h = $this->mock->getHeaders(); $this->assertArrayHasKey('a', $h->toArray()); $this->assertArrayHasKey('c', $h->toArray()); $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('a')); $this->assertInstanceOf('Guzzle\Http\Message\Header\HeaderInterface', $h->get('c')); } 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()); } 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')); } 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')); } public function testReturnsNullWhenHeaderIsNotFound() { $this->assertNull($this->mock->getHeader('foo')); } 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->assertContains('123', $header->toArray()); $this->assertContains('456', $header->toArray()); $this->assertContains('789', $header->toArray()); $this->assertContains('abc', $header->toArray()); } public function testCanStoreEmptyHeaders() { $this->mock->setHeader('Content-Length', 0); $this->assertTrue($this->mock->hasHeader('Content-Length')); $this->assertEquals(0, (string) $this->mock->getHeader('Content-Length')); } public function testCanSetCustomHeaderFactory() { $f = new Header\HeaderFactory(); $this->mock->setHeaderFactory($f); $this->assertSame($f, $this->readAttribute($this->mock, 'headerFactory')); } } guzzle/tests/Guzzle/Tests/Http/Message/RequestTest.php 0000604 00000063162 15173213567 0017132 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message; use Guzzle\Common\Collection; use Guzzle\Http\EntityBody; use Guzzle\Http\Url; use Guzzle\Http\Client; use Guzzle\Plugin\Async\AsyncPlugin; use Guzzle\Http\Message\RequestInterface; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\RedirectPlugin; use Guzzle\Http\Exception\BadResponseException; /** * @group server * @covers Guzzle\Http\Message\Request * @covers Guzzle\Http\Message\AbstractMessage */ class RequestTest extends \Guzzle\Tests\GuzzleTestCase { /** @var Request */ protected $request; /** @var Client */ protected $client; protected function setUp() { $this->client = new Client($this->getServer()->getUrl()); $this->request = $this->client->get(); } public function tearDown() { unset($this->request); unset($this->client); } 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')); } public function testDescribesEvents() { $this->assertInternalType('array', Request::getAllEvents()); } public function testConstructorBuildsRequestWithCollectionHeaders() { $request = new Request('GET', 'http://www.guzzle-project.com/', new Collection(array( 'foo' => 'bar' ))); $this->assertEquals('bar', $request->getHeader('foo')); } public function testConstructorBuildsRequestWithNoHeaders() { $request = new Request('GET', 'http://www.guzzle-project.com/', null); $this->assertFalse($request->hasHeader('foo')); } 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')); } 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" . "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 */ 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); } public function testGetEventDispatcher() { $d = $this->request->getEventDispatcher(); $this->assertInstanceOf('Symfony\\Component\\EventDispatcher\\EventDispatcherInterface', $d); $this->assertEquals($d, $this->request->getEventDispatcher()); } public function testRequestsManageClients() { $request = new Request('GET', 'http://test.com'); $this->assertNull($request->getClient()); $request->setClient($this->client); $this->assertSame($this->client, $request->getClient()); } /** * @expectedException \RuntimeException * @expectedExceptionMessage A client must be set on the request */ public function testRequestsRequireClients() { $request = new Request('GET', 'http://test.com'); $request->send(); } 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()); } 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()); $client = new Client('http://www.google.com'); $request = $client->get('http://www.google.com/'); $request->setResponse($response, true); $request->send(); $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 = $client->get(); $request->setResponse($response, true); $request->send(); $requestResponse = $request->getResponse(); $this->assertSame($response, $requestResponse); $this->assertInstanceOf('Guzzle\\Http\\EntityBody', $response->getBody()); } public function testRequestThrowsExceptionOnBadResponse() { try { $this->request->setResponse(new Response(404, array('Content-Length' => 3), 'abc'), true); $this->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()); } } 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)); } public function testRequestHasMethod() { $this->assertEquals('GET', $this->request->getMethod()); } public function testRequestHasScheme() { $this->assertEquals('http', $this->request->getScheme()); $this->assertEquals($this->request, $this->request->setScheme('https')); $this->assertEquals('https', $this->request->getScheme()); } 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()); } 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()); } 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()); } public function testPermitsFalsyComponents() { $request = new Request('GET', 'http://0/0?0'); $this->assertSame('0', $request->getHost()); $this->assertSame('/0', $request->getPath()); $this->assertSame('0', $request->getQuery(true)); $request = new Request('GET', '0'); $this->assertEquals('/0', $request->getPath()); } 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')); } 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()); // Set an auth with blank password $this->assertSame($this->request, $this->request->setAuth('michael', '')); $this->assertEquals('michael', $this->request->getUsername()); $this->assertEquals('', $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)); } /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testValidatesAuth() { $this->request->setAuth('foo', 'bar', 'bam'); } 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()); } 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()); } 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()); } 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'); } 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); $entityBody = EntityBody::factory(fopen($file, 'w+')); $request->setResponseBody($entityBody); $response = $request->send(); $this->assertSame($entityBody, $response->getBody()); $this->assertTrue(file_exists($file)); $this->assertEquals('data', file_get_contents($file)); unlink($file); $this->assertEquals('data', $response->getBody(true)); } 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')); // Multiple cookies by setting the Cookie header $this->request->setHeader('Cookie', '__utma=1.638370270.1344367610.1374365610.1944450276.2; __utmz=1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); hl=de; PHPSESSID=ak93pqashi5uubuoq8fjv60897'); $this->assertEquals('1.638370270.1344367610.1374365610.1944450276.2', $this->request->getCookie('__utma')); $this->assertEquals('1.1346368610.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)', $this->request->getCookie('__utmz')); $this->assertEquals('de', $this->request->getCookie('hl')); $this->assertEquals('ak93pqashi5uubuoq8fjv60897', $this->request->getCookie('PHPSESSID')); // Unset the cookies by setting the Cookie header to null $this->request->setHeader('Cookie', null); $this->assertNull($this->request->getCookie('test')); $this->request->removeHeader('Cookie'); // 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()); $this->request->addCookie('foo', 'bar'); $this->assertEquals('baz=boo; foo=bar', (string) $this->request->getHeader('Cookie')); } /** * @expectedException \Guzzle\Http\Exception\RequestException * @expectedExceptionMessage Error completing request */ public function testRequestThrowsExceptionWhenSetToCompleteWithNoResponse() { $this->request->setState(RequestInterface::STATE_COMPLETE); } public function testClonedRequestsUseNewInternalState() { $p = new AsyncPlugin(); $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->assertTrue($this->request->getEventDispatcher()->hasListeners('request.sent')); } public function testRecognizesBasicAuthCredentialsInUrls() { $this->request->setUrl('http://michael:test@test.com/'); $this->assertEquals('michael', $this->request->getUsername()); $this->assertEquals('test', $this->request->getPassword()); } 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\n<error><mesage>File not found</message></error>" )); $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 = $this->client->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]); } 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()); } } 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()); } 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())); } 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)); } public function testUnresolvedRedirectsReturnResponse() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 303 SEE OTHER\r\nContent-Length: 0\r\n\r\n", "HTTP/1.1 301 Foo\r\nLocation: /foo\r\nContent-Length: 0\r\n\r\n" )); $request = $this->request; $this->assertEquals(303, $request->send()->getStatusCode()); $request->getParams()->set(RedirectPlugin::DISABLE, true); $this->assertEquals(301, $request->send()->getStatusCode()); } public function testCanSendCustomRequests() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $request = $this->client->createRequest('PROPFIND', $this->getServer()->getUrl(), array( 'Content-Type' => 'text/plain' ), 'foo'); $response = $request->send(); $requests = $this->getServer()->getReceivedRequests(true); $this->assertEquals('PROPFIND', $requests[0]->getMethod()); $this->assertEquals(3, (string) $requests[0]->getHeader('Content-Length')); $this->assertEquals('foo', (string) $requests[0]->getBody()); } /** * @expectedException \PHPUnit_Framework_Error_Warning */ public function testEnsuresFileCanBeCreated() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); $this->client->get('/')->setResponseBody('/wefwefefefefwewefwe/wefwefwefefwe/wefwefewfw.txt')->send(); } public function testAllowsFilenameForDownloadingContent() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\ntest"); $name = sys_get_temp_dir() . '/foo.txt'; $this->client->get('/')->setResponseBody($name)->send(); $this->assertEquals('test', file_get_contents($name)); unlink($name); } public function testUsesCustomResponseBodyWhenItIsCustom() { $en = EntityBody::factory(); $request = $this->client->get(); $request->setResponseBody($en); $request->setResponse(new Response(200, array(), 'foo')); $this->assertEquals('foo', (string) $en); } public function testCanChangePortThroughScheme() { $request = new Request('GET', 'http://foo.com'); $request->setScheme('https'); $this->assertEquals('https://foo.com', (string) $request->getUrl()); $this->assertEquals('foo.com', $request->getHost()); $request->setScheme('http'); $this->assertEquals('http://foo.com', (string) $request->getUrl()); $this->assertEquals('foo.com', $request->getHost()); $request->setPort(null); $this->assertEquals('http://foo.com', (string) $request->getUrl()); $this->assertEquals('foo.com', $request->getHost()); } } guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparisonTest.php 0000604 00000005556 15173213567 0020730 0 ustar 00 <?php namespace Guzzle\Tests\Message; use Guzzle\Common\Collection; use Guzzle\Tests\Http\Message\HeaderComparison; class HeaderComparisonTest extends \Guzzle\Tests\GuzzleTestCase { public function filterProvider() { return array( // Headers match array(array( 'Content-Length' => '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)); } } guzzle/tests/Guzzle/Tests/Http/Message/Header/LinkTest.php 0000604 00000004003 15173213567 0017554 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message\Header; use Guzzle\Http\Message\Header\Link; use Guzzle\Tests\GuzzleTestCase; class LinkTest extends GuzzleTestCase { public function testParsesLinks() { $link = new Link('Link', '<http:/.../front.jpeg>; rel=front; type="image/jpeg", <http://.../back.jpeg>; rel=back; type="image/jpeg", <http://.../side.jpeg?test=1>; rel=side; type="image/jpeg"'); $links = $link->getLinks(); $this->assertEquals(array( array( 'rel' => 'front', 'type' => 'image/jpeg', 'url' => 'http:/.../front.jpeg', ), array( 'rel' => 'back', 'type' => 'image/jpeg', 'url' => 'http://.../back.jpeg', ), array( 'rel' => 'side', 'type' => 'image/jpeg', 'url' => 'http://.../side.jpeg?test=1' ) ), $links); $this->assertEquals(array( 'rel' => 'back', 'type' => 'image/jpeg', 'url' => 'http://.../back.jpeg', ), $link->getLink('back')); $this->assertTrue($link->hasLink('front')); $this->assertFalse($link->hasLink('foo')); } public function testCanAddLink() { $link = new Link('Link', '<http://foo>; rel=a; type="image/jpeg"'); $link->addLink('http://test.com', 'test', array('foo' => 'bar')); $this->assertEquals( '<http://foo>; rel=a; type="image/jpeg", <http://test.com>; rel="test"; foo="bar"', (string) $link ); } public function testCanParseLinksWithCommas() { $link = new Link('Link', '<http://example.com/TheBook/chapter1>; rel="previous"; title="start, index"'); $this->assertEquals(array( array( 'rel' => 'previous', 'title' => 'start, index', 'url' => 'http://example.com/TheBook/chapter1', ) ), $link->getLinks()); } } guzzle/tests/Guzzle/Tests/Http/Message/Header/HeaderFactoryTest.php 0000604 00000001551 15173213567 0021404 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message\Header; use Guzzle\Http\Message\Header\HeaderFactory; /** * @covers Guzzle\Http\Message\Header\HeaderFactory */ class HeaderFactoryTest extends \Guzzle\Tests\GuzzleTestCase { public function testCreatesBasicHeaders() { $f = new HeaderFactory(); $h = $f->createHeader('Foo', 'Bar'); $this->assertInstanceOf('Guzzle\Http\Message\Header', $h); $this->assertEquals('Foo', $h->getName()); $this->assertEquals('Bar', (string) $h); } public function testCreatesSpecificHeaders() { $f = new HeaderFactory(); $h = $f->createHeader('Link', '<http>; rel="test"'); $this->assertInstanceOf('Guzzle\Http\Message\Header\Link', $h); $this->assertEquals('Link', $h->getName()); $this->assertEquals('<http>; rel="test"', (string) $h); } } guzzle/tests/Guzzle/Tests/Http/Message/PostFileTest.php 0000604 00000006142 15173213567 0017222 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message; use Guzzle\Http\Client; use Guzzle\Http\Message\PostFile; /** * @covers Guzzle\Http\Message\PostFile * @group server */ class PostFileTest extends \Guzzle\Tests\GuzzleTestCase { public function testConstructorConfiguresPostFile() { $file = new PostFile('foo', __FILE__, 'x-foo', 'boo'); $this->assertEquals('foo', $file->getFieldName()); $this->assertEquals(__FILE__, $file->getFilename()); $this->assertEquals('boo', $file->getPostName()); $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__); if (version_compare(phpversion(), '5.5.0', '<')) { $this->assertContains('@' . __FILE__ . ';filename=PostFileTest.php;type=text/x-', $file->getCurlValue()); } else { $c = $file->getCurlValue(); $this->assertEquals(__FILE__, $c->getFilename()); $this->assertEquals('PostFileTest.php', $c->getPostFilename()); $this->assertContains('text/x-', $c->getMimeType()); } } public function testReturnsCurlValueStringAndPostname() { $file = new PostFile('foo', __FILE__, null, 'NewPostFileTest.php'); if (version_compare(phpversion(), '5.5.0', '<')) { $this->assertContains('@' . __FILE__ . ';filename=NewPostFileTest.php;type=text/x-', $file->getCurlValue()); } else { $c = $file->getCurlValue(); $this->assertEquals(__FILE__, $c->getFilename()); $this->assertEquals('NewPostFileTest.php', $c->getPostFilename()); $this->assertContains('text/x-', $c->getMimeType()); } } public function testContentDispositionFilePathIsStripped() { $this->getServer()->flush(); $client = new Client($this->getServer()->getUrl()); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $request = $client->post()->addPostFile('file', __FILE__); $request->send(); $requests = $this->getServer()->getReceivedRequests(false); $this->assertContains('POST / HTTP/1.1', $requests[0]); $this->assertContains('Content-Disposition: form-data; name="file"; filename="PostFileTest.php"', $requests[0]); } } guzzle/tests/Guzzle/Tests/Http/Message/HeaderComparison.php 0000604 00000010642 15173213567 0020060 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message; use Guzzle\Common\Collection; use Guzzle\Http\Message\Header\HeaderCollection; /** * Class used to compare HTTP headers using a custom DSL */ class HeaderComparison { /** * 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|bool Returns an array of the differences or FALSE if none */ public function compare($filteredHeaders, $actualHeaders) { $expected = array(); $ignore = array(); $absent = array(); if ($actualHeaders instanceof HeaderCollection) { $actualHeaders = $actualHeaders->toArray(); } 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 * * @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|bool 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) { if ($array instanceof Collection) { $keys = $array->getKeys(); } else { $keys = array_keys($array); } foreach ($keys as $k) { if (!strcasecmp($k, $key)) { return true; } } return false; } } guzzle/tests/Guzzle/Tests/Http/Message/EntityEnclosingRequestTest.php 0000604 00000040312 15173213567 0022161 0 ustar 00 <?php namespace Guzzle\Tests\Http\Message; use Guzzle\Http\Client; use Guzzle\Http\EntityBody; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\RedirectPlugin; use Guzzle\Http\Message\EntityEnclosingRequest; use Guzzle\Http\Message\PostFile; use Guzzle\Http\QueryString; /** * @group server * @covers Guzzle\Http\Message\EntityEnclosingRequest */ class EntityEnclosingRequestTest extends \Guzzle\Tests\GuzzleTestCase { protected $client; public function setUp() { $this->client = new Client(); } public function tearDown() { $this->client = null; } public function testConstructorConfiguresRequest() { $request = new EntityEnclosingRequest('PUT', 'http://test.com', array( 'X-Test' => '123' )); $request->setBody('Test'); $this->assertEquals('123', $request->getHeader('X-Test')); $this->assertNull($request->getHeader('Expect')); } public function testCanSetBodyWithoutOverridingContentType() { $request = new EntityEnclosingRequest('PUT', 'http://test.com', array('Content-Type' => 'foooooo')); $request->setBody('{"a":"b"}'); $this->assertEquals('foooooo', $request->getHeader('Content-Type')); } 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" . "Content-Length: 4\r\n\r\n" . "data", (string) $request); } 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" . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\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" . "Content-Type: multipart/form-data\r\n" . "Expect: 100-Continue\r\n\r\n", (string) $request); } 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" . "Content-Type: application/x-www-form-urlencoded; charset=utf-8\r\n\r\n" . "data=123", (string) $request); } 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')); } 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); } 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')); } public function testRequestBodyDoesNotUseContentLengthWhenChunked() { $request = RequestFactory::getInstance()->create('PUT', 'http://www.test.com/', array( 'Transfer-Encoding' => 'chunked' ), 'test'); $this->assertNull($request->getHeader('Content-Length')); $this->assertTrue($request->hasHeader('Transfer-Encoding')); } 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()); } 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()); } 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()); $files = $request->getPostFiles(); $post = $files['file'][0]; $this->assertEquals('file', $post->getFieldName()); $this->assertContains('text/x-', $post->getContentType()); $this->assertEquals(__FILE__, $post->getFilename()); $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'); } /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testSetPostFilesThrowsExceptionWhenFileIsNotFound() { $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') ->addPostFiles(array( 'file' => 'filenotfound.ini' )); } /** * @expectedException Guzzle\Http\Exception\RequestException */ public function testThrowsExceptionWhenNonStringsAreAddedToPost() { $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') ->addPostFile('foo', new \stdClass()); } 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')); } public function testGuessesContentTypeOfPostUpload() { $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/') ->addPostFile('foo', __FILE__); $file = $request->getPostFile('foo'); $this->assertContains('text/x-', $file[0]->getContentType()); } 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')); } 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; charset=utf-8', $request->getHeader('Content-Type')); } public function testProcessMethodAddsContentType() { $request = RequestFactory::getInstance()->create('POST', 'http://www.guzzle-project.com/'); $request->setPostField('a', 'b'); $this->assertEquals('application/x-www-form-urlencoded; charset=utf-8', $request->getHeader('Content-Type')); } 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')); } 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, (string) $requests[0]->getHeader('Content-Length')); $this->assertEquals(7, (string) $requests[1]->getHeader('Content-Length')); } public function testRemovingPostFieldRebuildsPostFields() { $request = new EntityEnclosingRequest('POST', 'http://test.com'); $request->setPostField('test', 'value'); $request->removePostField('test'); $this->assertNull($request->getPostField('test')); } 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')); } /** * @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')); } 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()); } public function testAllowsEmptyFields() { $request = new EntityEnclosingRequest('POST', 'http://test.com/'); $request->addPostFields(array( 'a' => '' )); $this->assertEquals(array( 'a' => '' ), $request->getPostFields()->getAll()); } /** * @expectedException \Guzzle\Http\Exception\RequestException */ public function testFailsOnInvalidFiles() { $request = new EntityEnclosingRequest('POST', 'http://test.com/'); $request->addPostFiles(array( 'a' => new \stdClass() )); } 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()); } public function testHoldsPostFiles() { $request = new EntityEnclosingRequest('POST', 'http://test.com/'); $request->addPostFile('foo', __FILE__); $request->addPostFile(new PostFile('foo', __FILE__)); $this->assertArrayHasKey('foo', $request->getPostFiles()); $foo = $request->getPostFile('foo'); $this->assertEquals(2, count($foo)); $this->assertEquals(__FILE__, $foo[0]->getFilename()); $this->assertEquals(__FILE__, $foo[1]->getFilename()); $request->removePostFile('foo'); $this->assertEquals(array(), $request->getPostFiles()); } public function testAllowsAtPrefixWhenAddingPostFiles() { $request = new EntityEnclosingRequest('POST', 'http://test.com/'); $request->addPostFiles(array( 'foo' => '@' . __FILE__ )); $foo = $request->getPostFile('foo'); $this->assertEquals(__FILE__, $foo[0]->getFilename()); } public function testSetStateToTransferWithEmptyBodySetsContentLengthToZero() { $request = new EntityEnclosingRequest('POST', 'http://test.com/'); $request->setState($request::STATE_TRANSFER); $this->assertEquals('0', (string) $request->getHeader('Content-Length')); } public function testSettingExpectHeaderCutoffChangesRequest() { $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); $request->setHeader('Expect', '100-Continue'); $request->setExpectHeaderCutoff(false); $this->assertNull($request->getHeader('Expect')); // There is not body, so remove the expect header $request->setHeader('Expect', '100-Continue'); $request->setExpectHeaderCutoff(10); $this->assertNull($request->getHeader('Expect')); // The size is less than the cutoff $request->setBody('foo'); $this->assertNull($request->getHeader('Expect')); // The size is greater than the cutoff $request->setBody('foobazbarbamboo'); $this->assertNotNull($request->getHeader('Expect')); } public function testStrictRedirectsCanBeSpecifiedOnEntityEnclosingRequests() { $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); $request->configureRedirects(true); $this->assertTrue($request->getParams()->get(RedirectPlugin::STRICT_REDIRECTS)); } public function testCanDisableRedirects() { $request = new EntityEnclosingRequest('PUT', 'http://test.com/'); $request->configureRedirects(false, false); $this->assertTrue($request->getParams()->get(RedirectPlugin::DISABLE)); } public function testSetsContentTypeWhenSettingBodyByGuessingFromEntityBody() { $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); $request->setBody(EntityBody::factory(fopen(__FILE__, 'r'))); $this->assertEquals('text/x-php', (string) $request->getHeader('Content-Type')); } public function testDoesNotCloneBody() { $request = new EntityEnclosingRequest('PUT', 'http://test.com/foo'); $request->setBody('test'); $newRequest = clone $request; $newRequest->setBody('foo'); $this->assertInternalType('string', (string) $request->getBody()); } } guzzle/tests/Guzzle/Tests/Http/server.js 0000604 00000011544 15173213567 0014406 0 ustar 00 /** * 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 <http://www.guzzlephp.org> * @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, log) { this.port = port; this.log = log; this.responses = []; this.requests = []; var that = this; var controlRequest = function(request, req, res) { if (req.url == '/guzzle-server/perf') { res.writeHead(200, "OK", {"Content-Length": 16}); res.end("Body of response"); } else if (req.method == "DELETE") { if (req.url == "/guzzle-server/requests") { // Clear the received requests that.requests = []; res.writeHead(200, "OK", { "Content-Length": 0 }); res.end(); if (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(400, "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(); } } }; var receivedRequest = function(request, req, res) { if (req.url.indexOf("/guzzle-server") === 0) { controlRequest(request, req, res); } else if (req.url.indexOf("/guzzle-server") == -1 && !that.responses.length) { res.writeHead(500); res.end("No responses in queue"); } else { var response = that.responses.shift(); res.writeHead(response.statusCode, response.reasonPhrase, response.headers); res.end(response.body); that.requests.push(request); } }; this.start = function() { that.server = http.createServer(function(req, res) { var request = req.method + " " + req.url + " HTTP/" + req.httpVersion + "\r\n"; 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(); guzzle/tests/Guzzle/Tests/Http/AbstractEntityBodyDecoratorTest.php 0000604 00000002723 15173213567 0021533 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\EntityBody; /** * @covers Guzzle\Http\AbstractEntityBodyDecorator */ class AbstractEntityBodyDecoratorTest extends \Guzzle\Tests\GuzzleTestCase { public function testDecoratesEntityBody() { $e = EntityBody::factory(); $mock = $this->getMockForAbstractClass('Guzzle\Http\AbstractEntityBodyDecorator', array($e)); $this->assertSame($e->getStream(), $mock->getStream()); $this->assertSame($e->getContentLength(), $mock->getContentLength()); $this->assertSame($e->getSize(), $mock->getSize()); $this->assertSame($e->getContentMd5(), $mock->getContentMd5()); $this->assertSame($e->getContentType(), $mock->getContentType()); $this->assertSame($e->__toString(), $mock->__toString()); $this->assertSame($e->getUri(), $mock->getUri()); $this->assertSame($e->getStreamType(), $mock->getStreamType()); $this->assertSame($e->getWrapper(), $mock->getWrapper()); $this->assertSame($e->getWrapperData(), $mock->getWrapperData()); $this->assertSame($e->isReadable(), $mock->isReadable()); $this->assertSame($e->isWritable(), $mock->isWritable()); $this->assertSame($e->isConsumed(), $mock->isConsumed()); $this->assertSame($e->isLocal(), $mock->isLocal()); $this->assertSame($e->isSeekable(), $mock->isSeekable()); $this->assertSame($e->getContentEncoding(), $mock->getContentEncoding()); } } guzzle/tests/Guzzle/Tests/Http/QueryStringTest.php 0000604 00000017315 15173213567 0016411 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\QueryString; use Guzzle\Http\QueryAggregator\DuplicateAggregator; use Guzzle\Http\QueryAggregator\CommaAggregator; class QueryStringTest extends \Guzzle\Tests\GuzzleTestCase { /** @var \Guzzle\Http\QueryString The query string object to test */ protected $q; public function setup() { $this->q = new QueryString(); } public function testGetFieldSeparator() { $this->assertEquals('&', $this->q->getFieldSeparator()); } public function testGetValueSeparator() { $this->assertEquals('=', $this->q->getValueSeparator()); } public function testIsUrlEncoding() { $this->assertEquals('RFC 3986', $this->q->getUrlEncoding()); $this->assertTrue($this->q->isUrlEncoding()); $this->assertEquals('foo%20bar', $this->q->encodeValue('foo bar')); $this->q->useUrlEncoding(QueryString::FORM_URLENCODED); $this->assertTrue($this->q->isUrlEncoding()); $this->assertEquals(QueryString::FORM_URLENCODED, $this->q->getUrlEncoding()); $this->assertEquals('foo+bar', $this->q->encodeValue('foo bar')); $this->assertSame($this->q, $this->q->useUrlEncoding(false)); $this->assertFalse($this->q->isUrlEncoding()); $this->assertFalse($this->q->isUrlEncoding()); } public function testSetFieldSeparator() { $this->assertEquals($this->q, $this->q->setFieldSeparator('/')); $this->assertEquals('/', $this->q->getFieldSeparator()); } public function testSetValueSeparator() { $this->assertEquals($this->q, $this->q->setValueSeparator('/')); $this->assertEquals('/', $this->q->getValueSeparator()); } 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 encoding $testData = array('test 2' => 'this is a test'); $this->q->replace($testData); $this->q->useUrlEncoding(false); $this->assertEquals($testData, $this->q->urlEncode()); } 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'), 'test4' => null, ); $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&test4', $this->q->__toString()); $this->q->useUrlEncoding(false); $this->assertEquals('test=value&test 2=this is a test?&test3[0]=v1&test3[1]=v2&test3[2]=v3&test4', $this->q->__toString()); // Use an alternative aggregator $this->q->setAggregator(new CommaAggregator()); $this->assertEquals('test=value&test 2=this is a test?&test3=v1,v2,v3&test4', $this->q->__toString()); } public function testAllowsMultipleValuesPerKey() { $q = new QueryString(); $q->add('facet', 'size'); $q->add('facet', 'width'); $q->add('facet.field', 'foo'); // Use the duplicate aggregator $q->setAggregator(new DuplicateAggregator()); $this->assertEquals('facet=size&facet=width&facet.field=foo', $q->__toString()); } public function testAllowsNestedQueryData() { $this->q->replace(array( 'test' => 'value', 't' => array( 'v1' => 'a', 'v2' => 'b', 'v3' => array( 'v4' => 'c', 'v5' => 'd', ) ) )); $this->q->useUrlEncoding(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 a single PHP array style query string value is parsed into an array array('q[]=a', array('q' => array('a'))), // 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 null values can be added array('q&a', array('q' => false, 'a' => false)), ); } /** * @dataProvider parseQueryProvider */ public function testParsesQueryStrings($query, $data) { $query = QueryString::fromString($query); $this->assertEquals($data, $query->getAll()); } public function testProperlyDealsWithDuplicateQueryStringValues() { $query = QueryString::fromString('foo=a&foo=b&?µ=c'); $this->assertEquals(array('a', 'b'), $query->get('foo')); $this->assertEquals('c', $query->get('?µ')); } public function testAllowsBlankQueryStringValues() { $query = QueryString::fromString('foo'); $this->assertEquals('foo', (string) $query); $query->set('foo', QueryString::BLANK); $this->assertEquals('foo', (string) $query); } public function testAllowsFalsyQueryStringValues() { $query = QueryString::fromString('0'); $this->assertEquals('0', (string) $query); $query->set('0', QueryString::BLANK); $this->assertSame('0', (string) $query); } public function testFromStringIgnoresQuestionMark() { $query = QueryString::fromString('foo=baz&bar=boo'); $this->assertEquals('foo=baz&bar=boo', (string) $query); } public function testConvertsPlusSymbolsToSpaces() { $query = QueryString::fromString('var=foo+bar'); $this->assertEquals('foo bar', $query->get('var')); } public function testFromStringDoesntMangleZeroes() { $query = QueryString::fromString('var=0'); $this->assertSame('0', $query->get('var')); } public function testAllowsZeroValues() { $query = new QueryString(array( 'foo' => 0, 'baz' => '0', 'bar' => null, 'boo' => false, 'bam' => '' )); $this->assertEquals('foo=0&baz=0&bar&boo&bam=', (string) $query); } public function testFromStringDoesntStripTrailingEquals() { $query = QueryString::fromString('data=mF0b3IiLCJUZWFtIERldiJdfX0='); $this->assertEquals('mF0b3IiLCJUZWFtIERldiJdfX0=', $query->get('data')); } public function testGuessesIfDuplicateAggregatorShouldBeUsed() { $query = QueryString::fromString('test=a&test=b'); $this->assertEquals('test=a&test=b', (string) $query); } public function testGuessesIfDuplicateAggregatorShouldBeUsedAndChecksForPhpStyle() { $query = QueryString::fromString('test[]=a&test[]=b'); $this->assertEquals('test%5B0%5D=a&test%5B1%5D=b', (string) $query); } } guzzle/tests/Guzzle/Tests/Http/QueryAggregator/PhpAggregatorTest.php 0000604 00000001374 15173213567 0021755 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\QueryString; use Guzzle\Http\QueryAggregator\PhpAggregator as Ag; class PhpAggregatorTest extends \Guzzle\Tests\GuzzleTestCase { public function testEncodes() { $query = new QueryString(); $query->useUrlEncoding(false); $a = new Ag(); $key = 't'; $value = array( 'v1' => 'a', 'v2' => 'b', 'v3' => array( 'v4' => 'c', 'v5' => 'd', ) ); $result = $a->aggregate($key, $value, $query); $this->assertEquals(array( 't[v1]' => 'a', 't[v2]' => 'b', 't[v3][v4]' => 'c', 't[v3][v5]' => 'd', ), $result); } } guzzle/tests/Guzzle/Tests/Http/QueryAggregator/CommaAggregatorTest.php 0000604 00000001540 15173213567 0022255 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\QueryString; use Guzzle\Http\QueryAggregator\CommaAggregator as Ag; class CommaAggregatorTest extends \Guzzle\Tests\GuzzleTestCase { public function testAggregates() { $query = new QueryString(); $a = new Ag(); $key = 'test 123'; $value = array('foo 123', 'baz', 'bar'); $result = $a->aggregate($key, $value, $query); $this->assertEquals(array('test%20123' => 'foo%20123,baz,bar'), $result); } public function testEncodes() { $query = new QueryString(); $query->useUrlEncoding(false); $a = new Ag(); $key = 'test 123'; $value = array('foo 123', 'baz', 'bar'); $result = $a->aggregate($key, $value, $query); $this->assertEquals(array('test 123' => 'foo 123,baz,bar'), $result); } } guzzle/tests/Guzzle/Tests/Http/QueryAggregator/DuplicateAggregatorTest.php 0000604 00000001560 15173213567 0023135 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\QueryString; use Guzzle\Http\QueryAggregator\DuplicateAggregator as Ag; class DuplicateAggregatorTest extends \Guzzle\Tests\GuzzleTestCase { public function testAggregates() { $query = new QueryString(); $a = new Ag(); $key = 'facet 1'; $value = array('size a', 'width b'); $result = $a->aggregate($key, $value, $query); $this->assertEquals(array('facet%201' => array('size%20a', 'width%20b')), $result); } public function testEncodes() { $query = new QueryString(); $query->useUrlEncoding(false); $a = new Ag(); $key = 'facet 1'; $value = array('size a', 'width b'); $result = $a->aggregate($key, $value, $query); $this->assertEquals(array('facet 1' => array('size a', 'width b')), $result); } } guzzle/tests/Guzzle/Tests/Http/IoEmittingEntityBodyTest.php 0000604 00000002542 15173213567 0020174 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\EntityBody; use Guzzle\Http\IoEmittingEntityBody; /** * @covers Guzzle\Http\IoEmittingEntityBody */ class IoEmittingEntityBodyTest extends \Guzzle\Tests\GuzzleTestCase { protected $body; protected $decorated; public function setUp() { $this->decorated = EntityBody::factory('hello'); $this->body = new IoEmittingEntityBody($this->decorated); } public function testEmitsReadEvents() { $e = null; $this->body->getEventDispatcher()->addListener('body.read', function ($event) use (&$e) { $e = $event; }); $this->assertEquals('hel', $this->body->read(3)); $this->assertEquals('hel', $e['read']); $this->assertEquals(3, $e['length']); $this->assertSame($this->body, $e['body']); } public function testEmitsWriteEvents() { $e = null; $this->body->getEventDispatcher()->addListener('body.write', function ($event) use (&$e) { $e = $event; }); $this->body->seek(0, SEEK_END); $this->assertEquals(5, $this->body->write('there')); $this->assertEquals('there', $e['write']); $this->assertEquals(5, $e['result']); $this->assertSame($this->body, $e['body']); $this->assertEquals('hellothere', (string) $this->body); } } guzzle/tests/Guzzle/Tests/Http/Exception/MultiTransferExceptionTest.php 0000604 00000003562 15173213567 0022530 0 ustar 00 <?php namespace Guzzle\Tests\Http\Exception; use Guzzle\Http\Exception\MultiTransferException; use Guzzle\Http\Curl\CurlHandle; use Guzzle\Http\Message\Request; /** * @covers Guzzle\Http\Exception\MultiTransferException */ class MultiTransferExceptionTest extends \Guzzle\Tests\GuzzleTestCase { public function testHasRequests() { $r1 = new Request('GET', 'http://www.foo.com'); $r2 = new Request('GET', 'http://www.foo.com'); $e = new MultiTransferException(); $e->addSuccessfulRequest($r1); $e->addFailedRequest($r2); $this->assertEquals(array($r1), $e->getSuccessfulRequests()); $this->assertEquals(array($r2), $e->getSuccessfulRequests()); $this->assertEquals(array($r1, $r2), $e->getAllRequests()); $this->assertTrue($e->containsRequest($r1)); $this->assertTrue($e->containsRequest($r2)); $this->assertFalse($e->containsRequest(new Request('POST', '/foo'))); } public function testCanSetRequests() { $s = array($r1 = new Request('GET', 'http://www.foo.com')); $f = array($r2 = new Request('GET', 'http://www.foo.com')); $e = new MultiTransferException(); $e->setSuccessfulRequests($s); $e->setFailedRequests($f); $this->assertEquals(array($r1), $e->getSuccessfulRequests()); $this->assertEquals(array($r2), $e->getSuccessfulRequests()); } public function testAssociatesExceptionsWithRequests() { $r1 = new Request('GET', 'http://www.foo.com'); $re1 = new \Exception('foo'); $re2 = new \Exception('bar'); $e = new MultiTransferException(); $e->add($re2); $e->addFailedRequestWithException($r1, $re1); $this->assertSame($re1, $e->getExceptionForFailedRequest($r1)); $this->assertNull($e->getExceptionForFailedRequest(new Request('POST', '/foo'))); } } guzzle/tests/Guzzle/Tests/Http/Exception/ExceptionTest.php 0000604 00000004201 15173213567 0017777 0 ustar 00 <?php namespace Guzzle\Tests\Http\Exception; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\Request; use Guzzle\Http\Exception\RequestException; use Guzzle\Http\Exception\BadResponseException; class ExceptionTest extends \Guzzle\Tests\GuzzleTestCase { /** * @covers Guzzle\Http\Exception\RequestException */ public function testRequestException() { $e = new RequestException('Message'); $request = new Request('GET', 'http://www.guzzle-project.com/'); $e->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); } } guzzle/tests/Guzzle/Tests/Http/Exception/CurlExceptionTest.php 0000604 00000001363 15173213567 0020633 0 ustar 00 <?php namespace Guzzle\Tests\Http\Exception; use Guzzle\Http\Exception\CurlException; use Guzzle\Http\Curl\CurlHandle; /** * @covers Guzzle\Http\Exception\CurlException */ class CurlExceptionTest extends \Guzzle\Tests\GuzzleTestCase { public function testStoresCurlError() { $e = new CurlException(); $this->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(); } } guzzle/tests/Guzzle/Tests/Http/ReadLimitEntityBodyTest.php 0000604 00000004356 15173213567 0020003 0 ustar 00 <?php namespace Guzzle\Tests\Http; use Guzzle\Http\EntityBody; use Guzzle\Http\ReadLimitEntityBody; /** * @covers Guzzle\Http\ReadLimitEntityBody */ class ReadLimitEntityBodyTest extends \Guzzle\Tests\GuzzleTestCase { /** @var ReadLimitEntityBody */ protected $body; /** @var EntityBody */ protected $decorated; public function setUp() { $this->decorated = EntityBody::factory(fopen(__FILE__, 'r')); $this->body = new ReadLimitEntityBody($this->decorated, 10, 3); } public function testReturnsSubsetWhenCastToString() { $body = EntityBody::factory('foo_baz_bar'); $limited = new ReadLimitEntityBody($body, 3, 4); $this->assertEquals('baz', (string) $limited); } public function testReturnsSubsetOfEmptyBodyWhenCastToString() { $body = EntityBody::factory(''); $limited = new ReadLimitEntityBody($body, 0, 10); $this->assertEquals('', (string) $limited); } public function testSeeksWhenConstructed() { $this->assertEquals(3, $this->body->ftell()); } public function testAllowsBoundedSeek() { $this->body->seek(100); $this->assertEquals(13, $this->body->ftell()); $this->body->seek(0); $this->assertEquals(3, $this->body->ftell()); $this->assertEquals(false, $this->body->seek(1000, SEEK_END)); } public function testReadsOnlySubsetOfData() { $data = $this->body->read(100); $this->assertEquals(10, strlen($data)); $this->assertFalse($this->body->read(1000)); $this->body->setOffset(10); $newData = $this->body->read(100); $this->assertEquals(10, strlen($newData)); $this->assertNotSame($data, $newData); } public function testClaimsConsumedWhenReadLimitIsReached() { $this->assertFalse($this->body->isConsumed()); $this->body->read(1000); $this->assertTrue($this->body->isConsumed()); } public function testContentLengthIsBounded() { $this->assertEquals(10, $this->body->getContentLength()); } public function testContentMd5IsBasedOnSubsection() { $this->assertNotSame($this->body->getContentMd5(), $this->decorated->getContentMd5()); } } guzzle/tests/Guzzle/Tests/Inflection/PreComputedInflectorTest.php 0000604 00000004033 15173213567 0021356 0 ustar 00 <?php namespace Guzzle\Tests\Inflection; use Guzzle\Inflection\PreComputedInflector; /** * @covers Guzzle\Inflection\PreComputedInflector */ class PreComputedInflectorTest extends \Guzzle\Tests\GuzzleTestCase { public function testUsesPreComputedHash() { $mock = $this->getMock('Guzzle\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\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\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')); } } guzzle/tests/Guzzle/Tests/Inflection/InflectorTest.php 0000604 00000002775 15173213567 0017221 0 ustar 00 <?php namespace Guzzle\Tests\Inflection; use Guzzle\Inflection\Inflector; /** * @covers Guzzle\Inflection\Inflector */ class InflectorTest extends \Guzzle\Tests\GuzzleTestCase { public function testReturnsDefaultInstance() { $this->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)); } } guzzle/tests/Guzzle/Tests/Inflection/MemoizingInflectorTest.php 0000604 00000003204 15173213567 0021064 0 ustar 00 <?php namespace Guzzle\Tests\Inflection; use Guzzle\Inflection\MemoizingInflector; use Guzzle\Inflection\Inflector; /** * @covers Guzzle\Inflection\MemoizingInflector */ class MemoizingInflectorTest extends \Guzzle\Tests\GuzzleTestCase { public function testUsesCache() { $mock = $this->getMock('Guzzle\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'])); } } guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieTest.php 0000604 00000017261 15173213567 0017056 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cookie; use Guzzle\Plugin\Cookie\Cookie; /** * @covers Guzzle\Plugin\Cookie\Cookie */ class CookieTest extends \Guzzle\Tests\GuzzleTestCase { public function testInitializesDefaultValues() { $cookie = new Cookie(); $this->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->assertTrue($cookie->matchesDomain('baz.com')); $cookie->setDomain('.127.0.0.1'); $this->assertTrue($cookie->matchesDomain('127.0.0.1')); $cookie->setDomain('127.0.0.1'); $this->assertTrue($cookie->matchesDomain('127.0.0.1')); $cookie->setDomain('.com.'); $this->assertFalse($cookie->matchesDomain('baz.com')); $cookie->setDomain('.local'); $this->assertTrue($cookie->matchesDomain('example.local')); } public function testMatchesPath() { $cookie = new Cookie(); $this->assertTrue($cookie->matchesPath('/foo')); $cookie->setPath('/foo'); // o The cookie-path and the request-path are identical. $this->assertTrue($cookie->matchesPath('/foo')); $this->assertFalse($cookie->matchesPath('/bar')); // o The cookie-path is a prefix of the request-path, and the first // character of the request-path that is not included in the cookie- // path is a %x2F ("/") character. $this->assertTrue($cookie->matchesPath('/foo/bar')); $this->assertFalse($cookie->matchesPath('/fooBar')); // o The cookie-path is a prefix of the request-path, and the last // character of the cookie-path is %x2F ("/"). $cookie->setPath('/foo/'); $this->assertTrue($cookie->matchesPath('/foo/bar')); $this->assertFalse($cookie->matchesPath('/fooBaz')); $this->assertFalse($cookie->matchesPath('/foo')); } public function cookieValidateProvider() { return array( array('foo', 'baz', 'bar', true), array('0', '0', '0', true), array('', 'baz', 'bar', 'The cookie name must not be empty'), array('foo', '', 'bar', 'The cookie value must not be empty'), array('foo', 'baz', '', 'The cookie domain must not be empty'), array('foo\\', 'baz', '0', 'The cookie name must not contain invalid characters: foo\\'), ); } /** * @dataProvider cookieValidateProvider */ public function testValidatesCookies($name, $value, $domain, $result) { $cookie = new Cookie(array( 'name' => $name, 'value' => $value, 'domain' => $domain )); $this->assertSame($result, $cookie->validate()); } public function testCreatesInvalidCharacterString() { $m = new \ReflectionMethod('Guzzle\Plugin\Cookie\Cookie', 'getInvalidCharacters'); $m->setAccessible(true); $p = new \ReflectionProperty('Guzzle\Plugin\Cookie\Cookie', 'invalidCharString'); $p->setAccessible(true); $p->setValue(''); // Expects a string containing 51 invalid characters $this->assertEquals(51, strlen($m->invoke($m))); $this->assertContains('@', $m->invoke($m)); } } guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookiePluginTest.php 0000604 00000010365 15173213567 0020233 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cookie; use Guzzle\Common\Event; use Guzzle\Plugin\Cookie\Cookie; use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; use Guzzle\Http\Client; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Cookie\CookiePlugin; /** * @group server * @covers Guzzle\Plugin\Cookie\CookiePlugin */ class CookiePluginTest extends \Guzzle\Tests\GuzzleTestCase { public function testExtractsAndStoresCookies() { $response = new Response(200); $mock = $this->getMockBuilder('Guzzle\Plugin\Cookie\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\Plugin\Cookie\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()->flush(); $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); $client->get()->send(); $request = $client->get(); $request->send(); $this->assertEquals('test=583551', $request->getHeader('Cookie')); $requests = $this->getServer()->getReceivedRequests(true); // Confirm subsequent requests have the cookie. $this->assertEquals('test=583551', $requests[2]->getHeader('Cookie')); // Confirm the redirected request has the cookie. $this->assertEquals('test=583551', $requests[1]->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()); } public function testEscapesCookieDomains() { $cookie = new Cookie(array('domain' => '/foo/^$[A-Z]+/')); $this->assertFalse($cookie->matchesDomain('foo')); } } guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/ArrayCookieJarTest.php 0000604 00000032605 15173213567 0022357 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cookie\CookieJar; use Guzzle\Plugin\Cookie\Cookie; use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\Request; /** * @covers Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar */ class ArrayCookieJarTest extends \Guzzle\Tests\GuzzleTestCase { /** * @var ArrayCookieJar */ private $jar; public function setUp() { $this->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->assertFalse($this->jar->add(new Cookie(array( 'name' => 'foo' )))); $this->assertFalse($this->jar->add(new Cookie(array( 'name' => false )))); $this->assertFalse($this->jar->add(new Cookie(array( 'name' => true )))); $this->assertFalse($this->jar->add(new Cookie(array( 'name' => 'foo', 'domain' => 'foo.com' )))); } public function testDoesAddValidCookies() { $this->assertTrue($this->jar->add(new Cookie(array( 'name' => 'foo', 'domain' => 'foo.com', 'value' => 0 )))); $this->assertTrue($this->jar->add(new Cookie(array( 'name' => 'foo', 'domain' => 'foo.com', 'value' => 0.0 )))); $this->assertTrue($this->jar->add(new Cookie(array( 'name' => 'foo', 'domain' => 'foo.com', 'value' => '0' )))); } 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()); } 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)); // Changing the value plus a parameter also must overwrite the existing one $data['value'] = 'zoo'; $data['secure'] = false; $this->assertTrue($this->jar->add(new Cookie($data))); $this->assertEquals(1, count($this->jar)); $c = $this->jar->all(); $this->assertEquals('zoo', $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;" )); $request = new Request('GET', 'http://www.example.com'); $this->jar->addCookiesFromResponse($response, $request); $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(0)), 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($cookies), count($results)); foreach ($cookies as $i) { $this->assertContains($bag[$i], $results); } } /** * @expectedException \Guzzle\Plugin\Cookie\Exception\InvalidCookieException * @expectedExceptionMessage The cookie name must not contain invalid characters: abc:@123 */ public function testThrowsExceptionWithStrictMode() { $a = new ArrayCookieJar(); $a->setStrictMode(true); $a->add(new Cookie(array( 'name' => 'abc:@123', 'value' => 'foo', 'domain' => 'bar' ))); } public function testRemoveExistingCookieIfEmpty() { // Add a cookie that should not be affected $a = new Cookie(array( 'name' => 'foo', 'value' => 'nope', 'domain' => 'foo.com', 'path' => '/abc' )); $this->jar->add($a); $data = array( 'name' => 'foo', 'value' => 'bar', 'domain' => 'foo.com', 'path' => '/' ); $b = new Cookie($data); $this->assertTrue($this->jar->add($b)); $this->assertEquals(2, count($this->jar)); // Try to re-set the same cookie with no value: assert that cookie is not added $data['value'] = null; $this->assertFalse($this->jar->add(new Cookie($data))); // assert that original cookie has been deleted $cookies = $this->jar->all('foo.com'); $this->assertTrue(in_array($a, $cookies, true)); $this->assertFalse(in_array($b, $cookies, true)); $this->assertEquals(1, count($this->jar)); } } guzzle/tests/Guzzle/Tests/Plugin/Cookie/CookieJar/FileCookieJarTest.php 0000604 00000003151 15173213567 0022152 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cookie\CookieJar; use Guzzle\Plugin\Cookie\Cookie; use Guzzle\Plugin\Cookie\CookieJar\FileCookieJar; /** * @covers Guzzle\Plugin\Cookie\CookieJar\FileCookieJar */ class FileCookieJarTest extends \Guzzle\Tests\GuzzleTestCase { private $file; public function setUp() { $this->file = tempnam('/tmp', 'file-cookies'); } public function testLoadsFromFileFile() { $jar = new FileCookieJar($this->file); $this->assertEquals(array(), $jar->all()); unlink($this->file); } 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); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/CallbackBackoffStrategyTest.php 0000604 00000002111 15173213567 0022446 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Plugin\Backoff\CallbackBackoffStrategy; /** * @covers Guzzle\Plugin\Backoff\CallbackBackoffStrategy */ class CallbackBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testEnsuresIsCallable() { $strategy = new CallbackBackoffStrategy(new \stdClass(), true); } public function testRetriesWithCallable() { $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $strategy = new CallbackBackoffStrategy(function () { return 10; }, true); $this->assertTrue($strategy->makesDecision()); $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request)); // Ensure it chains correctly when null is returned $strategy = new CallbackBackoffStrategy(function () { return null; }, false); $this->assertFalse($strategy->makesDecision()); $this->assertFalse($strategy->getBackoffPeriod(0, $request)); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/AbstractBackoffStrategyTest.php 0000604 00000006442 15173213567 0022530 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Http\Message\Request; use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy; use Guzzle\Plugin\Backoff\CallbackBackoffStrategy; /** * @covers Guzzle\Plugin\Backoff\AbstractBackoffStrategy */ class AbstractBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { protected function getMockStrategy() { return $this->getMockBuilder('Guzzle\Plugin\Backoff\AbstractBackoffStrategy') ->setMethods(array('getDelay', 'makesDecision')) ->getMockForAbstractClass(); } public function testReturnsZeroWhenNoNextAndGotNull() { $request = new Request('GET', 'http://www.foo.com'); $mock = $this->getMockStrategy(); $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); $this->assertEquals(0, $mock->getBackoffPeriod(0, $request)); } public function testReturnsFalse() { $request = new Request('GET', 'http://www.foo.com'); $mock = $this->getMockStrategy(); $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(false)); $this->assertEquals(false, $mock->getBackoffPeriod(0, $request)); } public function testReturnsNextValueWhenNullOrTrue() { $request = new Request('GET', 'http://www.foo.com'); $mock = $this->getMockStrategy(); $mock->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(null)); $mock->expects($this->any())->method('makesDecision')->will($this->returnValue(false)); $mock2 = $this->getMockStrategy(); $mock2->expects($this->atLeastOnce())->method('getDelay')->will($this->returnValue(10)); $mock2->expects($this->atLeastOnce())->method('makesDecision')->will($this->returnValue(true)); $mock->setNext($mock2); $this->assertEquals(10, $mock->getBackoffPeriod(0, $request)); } public function testReturnsFalseWhenNullAndNoNext() { $request = new Request('GET', 'http://www.foo.com'); $s = new TruncatedBackoffStrategy(2); $this->assertFalse($s->getBackoffPeriod(0, $request)); } public function testHasNext() { $a = new TruncatedBackoffStrategy(2); $b = new TruncatedBackoffStrategy(2); $a->setNext($b); $this->assertSame($b, $a->getNext()); } public function testSkipsOtherDecisionsInChainWhenOneReturnsTrue() { $a = new CallbackBackoffStrategy(function () { return null; }, true); $b = new CallbackBackoffStrategy(function () { return true; }, true); $c = new CallbackBackoffStrategy(function () { return null; }, true); $d = new CallbackBackoffStrategy(function () { return 10; }, false); $a->setNext($b); $b->setNext($c); $c->setNext($d); $this->assertEquals(10, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); } public function testReturnsZeroWhenDecisionMakerReturnsTrueButNoFurtherStrategiesAreInTheChain() { $a = new CallbackBackoffStrategy(function () { return null; }, true); $b = new CallbackBackoffStrategy(function () { return true; }, true); $a->setNext($b); $this->assertSame(0, $a->getBackoffPeriod(2, new Request('GET', 'http://www.foo.com'))); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/TruncatedBackoffStrategyTest.php 0000604 00000002321 15173213567 0022706 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy; use Guzzle\Plugin\Backoff\HttpBackoffStrategy; use Guzzle\Plugin\Backoff\ConstantBackoffStrategy; /** * @covers Guzzle\Plugin\Backoff\TruncatedBackoffStrategy */ class TruncatedBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testRetriesWhenLessThanMax() { $strategy = new TruncatedBackoffStrategy(2); $this->assertTrue($strategy->makesDecision()); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $this->assertFalse($strategy->getBackoffPeriod(0, $request)); $this->assertFalse($strategy->getBackoffPeriod(1, $request)); $this->assertFalse($strategy->getBackoffPeriod(2, $request)); $response = new Response(500); $strategy->setNext(new HttpBackoffStrategy(null, new ConstantBackoffStrategy(10))); $this->assertEquals(10, $strategy->getBackoffPeriod(0, $request, $response)); $this->assertEquals(10, $strategy->getBackoffPeriod(1, $request, $response)); $this->assertFalse($strategy->getBackoffPeriod(2, $request, $response)); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/ConstantBackoffStrategyTest.php 0000604 00000001223 15173213567 0022546 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Plugin\Backoff\ConstantBackoffStrategy; /** * @covers Guzzle\Plugin\Backoff\ConstantBackoffStrategy */ class ConstantBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testRetriesWithConstantDelay() { $strategy = new ConstantBackoffStrategy(3.5); $this->assertFalse($strategy->makesDecision()); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $this->assertEquals(3.5, $strategy->getBackoffPeriod(0, $request)); $this->assertEquals(3.5, $strategy->getBackoffPeriod(1, $request)); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/LinearBackoffStrategyTest.php 0000604 00000001316 15173213567 0022172 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Plugin\Backoff\LinearBackoffStrategy; /** * @covers Guzzle\Plugin\Backoff\LinearBackoffStrategy */ class LinearBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testRetriesWithLinearDelay() { $strategy = new LinearBackoffStrategy(5); $this->assertFalse($strategy->makesDecision()); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request)); $this->assertEquals(5, $strategy->getBackoffPeriod(1, $request)); $this->assertEquals(10, $strategy->getBackoffPeriod(2, $request)); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/CurlBackoffStrategyTest.php 0000604 00000002542 15173213567 0021667 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Backoff\CurlBackoffStrategy; use Guzzle\Http\Exception\CurlException; /** * @covers Guzzle\Plugin\Backoff\CurlBackoffStrategy * @covers Guzzle\Plugin\Backoff\AbstractErrorCodeBackoffStrategy */ class CurlBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testRetriesWithExponentialDelay() { $this->assertNotEmpty(CurlBackoffStrategy::getDefaultFailureCodes()); $strategy = new CurlBackoffStrategy(); $this->assertTrue($strategy->makesDecision()); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $e = new CurlException(); $e->setError('foo', CURLE_BAD_CALLING_ORDER); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, null, $e)); foreach (CurlBackoffStrategy::getDefaultFailureCodes() as $code) { $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, null, $e->setError('foo', $code))); } } public function testIgnoresNonErrors() { $strategy = new CurlBackoffStrategy(); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, new Response(200))); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/ExponentialBackoffStrategyTest.php 0000604 00000001572 15173213567 0023252 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Plugin\Backoff\ExponentialBackoffStrategy; /** * @covers Guzzle\Plugin\Backoff\ExponentialBackoffStrategy */ class ExponentialBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testRetriesWithExponentialDelay() { $strategy = new ExponentialBackoffStrategy(); $this->assertFalse($strategy->makesDecision()); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $this->assertEquals(1, $strategy->getBackoffPeriod(0, $request)); $this->assertEquals(2, $strategy->getBackoffPeriod(1, $request)); $this->assertEquals(4, $strategy->getBackoffPeriod(2, $request)); $this->assertEquals(8, $strategy->getBackoffPeriod(3, $request)); $this->assertEquals(16, $strategy->getBackoffPeriod(4, $request)); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/HttpBackoffStrategyTest.php 0000604 00000003462 15173213567 0021703 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Plugin\Backoff\HttpBackoffStrategy; use Guzzle\Http\Message\Response; /** * @covers Guzzle\Plugin\Backoff\HttpBackoffStrategy * @covers Guzzle\Plugin\Backoff\AbstractErrorCodeBackoffStrategy */ class HttpBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testRetriesWhenCodeMatches() { $this->assertNotEmpty(HttpBackoffStrategy::getDefaultFailureCodes()); $strategy = new HttpBackoffStrategy(); $this->assertTrue($strategy->makesDecision()); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $response = new Response(200); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); $response->setStatus(400); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); foreach (HttpBackoffStrategy::getDefaultFailureCodes() as $code) { $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response->setStatus($code))); } } public function testAllowsCustomCodes() { $strategy = new HttpBackoffStrategy(array(204)); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $response = new Response(204); $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); $response->setStatus(500); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); } public function testIgnoresNonErrors() { $strategy = new HttpBackoffStrategy(); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/ReasonPhraseBackoffStrategyTest.php 0000604 00000002374 15173213567 0023357 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Plugin\Backoff\ReasonPhraseBackoffStrategy; use Guzzle\Http\Message\Response; /** * @covers Guzzle\Plugin\Backoff\ReasonPhraseBackoffStrategy * @covers Guzzle\Plugin\Backoff\AbstractErrorCodeBackoffStrategy */ class ReasonPhraseBackoffStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testRetriesWhenCodeMatches() { $this->assertEmpty(ReasonPhraseBackoffStrategy::getDefaultFailureCodes()); $strategy = new ReasonPhraseBackoffStrategy(array('Foo', 'Internal Server Error')); $this->assertTrue($strategy->makesDecision()); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $response = new Response(200); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request, $response)); $response->setStatus(200, 'Foo'); $this->assertEquals(0, $strategy->getBackoffPeriod(0, $request, $response)); } public function testIgnoresNonErrors() { $strategy = new ReasonPhraseBackoffStrategy(); $request = $this->getMock('Guzzle\Http\Message\Request', array(), array(), '', false); $this->assertEquals(false, $strategy->getBackoffPeriod(0, $request)); } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffLoggerTest.php 0000604 00000005735 15173213567 0020465 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Common\Event; use Guzzle\Log\ClosureLogAdapter; use Guzzle\Http\Curl\CurlHandle; use Guzzle\Plugin\Backoff\BackoffLogger; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\RequestFactory; /** * @covers Guzzle\Plugin\Backoff\BackoffLogger */ class BackoffLoggerTest extends \Guzzle\Tests\GuzzleTestCase { public $message; public function setUp() { $this->message = ''; } public function testHasEventList() { $this->assertEquals(1, count(BackoffLogger::getSubscribedEvents())); } public function testLogsEvents() { list($logPlugin, $request, $response) = $this->getMocks(); $response = $this->getMockBuilder('Guzzle\Http\Message\Response') ->setConstructorArgs(array(503)) ->setMethods(array('getInfo')) ->getMock(); $response->expects($this->any()) ->method('getInfo') ->will($this->returnValue(2)); $handle = $this->getMockHandle(); $event = new Event(array( 'request' => $request, 'response' => $response, 'retries' => 1, 'delay' => 3, 'handle' => $handle )); $logPlugin->onRequestRetry($event); $this->assertContains( '] PUT http://www.example.com - 503 Service Unavailable - Retries: 1, Delay: 3, Time: 2, 2, cURL: 30 Foo', $this->message ); } public function testCanSetTemplate() { $l = new BackoffLogger(new ClosureLogAdapter(function () {})); $l->setTemplate('foo'); $t = $this->readAttribute($l, 'formatter'); $this->assertEquals('foo', $this->readAttribute($t, 'template')); } /** * @return array */ protected function getMocks() { $that = $this; $logger = new ClosureLogAdapter(function ($message) use ($that) { $that->message .= $message . "\n"; }); $logPlugin = new BackoffLogger($logger); $response = new Response(503); $request = RequestFactory::getInstance()->create('PUT', 'http://www.example.com', array( 'Content-Length' => 3, 'Foo' => 'Bar' )); return array($logPlugin, $request, $response); } /** * @return CurlHandle */ protected function getMockHandle() { $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') ->disableOriginalConstructor() ->setMethods(array('getError', 'getErrorNo', 'getInfo')) ->getMock(); $handle->expects($this->once()) ->method('getError') ->will($this->returnValue('Foo')); $handle->expects($this->once()) ->method('getErrorNo') ->will($this->returnValue(30)); $handle->expects($this->any()) ->method('getInfo') ->will($this->returnValue(2)); return $handle; } } guzzle/tests/Guzzle/Tests/Plugin/Backoff/BackoffPluginTest.php 0000604 00000026700 15173213567 0020477 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Backoff; use Guzzle\Common\Event; use Guzzle\Http\Exception\CurlException; use Guzzle\Http\Client; use Guzzle\Plugin\Backoff\BackoffPlugin; use Guzzle\Http\Message\RequestInterface; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\EntityEnclosingRequest; use Guzzle\Http\Message\Response; use Guzzle\Http\Curl\CurlMulti; use Guzzle\Http\Curl\CurlMultiInterface; use Guzzle\Plugin\Backoff\ConstantBackoffStrategy; use Guzzle\Plugin\Backoff\CurlBackoffStrategy; use Guzzle\Plugin\Backoff\HttpBackoffStrategy; use Guzzle\Plugin\Backoff\TruncatedBackoffStrategy; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * @group server * @covers Guzzle\Plugin\Backoff\BackoffPlugin */ class BackoffPluginTest extends \Guzzle\Tests\GuzzleTestCase implements EventSubscriberInterface { protected $retried; public function setUp() { $this->retried = false; } public static function getSubscribedEvents() { return array(BackoffPlugin::RETRY_EVENT => 'onRequestRetry'); } public function onRequestRetry(Event $event) { $this->retried = $event; } public function testHasEventList() { $this->assertEquals(1, count(BackoffPlugin::getAllEvents())); } public function testCreatesDefaultExponentialBackoffPlugin() { $plugin = BackoffPlugin::getExponentialBackoff(3, array(204), array(10)); $this->assertInstanceOf('Guzzle\Plugin\Backoff\BackoffPlugin', $plugin); $strategy = $this->readAttribute($plugin, 'strategy'); $this->assertInstanceOf('Guzzle\Plugin\Backoff\TruncatedBackoffStrategy', $strategy); $this->assertEquals(3, $this->readAttribute($strategy, 'max')); $strategy = $this->readAttribute($strategy, 'next'); $this->assertInstanceOf('Guzzle\Plugin\Backoff\HttpBackoffStrategy', $strategy); $this->assertEquals(array(204 => true), $this->readAttribute($strategy, 'errorCodes')); $strategy = $this->readAttribute($strategy, 'next'); $this->assertInstanceOf('Guzzle\Plugin\Backoff\CurlBackoffStrategy', $strategy); $this->assertEquals(array(10 => true), $this->readAttribute($strategy, 'errorCodes')); $strategy = $this->readAttribute($strategy, 'next'); $this->assertInstanceOf('Guzzle\Plugin\Backoff\ExponentialBackoffStrategy', $strategy); } public function testDoesNotRetryUnlessStrategyReturnsNumber() { $request = new Request('GET', 'http://www.example.com'); $request->setState('transfer'); $mock = $this->getMockBuilder('Guzzle\Plugin\Backoff\BackoffStrategyInterface') ->setMethods(array('getBackoffPeriod')) ->getMockForAbstractClass(); $mock->expects($this->once()) ->method('getBackoffPeriod') ->will($this->returnValue(false)); $plugin = new BackoffPlugin($mock); $plugin->addSubscriber($this); $plugin->onRequestSent(new Event(array('request' => $request))); $this->assertFalse($this->retried); } public function testUpdatesRequestForRetry() { $request = new Request('GET', 'http://www.example.com'); $request->setState('transfer'); $response = new Response(500); $handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle')->disableOriginalConstructor()->getMock(); $e = new CurlException(); $e->setCurlHandle($handle); $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); $plugin->addSubscriber($this); $event = new Event(array( 'request' => $request, 'response' => $response, 'exception' => $e )); $plugin->onRequestSent($event); $this->assertEquals(array( 'request' => $request, 'response' => $response, 'handle' => $handle, 'retries' => 1, 'delay' => 10 ), $this->readAttribute($this->retried, 'context')); $plugin->onRequestSent($event); $this->assertEquals(array( 'request' => $request, 'response' => $response, 'handle' => $handle, 'retries' => 2, 'delay' => 10 ), $this->readAttribute($this->retried, 'context')); } public function testDoesNothingWhenNotRetryingAndPollingRequest() { $request = new Request('GET', 'http://www.foo.com'); $plugin = new BackoffPlugin(new ConstantBackoffStrategy(10)); $plugin->onRequestPoll(new Event(array('request' => $request))); } 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 BackoffPlugin( new TruncatedBackoffStrategy(3, new HttpBackoffStrategy(null, new CurlBackoffStrategy(null, new ConstantBackoffStrategy(0.05) ) ) ) ); $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))); $this->assertEquals(2, $request->getParams()->get(BackoffPlugin::RETRY_PARAM)); } /** * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException */ public function testFailsOnTruncation() { $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" )); $plugin = new BackoffPlugin( new TruncatedBackoffStrategy(2, new HttpBackoffStrategy(null, new ConstantBackoffStrategy(0.05) ) ) ); $client = new Client($this->getServer()->getUrl()); $client->addSubscriber($plugin); $client->get()->send(); } public function testRetriesRequestsWhenInParallel() { // 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 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", "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", "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", "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\ndata", "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\ndata" )); $plugin = new BackoffPlugin( new HttpBackoffStrategy(null, new TruncatedBackoffStrategy(3, new CurlBackoffStrategy(null, new ConstantBackoffStrategy(0.1) ) ) ) ); $client = new Client($this->getServer()->getUrl()); $client->getEventDispatcher()->addSubscriber($plugin); $requests = array(); for ($i = 0; $i < 5; $i++) { $requests[] = $client->get(); } $client->send($requests); $this->assertEquals(15, count($this->getServer()->getReceivedRequests(false))); } /** * @covers Guzzle\Plugin\Backoff\BackoffPlugin * @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 some time ensure that the polling works correctly in the observer $plugin = new BackoffPlugin(new HttpBackoffStrategy(null, new TruncatedBackoffStrategy(1, new ConstantBackoffStrategy(0.5)))); $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))); } public function testSeeksToBeginningOfRequestBodyWhenRetrying() { // 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(BackoffPlugin::DELAY_PARAM, 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 BackoffPlugin(new ConstantBackoffStrategy(0)); $plugin->onRequestPoll($this->getMockEvent($request)); // Ensure that the stream was seeked to 0 $this->assertEquals('a', $request->getBody()->read(1)); } public function testDoesNotSeekOnRequestsWithNoBodyWhenRetrying() { // Create a request with a body $request = new EntityEnclosingRequest('PUT', 'http://www.example.com'); $request->getParams()->set(BackoffPlugin::DELAY_PARAM, 2); $plugin = new BackoffPlugin(new ConstantBackoffStrategy(0)); $plugin->onRequestPoll($this->getMockEvent($request)); } protected function getMockEvent(RequestInterface $request) { // Create a mock curl multi object $multi = $this->getMockBuilder('Guzzle\Http\Curl\CurlMulti') ->setMethods(array('remove', 'add')) ->getMock(); // Create an event that is expected for the Poll event $event = new Event(array( 'request' => $request, 'curl_multi' => $multi )); $event->setName(CurlMultiInterface::POLLING_REQUEST); return $event; } } guzzle/tests/Guzzle/Tests/Plugin/Async/AsyncPluginTest.php 0000604 00000006204 15173213567 0017740 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Async; use Guzzle\Plugin\Async\AsyncPlugin; use Guzzle\Http\Message\RequestInterface; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Curl\CurlHandle; use Guzzle\Http\Exception\CurlException; use Guzzle\Common\Event; use Guzzle\Http\Client; /** * @covers Guzzle\Plugin\Async\AsyncPlugin */ class AsyncPluginTest extends \Guzzle\Tests\GuzzleTestCase { public function testSubscribesToEvents() { $events = AsyncPlugin::getSubscribedEvents(); $this->assertArrayHasKey('request.before_send', $events); $this->assertArrayHasKey('request.exception', $events); $this->assertArrayHasKey('curl.callback.progress', $events); } 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')); } 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->getHandle(), 'uploaded' => 10, 'upload_size' => 10, 'downloaded' => 0 )); $p->onCurlProgress($event); } public function testEnsuresRequestIsSet() { $p = new AsyncPlugin(); $event = new Event(array( 'uploaded' => 10, 'upload_size' => 10, 'downloaded' => 0 )); $p->onCurlProgress($event); } 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()->flush(); $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')); $received = $this->getServer()->getReceivedRequests(true); $this->assertEquals('POST', $received[0]->getMethod()); } } guzzle/tests/Guzzle/Tests/Plugin/Cache/SkipRevalidationTest.php 0000604 00000000745 15173213567 0020706 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cache; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Cache\SkipRevalidation; /** * @covers Guzzle\Plugin\Cache\SkipRevalidation */ class SkipRevalidationTest extends \Guzzle\Tests\GuzzleTestCase { public function testSkipsRequestRevalidation() { $skip = new SkipRevalidation(); $this->assertTrue($skip->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); } } guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCacheStorageTest.php 0000604 00000016503 15173213567 0021272 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cache; use Guzzle\Cache\DoctrineCacheAdapter; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Cache\DefaultCacheStorage; use Doctrine\Common\Cache\ArrayCache; /** * @covers Guzzle\Plugin\Cache\DefaultCacheStorage */ class DefaultCacheStorageTest extends \Guzzle\Tests\GuzzleTestCase { protected function getCache() { $a = new ArrayCache(); $c = new DoctrineCacheAdapter($a); $s = new DefaultCacheStorage($c); $request = new Request('GET', 'http://foo.com', array('Accept' => 'application/json')); $response = new Response(200, array( 'Content-Type' => 'application/json', 'Connection' => 'close', 'X-Foo' => 'Bar', 'Vary' => 'Accept' ), 'test'); $s->cache($request, $response); $data = $this->readAttribute($a, 'data'); return array( 'cache' => $a, 'adapter' => $c, 'storage' => $s, 'request' => $request, 'response' => $response, 'serialized' => end($data) ); } public function testReturnsNullForCacheMiss() { $cache = $this->getCache(); $this->assertNull($cache['storage']->fetch(new Request('GET', 'http://test.com'))); } public function testCachesRequests() { $cache = $this->getCache(); $foundRequest = $foundBody = $bodyKey = false; foreach ($this->readAttribute($cache['cache'], 'data') as $key => $v) { if (strpos($v, 'foo.com')) { $foundRequest = true; $data = unserialize($v); $bodyKey = $data[0][3]; $this->assertInternalType('integer', $data[0][4]); $this->assertFalse(isset($data[0][0]['connection'])); $this->assertEquals('foo.com', $data[0][0]['host']); } elseif ($v == 'test') { $foundBody = $key; } } $this->assertContains($bodyKey, $foundBody); $this->assertTrue($foundRequest); } public function testFetchesResponse() { $cache = $this->getCache(); $response = $cache['storage']->fetch($cache['request']); $this->assertEquals(200, $response->getStatusCode()); $this->assertFalse($response->hasHeader('Connection')); $this->assertEquals('Bar', (string) $response->getHeader('X-Foo')); $this->assertEquals('test', (string) $response->getBody()); $this->assertTrue(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); } public function testDeletesRequestItemsAndBody() { $cache = $this->getCache(); $cache['storage']->delete($cache['request']); $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); } public function testCachesMultipleRequestsWithVary() { $cache = $this->getCache(); $cache['request']->setHeader('Accept', 'application/xml'); $response = $cache['response']->setHeader('Content-Type', 'application/xml'); $response->setBody('123'); $cache['storage']->cache($cache['request'], $response); $data = $this->readAttribute($cache['cache'], 'data'); foreach ($data as $v) { if (strpos($v, 'foo.com')) { $u = unserialize($v); $this->assertEquals(2, count($u)); $this->assertEquals($u[0][0]['accept'], 'application/xml'); $this->assertEquals($u[0][1]['content-type'], 'application/xml'); $this->assertEquals($u[1][0]['accept'], 'application/json'); $this->assertEquals($u[1][1]['content-type'], 'application/json'); $this->assertNotSame($u[0][3], $u[1][3]); break; } } } public function testPurgeRemovesAllMethodCaches() { $cache = $this->getCache(); foreach (array('HEAD', 'POST', 'PUT', 'DELETE') as $method) { $request = RequestFactory::getInstance()->cloneRequestWithMethod($cache['request'], $method); $cache['storage']->cache($request, $cache['response']); } $cache['storage']->purge('http://foo.com'); $this->assertFalse(in_array('test', $this->readAttribute($cache['cache'], 'data'))); $this->assertFalse(in_array($cache['serialized'], $this->readAttribute($cache['cache'], 'data'))); $this->assertEquals( array('DoctrineNamespaceCacheKey[]'), array_keys($this->readAttribute($cache['cache'], 'data')) ); } public function testRemovesExpiredResponses() { $cache = $this->getCache(); $request = new Request('GET', 'http://xyz.com'); $response = new Response(200, array('Age' => 1000, 'Cache-Control' => 'max-age=-10000')); $cache['storage']->cache($request, $response); $this->assertNull($cache['storage']->fetch($request)); $data = $this->readAttribute($cache['cache'], 'data'); $this->assertFalse(in_array('xyz.com', $data)); $this->assertTrue(in_array($cache['serialized'], $data)); } public function testUsesVaryToDetermineResult() { $cache = $this->getCache(); $this->assertInstanceOf('Guzzle\Http\Message\Response', $cache['storage']->fetch($cache['request'])); $request = new Request('GET', 'http://foo.com', array('Accept' => 'application/xml')); $this->assertNull($cache['storage']->fetch($request)); } public function testEnsuresResponseIsStillPresent() { $cache = $this->getCache(); $data = $this->readAttribute($cache['cache'], 'data'); $key = array_search('test', $data); $cache['cache']->delete(substr($key, 1, -4)); $this->assertNull($cache['storage']->fetch($cache['request'])); } public function staleProvider() { return array( array( new Request('GET', 'http://foo.com', array('Accept' => 'foo')), new Response(200, array('Cache-Control' => 'stale-if-error=100', 'Vary' => 'Accept')) ), array( new Request('GET', 'http://foo.com', array('Accept' => 'foo')), new Response(200, array('Cache-Control' => 'stale-if-error', 'Vary' => 'Accept')) ) ); } /** * @dataProvider staleProvider */ public function testUsesStaleTimeDirectiveForTtd($request, $response) { $cache = $this->getCache(); $cache['storage']->cache($request, $response); $data = $this->readAttribute($cache['cache'], 'data'); foreach ($data as $v) { if (strpos($v, 'foo.com')) { $u = unserialize($v); $this->assertGreaterThan($u[1][4], $u[0][4]); break; } } } public function testCanFilterCacheKeys() { $cache = $this->getCache(); $cache['request']->getQuery()->set('auth', 'foo'); $this->assertNull($cache['storage']->fetch($cache['request'])); $cache['request']->getParams()->set('cache.key_filter', 'auth'); $this->assertNotNull($cache['storage']->fetch($cache['request'])); } } guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultCanCacheStrategyTest.php 0000604 00000002417 15173213567 0022111 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cache; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Cache\DefaultCanCacheStrategy; /** * @covers Guzzle\Plugin\Cache\DefaultCanCacheStrategy */ class DefaultCanCacheStrategyTest extends \Guzzle\Tests\GuzzleTestCase { public function testReturnsRequestcanCacheRequest() { $strategy = new DefaultCanCacheStrategy(); $request = new Request('GET', 'http://foo.com'); $this->assertTrue($strategy->canCacheRequest($request)); } public function testDoesNotCacheNoStore() { $strategy = new DefaultCanCacheStrategy(); $request = new Request('GET', 'http://foo.com', array('cache-control' => 'no-store')); $this->assertFalse($strategy->canCacheRequest($request)); } public function testCanCacheResponse() { $response = $this->getMockBuilder('Guzzle\Http\Message\Response') ->setMethods(array('canCache')) ->setConstructorArgs(array(200)) ->getMock(); $response->expects($this->once()) ->method('canCache') ->will($this->returnValue(true)); $strategy = new DefaultCanCacheStrategy(); $this->assertTrue($strategy->canCacheResponse($response)); } } guzzle/tests/Guzzle/Tests/Plugin/Cache/CachePluginTest.php 0000604 00000050411 15173213567 0017613 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cache; use Guzzle\Common\Event; use Guzzle\Common\Version; use Guzzle\Cache\DoctrineCacheAdapter; use Guzzle\Http\Client; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Cache\CachePlugin; use Guzzle\Plugin\Cache\DefaultCacheStorage; use Guzzle\Plugin\Cache\CallbackCanCacheStrategy; use Doctrine\Common\Cache\ArrayCache; /** * @group server * @covers Guzzle\Plugin\Cache\CachePlugin * @covers Guzzle\Plugin\Cache\DefaultRevalidation */ class CachePluginTest extends \Guzzle\Tests\GuzzleTestCase { public function testAddsDefaultStorage() { $plugin = new CachePlugin(); $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); } public function testAddsDefaultCollaborators() { $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); $plugin = new CachePlugin(array( 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass() )); $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); $this->assertInstanceOf( 'Guzzle\Plugin\Cache\CanCacheStrategyInterface', $this->readAttribute($plugin, 'canCache') ); $this->assertInstanceOf( 'Guzzle\Plugin\Cache\RevalidationInterface', $this->readAttribute($plugin, 'revalidation') ); } public function testAddsCallbackCollaborators() { $this->assertNotEmpty(CachePlugin::getSubscribedEvents()); $plugin = new CachePlugin(array('can_cache' => function () {})); $this->assertInstanceOf( 'Guzzle\Plugin\Cache\CallbackCanCacheStrategy', $this->readAttribute($plugin, 'canCache') ); } public function testCanPassCacheAsOnlyArgumentToConstructor() { $p = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); $p = new CachePlugin(new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache()))); } public function testUsesCreatedCacheStorage() { $plugin = new CachePlugin(array( 'adapter' => $this->getMockBuilder('Guzzle\Cache\CacheAdapterInterface')->getMockForAbstractClass() )); $this->assertInstanceOf('Guzzle\Plugin\Cache\CacheStorageInterface', $this->readAttribute($plugin, 'storage')); } public function testUsesProvidedOptions() { $can = $this->getMockBuilder('Guzzle\Plugin\Cache\CanCacheStrategyInterface')->getMockForAbstractClass(); $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\RevalidationInterface')->getMockForAbstractClass(); $plugin = new CachePlugin(array( 'storage' => $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(), 'can_cache' => $can, 'revalidation' => $revalidate )); $this->assertSame($can, $this->readAttribute($plugin, 'canCache')); $this->assertSame($revalidate, $this->readAttribute($plugin, 'revalidation')); } public function satisfyProvider() { $req1 = new Request('GET', 'http://foo.com', array('Cache-Control' => 'no-cache')); return array( // The response is too old to satisfy the request array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-age=20')), new Response(200, array('Age' => 100)), false, false), // The response cannot satisfy the request because it is stale array(new Request('GET', 'http://foo.com'), new Response(200, array('Cache-Control' => 'max-age=10', 'Age' => 100)), false, false), // Allows the expired response to satisfy the request because of the max-stale array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=15')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), true, false), // Max stale is > than the allowed staleness array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale=5')), new Response(200, array('Cache-Control' => 'max-age=90', 'Age' => 100)), false, false), // Performs cache revalidation array($req1, new Response(200), true, true), // Performs revalidation due to ETag on the response and no cache-control on the request array(new Request('GET', 'http://foo.com'), new Response(200, array( 'ETag' => 'ABC', 'Expires' => date('c', strtotime('+1 year')) )), true, true), ); } /** * @dataProvider satisfyProvider */ public function testChecksIfResponseCanSatisfyRequest($request, $response, $can, $revalidates) { $didRevalidate = false; $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface')->getMockForAbstractClass(); $revalidate = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') ->setMethods(array('revalidate')) ->setConstructorArgs(array($storage)) ->getMockForAbstractClass(); $revalidate->expects($this->any()) ->method('revalidate') ->will($this->returnCallback(function () use (&$didRevalidate) { $didRevalidate = true; return true; })); $plugin = new CachePlugin(array( 'storage' => $storage, 'revalidation' => $revalidate )); $this->assertEquals($can, $plugin->canResponseSatisfyRequest($request, $response)); $this->assertEquals($didRevalidate, $revalidates); } public function satisfyFailedProvider() { return array( // Neither has stale-if-error array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100)), false), // Request has stale-if-error array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), // Request has valid stale-if-error array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), true), // Request has expired stale-if-error array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50')), false), // Response has permanent stale-if-error array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error', )), true), // Response has valid stale-if-error array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), true), // Response has expired stale-if-error array(new Request('GET', 'http://foo.com', array()), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), // Request has valid stale-if-error but response does not array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=50')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=20')), false), // Response has valid stale-if-error but request does not array(new Request('GET', 'http://foo.com', array('Cache-Control' => 'stale-if-error=20')), new Response(200, array('Age' => 100, 'Cache-Control' => 'max-age=50, stale-if-error=50')), false), ); } /** * @dataProvider satisfyFailedProvider */ public function testChecksIfResponseCanSatisfyFailedRequest($request, $response, $can) { $plugin = new CachePlugin(); $this->assertEquals($can, $plugin->canResponseSatisfyFailedRequest($request, $response)); } public function testDoesNothingWhenRequestIsNotCacheable() { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('fetch')) ->getMockForAbstractClass(); $storage->expects($this->never())->method('fetch'); $plugin = new CachePlugin(array( 'storage' => $storage, 'can_cache' => new CallbackCanCacheStrategy(function () { return false; }) )); $plugin->onRequestBeforeSend(new Event(array( 'request' => new Request('GET', 'http://foo.com') ))); } public function satisfiableProvider() { $date = new \DateTime('-10 seconds'); return array( // Fresh response array(new Response(200, array(), 'foo')), // Stale response array(new Response(200, array('Date' => $date->format('c'), 'Cache-Control' => 'max-age=5'), 'foo')) ); } /** * @dataProvider satisfiableProvider */ public function testInjectsSatisfiableResponses($response) { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('fetch')) ->getMockForAbstractClass(); $storage->expects($this->once())->method('fetch')->will($this->returnValue($response)); $plugin = new CachePlugin(array('storage' => $storage)); $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); $plugin->onRequestBeforeSend(new Event(array('request' => $request))); $plugin->onRequestSent(new Event(array('request' => $request, 'response' => $request->getResponse()))); $this->assertEquals($response->getStatusCode(), $request->getResponse()->getStatusCode()); $this->assertEquals((string) $response->getBody(), (string) $request->getResponse()->getBody()); $this->assertTrue($request->getResponse()->hasHeader('Age')); if ($request->getResponse()->isFresh() === false) { $this->assertContains('110', (string) $request->getResponse()->getHeader('Warning')); } $this->assertSame( sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via') ); $this->assertSame( sprintf('%s GuzzleCache/%s',$request->getProtocolVersion(), Version::VERSION), (string) $request->getResponse()->getHeader('Via') ); $this->assertTrue($request->getParams()->get('cache.lookup')); $this->assertTrue($request->getParams()->get('cache.hit')); $this->assertTrue($request->getResponse()->hasHeader('X-Cache-Lookup')); $this->assertTrue($request->getResponse()->hasHeader('X-Cache')); $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache')); $this->assertEquals('HIT from GuzzleCache', (string) $request->getResponse()->getHeader('X-Cache-Lookup')); } public function satisfiableOnErrorProvider() { $date = new \DateTime('-10 seconds'); return array( array( new Response(200, array( 'Date' => $date->format('c'), 'Cache-Control' => 'max-age=5, stale-if-error' ), 'foo'), ) ); } /** * @dataProvider satisfiableOnErrorProvider */ public function testInjectsSatisfiableResponsesOnError($cacheResponse) { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('fetch')) ->getMockForAbstractClass(); $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); $plugin = new CachePlugin(array('storage' => $storage)); $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); $plugin->onRequestBeforeSend(new Event(array('request' => $request))); $plugin->onRequestError( $event = new Event(array( 'request' => $request, 'response' => $request->getResponse(), )) ); $response = $event['response']; $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); $this->assertTrue($response->hasHeader('Age')); if ($response->isFresh() === false) { $this->assertContains('110', (string) $response->getHeader('Warning')); } $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); $this->assertTrue($request->getParams()->get('cache.lookup')); $this->assertSame('error', $request->getParams()->get('cache.hit')); $this->assertTrue($response->hasHeader('X-Cache-Lookup')); $this->assertTrue($response->hasHeader('X-Cache')); $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); } /** * @dataProvider satisfiableOnErrorProvider */ public function testInjectsSatisfiableResponsesOnException($cacheResponse) { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('fetch')) ->getMockForAbstractClass(); $storage->expects($this->exactly(2))->method('fetch')->will($this->returnValue($cacheResponse)); $plugin = new CachePlugin(array('storage' => $storage)); $request = new Request('GET', 'http://foo.com', array('Cache-Control' => 'max-stale')); $plugin->onRequestBeforeSend(new Event(array( 'request' => $request ))); $plugin->onRequestException( new Event(array( 'request' => $request, 'response' => $request->getResponse(), 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), )) ); $plugin->onRequestSent( new Event(array( 'request' => $request, 'response' => $response = $request->getResponse(), )) ); $this->assertEquals($cacheResponse->getStatusCode(), $response->getStatusCode()); $this->assertEquals((string) $cacheResponse->getBody(), (string) $response->getBody()); $this->assertTrue($response->hasHeader('Age')); if ($response->isFresh() === false) { $this->assertContains('110', (string) $response->getHeader('Warning')); } $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $request->getHeader('Via')); $this->assertSame(sprintf('%s GuzzleCache/%s', $request->getProtocolVersion(), Version::VERSION), (string) $response->getHeader('Via')); $this->assertTrue($request->getParams()->get('cache.lookup')); $this->assertSame('error', $request->getParams()->get('cache.hit')); $this->assertTrue($response->hasHeader('X-Cache-Lookup')); $this->assertTrue($response->hasHeader('X-Cache')); $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); } public function unsatisfiableOnErrorProvider() { $date = new \DateTime('-10 seconds'); return array( // no-store on request array( false, array('Cache-Control' => 'no-store'), new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), ), // request expired array( true, array('Cache-Control' => 'stale-if-error=4'), new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error'), 'foo'), ), // response expired array( true, array('Cache-Control' => 'stale-if-error'), new Response(200, array('Date' => $date->format('D, d M Y H:i:s T'), 'Cache-Control' => 'max-age=5, stale-if-error=4'), 'foo'), ), ); } /** * @dataProvider unsatisfiableOnErrorProvider */ public function testDoesNotInjectUnsatisfiableResponsesOnError($requestCanCache, $requestHeaders, $cacheResponse) { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('fetch')) ->getMockForAbstractClass(); $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($cacheResponse)); $plugin = new CachePlugin(array('storage' => $storage)); $request = new Request('GET', 'http://foo.com', $requestHeaders); $plugin->onRequestBeforeSend(new Event(array( 'request' => $request ))); $plugin->onRequestError( $event = new Event(array( 'request' => $request, 'response' => $response = $request->getResponse(), )) ); $this->assertSame($response, $event['response']); } /** * @dataProvider unsatisfiableOnErrorProvider */ public function testDoesNotInjectUnsatisfiableResponsesOnException($requestCanCache, $requestHeaders, $responseParts) { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('fetch')) ->getMockForAbstractClass(); $storage->expects($this->exactly($requestCanCache ? 2 : 0))->method('fetch')->will($this->returnValue($responseParts)); $plugin = new CachePlugin(array('storage' => $storage)); $request = new Request('GET', 'http://foo.com', $requestHeaders); $plugin->onRequestBeforeSend(new Event(array( 'request' => $request ))); $plugin->onRequestException( $event = new Event(array( 'request' => $request, 'response' => $response = $request->getResponse(), 'exception' => $this->getMock('Guzzle\Http\Exception\CurlException'), )) ); $this->assertSame($response, $request->getResponse()); } public function testCachesResponsesWhenCacheable() { $cache = new ArrayCache(); $plugin = new CachePlugin($cache); $request = new Request('GET', 'http://foo.com'); $response = new Response(200, array(), 'Foo'); $plugin->onRequestBeforeSend(new Event(array( 'request' => $request ))); $plugin->onRequestSent(new Event(array( 'request' => $request, 'response' => $response ))); $data = $this->readAttribute($cache, 'data'); $this->assertNotEmpty($data); } public function testPurgesRequests() { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('purge')) ->getMockForAbstractClass(); $storage->expects($this->atLeastOnce())->method('purge'); $plugin = new CachePlugin(array('storage' => $storage)); $request = new Request('GET', 'http://foo.com', array('X-Foo' => 'Bar')); $plugin->purge($request); } public function testAutoPurgesRequests() { $storage = $this->getMockBuilder('Guzzle\Plugin\Cache\CacheStorageInterface') ->setMethods(array('purge')) ->getMockForAbstractClass(); $storage->expects($this->atLeastOnce())->method('purge'); $plugin = new CachePlugin(array('storage' => $storage, 'auto_purge' => true)); $client = new Client(); $request = $client->put('http://foo.com', array('X-Foo' => 'Bar')); $request->addSubscriber($plugin); $request->setResponse(new Response(200), true); $request->send(); } } guzzle/tests/Guzzle/Tests/Plugin/Cache/DefaultRevalidationTest.php 0000604 00000023426 15173213567 0021365 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cache; use Guzzle\Http\Client; use Guzzle\Http\ClientInterface; use Guzzle\Http\Exception\BadResponseException; use Guzzle\Http\Exception\CurlException; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\RequestFactory; use Guzzle\Plugin\Cache\CachePlugin; use Guzzle\Cache\DoctrineCacheAdapter; use Doctrine\Common\Cache\ArrayCache; use Guzzle\Plugin\Cache\DefaultCacheStorage; use Guzzle\Plugin\Mock\MockPlugin; use Guzzle\Tests\Http\Server; /** * @covers Guzzle\Plugin\Cache\DefaultRevalidation * @group server */ class DefaultRevalidationTest extends \Guzzle\Tests\GuzzleTestCase { protected function getHttpDate($time) { return gmdate(ClientInterface::HTTP_DATE, strtotime($time)); } /** * 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: " . $this->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", "HTTP/1.1 200 OK\r\nCache-Control: must-revalidate, no-cache\r\nDate: " . $this->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: " . $this->getHttpDate('now') . "\r\n\r\nDatas" ), // Throws an exception during revalidation array( false, "\r\n", "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " . $this->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", "HTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nETag: \"123\"\r\nDate: " . $this->getHttpDate('-10 hours') . "\r\n\r\nData", "HTTP/1.1 304 NOT MODIFIED\r\nETag: \"123456\"\r\n\r\n", ), ); } /** * @dataProvider cacheRevalidationDataProvider */ public function testRevalidatesResponsesAgainstOriginServer($can, $request, $response, $validate = null, $result = 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()); $plugin = new CachePlugin(new DoctrineCacheAdapter(new ArrayCache())); $this->assertEquals( $can, $plugin->canResponseSatisfyRequest($request, $response), '-> ' . $request . "\n" . $response ); if ($result) { $result = Response::fromMessage($result); $result->removeHeader('Date'); $request->getResponse()->removeHeader('Date'); $request->getResponse()->removeHeader('Connection'); // Get rid of dates $this->assertEquals((string) $result, (string) $request->getResponse()); } if ($validate) { $this->assertEquals(1, count($server->getReceivedRequests())); } } public function testHandles404RevalidationResponses() { $request = new Request('GET', 'http://foo.com'); $request->setClient(new Client()); $badResponse = new Response(404, array(), 'Oh no!'); $badRequest = clone $request; $badRequest->setResponse($badResponse, true); $response = new Response(200, array(), 'foo'); // Seed the cache $s = new DefaultCacheStorage(new DoctrineCacheAdapter(new ArrayCache())); $s->cache($request, $response); $this->assertNotNull($s->fetch($request)); $rev = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultRevalidation') ->setConstructorArgs(array($s)) ->setMethods(array('createRevalidationRequest')) ->getMock(); $rev->expects($this->once()) ->method('createRevalidationRequest') ->will($this->returnValue($badRequest)); try { $rev->revalidate($request, $response); $this->fail('Should have thrown an exception'); } catch (BadResponseException $e) { $this->assertSame($badResponse, $e->getResponse()); $this->assertNull($s->fetch($request)); } } public function testCanRevalidateWithPlugin() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\n" . "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . "Content-Length: 2\r\n\r\nhi", "HTTP/1.0 304 Not Modified\r\n" . "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . "Content-Type: text/html; charset=UTF-8\r\n" . "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . "Age: 6302\r\n\r\n", "HTTP/1.0 304 Not Modified\r\n" . "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . "Content-Type: text/html; charset=UTF-8\r\n" . "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . "Age: 6302\r\n\r\n", )); $client = new Client($this->getServer()->getUrl()); $client->addSubscriber(new CachePlugin()); $this->assertEquals(200, $client->get()->send()->getStatusCode()); $this->assertEquals(200, $client->get()->send()->getStatusCode()); $this->assertEquals(200, $client->get()->send()->getStatusCode()); $this->assertEquals(3, count($this->getServer()->getReceivedRequests())); } public function testCanHandleRevalidationFailures() { $client = new Client($this->getServer()->getUrl()); $lm = gmdate('c', time() - 60); $mock = new MockPlugin(array( new Response(200, array( 'Date' => $lm, 'Cache-Control' => 'max-age=100, must-revalidate, stale-if-error=9999', 'Last-Modified' => $lm, 'Content-Length' => 2 ), 'hi'), new CurlException('Bleh'), new CurlException('Bleh') )); $client->addSubscriber(new CachePlugin()); $client->addSubscriber($mock); $client->get()->send(); $response = $client->get()->send(); $this->assertEquals(200, $response->getStatusCode()); $this->assertEquals('hi', $response->getBody(true)); $this->assertEquals(3, count($mock->getReceivedRequests())); $this->assertEquals(0, count($mock->getQueue())); } public function testCanHandleStaleIfErrorWhenRevalidating() { $lm = gmdate('c', time() - 60); $mock = new MockPlugin(array( new Response(200, array( 'Date' => $lm, 'Cache-Control' => 'must-revalidate, max-age=0, stale-if-error=1200', 'Last-Modified' => $lm, 'Content-Length' => 2 ), 'hi'), new CurlException('Oh no!'), new CurlException('Oh no!') )); $cache = new CachePlugin(); $client = new Client('http://www.example.com'); $client->addSubscriber($cache); $client->addSubscriber($mock); $this->assertEquals(200, $client->get()->send()->getStatusCode()); $response = $client->get()->send(); $this->assertEquals(200, $response->getStatusCode()); $this->assertCount(0, $mock); $this->assertEquals('HIT from GuzzleCache', (string) $response->getHeader('X-Cache-Lookup')); $this->assertEquals('HIT_ERROR from GuzzleCache', (string) $response->getHeader('X-Cache')); } /** * @group issue-437 */ public function testDoesNotTouchClosureListeners() { $this->getServer()->flush(); $this->getServer()->enqueue(array( "HTTP/1.1 200 OK\r\n" . "Date: Mon, 12 Nov 2012 03:06:37 GMT\r\n" . "Cache-Control: private, s-maxage=0, max-age=0, must-revalidate\r\n" . "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . "Content-Length: 2\r\n\r\nhi", "HTTP/1.0 304 Not Modified\r\n" . "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . "Content-Type: text/html; charset=UTF-8\r\n" . "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . "Age: 6302\r\n\r\n", "HTTP/1.0 304 Not Modified\r\n" . "Date: Mon, 12 Nov 2012 03:06:38 GMT\r\n" . "Content-Type: text/html; charset=UTF-8\r\n" . "Last-Modified: Mon, 12 Nov 2012 02:53:38 GMT\r\n" . "Age: 6302\r\n\r\n", )); $client = new Client($this->getServer()->getUrl()); $client->addSubscriber(new CachePlugin()); $client->getEventDispatcher()->addListener('command.after_send', function(){}); $this->assertEquals(200, $client->get()->send()->getStatusCode()); $this->assertEquals(200, $client->get()->send()->getStatusCode()); $this->assertEquals(200, $client->get()->send()->getStatusCode()); } } guzzle/tests/Guzzle/Tests/Plugin/Cache/DenyRevalidationTest.php 0000604 00000000747 15173213567 0020701 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cache; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Cache\DenyRevalidation; /** * @covers Guzzle\Plugin\Cache\DenyRevalidation */ class DenyRevalidationTest extends \Guzzle\Tests\GuzzleTestCase { public function testDeniesRequestRevalidation() { $deny = new DenyRevalidation(); $this->assertFalse($deny->revalidate(new Request('GET', 'http://foo.com'), new Response(200))); } } guzzle/tests/Guzzle/Tests/Plugin/Cache/CallbackCanCacheStrategyTest.php 0000604 00000004777 15173213567 0022234 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Cache; use Doctrine\Common\Cache\ArrayCache; use Guzzle\Cache\DoctrineCacheAdapter; use Guzzle\Common\Event; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Cache\CachePlugin; use Guzzle\Plugin\Cache\CallbackCanCacheStrategy; /** * @covers Guzzle\Plugin\Cache\CallbackCanCacheStrategy */ class CallbackCanCacheStrategyTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException \Guzzle\Common\Exception\InvalidArgumentException */ public function testConstructorEnsuresCallbackIsCallable() { $p = new CallbackCanCacheStrategy(new \stdClass()); } public function testUsesCallback() { $c = new CallbackCanCacheStrategy(function ($request) { return true; }); $this->assertTrue($c->canCacheRequest(new Request('DELETE', 'http://www.foo.com'))); } /** * The following is a bit of an integration test to ensure that the CachePlugin honors a * custom can cache strategy. */ public function testIntegrationWithCachePlugin() { $c = new CallbackCanCacheStrategy( function ($request) { return true; }, function ($response) { return true; } ); // Make a request and response that have no business being cached $request = new Request('DELETE', 'http://www.foo.com'); $response = Response::fromMessage( "HTTP/1.1 200 OK\r\n" . "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" . "Last-Modified: Wed, 09 Jan 2013 08:48:53 GMT\r\n" . "Content-Length: 2\r\n" . "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n\r\n" . "hi" ); $this->assertTrue($c->canCacheRequest($request)); $this->assertTrue($c->canCacheResponse($response)); $s = $this->getMockBuilder('Guzzle\Plugin\Cache\DefaultCacheStorage') ->setConstructorArgs(array(new DoctrineCacheAdapter(new ArrayCache()))) ->setMethods(array('fetch')) ->getMockForAbstractClass(); $s->expects($this->once()) ->method('fetch') ->will($this->returnValue($response)); $plugin = new CachePlugin(array('can_cache' => $c, 'storage' => $s)); $plugin->onRequestBeforeSend(new Event(array('request' => $request))); $this->assertEquals(200, $request->getResponse()->getStatusCode()); $this->assertEquals('hi', $request->getResponse()->getBody(true)); } } guzzle/tests/Guzzle/Tests/Plugin/History/HistoryPluginTest.php 0000604 00000010141 15173213567 0020703 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\History; use Guzzle\Http\Client; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Plugin\History\HistoryPlugin; use Guzzle\Plugin\Mock\MockPlugin; /** * @covers Guzzle\Plugin\History\HistoryPlugin */ class HistoryPluginTest extends \Guzzle\Tests\GuzzleTestCase { /** * Adds multiple requests to a plugin * * @param HistoryPlugin $h Plugin * @param int $num Number of requests to add * * @return array */ protected function addRequests(HistoryPlugin $h, $num) { $requests = array(); $client = new Client('http://127.0.0.1/'); for ($i = 0; $i < $num; $i++) { $requests[$i] = $client->get(); $requests[$i]->setResponse(new Response(200), true); $requests[$i]->send(); $h->add($requests[$i]); } return $requests; } public function testDescribesSubscribedEvents() { $this->assertInternalType('array', HistoryPlugin::getSubscribedEvents()); } public function testMaintainsLimitValue() { $h = new HistoryPlugin(); $this->assertSame($h, $h->setLimit(10)); $this->assertEquals(10, $h->getLimit()); } 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]); } /** * @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); } } } public function testReturnsLastRequest() { $h = new HistoryPlugin(); $requests = $this->addRequests($h, 5); $this->assertSame(end($requests), $h->getLastRequest()); } public function testReturnsLastResponse() { $h = new HistoryPlugin(); $requests = $this->addRequests($h, 5); $this->assertSame(end($requests)->getResponse(), $h->getLastResponse()); } public function testClearsHistory() { $h = new HistoryPlugin(); $requests = $this->addRequests($h, 5); $this->assertEquals(5, count($h)); $h->clear(); $this->assertEquals(0, count($h)); } /** * @depends testAddsRequests */ public function testUpdatesAddRequests() { $h = new HistoryPlugin(); $client = new Client('http://127.0.0.1/'); $client->getEventDispatcher()->addSubscriber($h); $request = $client->get(); $request->setResponse(new Response(200), true); $request->send(); $this->assertSame($request, $h->getLastRequest()); } public function testCanCastToString() { $client = new Client('http://127.0.0.1/'); $h = new HistoryPlugin(); $client->getEventDispatcher()->addSubscriber($h); $mock = new MockPlugin(array( new Response(301, array('Location' => '/redirect1', 'Content-Length' => 0)), new Response(307, array('Location' => '/redirect2', 'Content-Length' => 0)), new Response(200, array('Content-Length' => '2'), 'HI') )); $client->getEventDispatcher()->addSubscriber($mock); $request = $client->get(); $request->send(); $this->assertEquals(3, count($h)); $this->assertEquals(3, count($mock->getReceivedRequests())); $h = str_replace("\r", '', $h); $this->assertContains("> GET / HTTP/1.1\nHost: 127.0.0.1\nUser-Agent:", $h); $this->assertContains("< HTTP/1.1 301 Moved Permanently\nLocation: /redirect1", $h); $this->assertContains("< HTTP/1.1 307 Temporary Redirect\nLocation: /redirect2", $h); $this->assertContains("< HTTP/1.1 200 OK\nContent-Length: 2\n\nHI", $h); } } guzzle/tests/Guzzle/Tests/Plugin/Oauth/OauthPluginTest.php 0000604 00000031134 15173213567 0017746 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Oauth; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\QueryAggregator\CommaAggregator; use Guzzle\Plugin\Oauth\OauthPlugin; use Guzzle\Common\Event; /** * @covers Guzzle\Plugin\Oauth\OauthPlugin */ class OauthPluginTest extends \Guzzle\Tests\GuzzleTestCase { const TIMESTAMP = '1327274290'; const NONCE = 'e7aa11195ca58349bec8b5ebe351d3497eb9e603'; protected $config = array( 'consumer_key' => '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' )); } public function testSubscribesToEvents() { $events = OauthPlugin::getSubscribedEvents(); $this->assertArrayHasKey('request.before_send', $events); } 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']); $this->assertEquals('header', $config['request_method']); } public function testCreatesStringToSignFromPostRequest() { $p = new OauthPlugin($this->config); $request = $this->getRequest(); $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); $this->assertContains('&e=f', rawurldecode($signString)); $expectedSignString = // 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%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . 'oauth_signature_method%3DHMAC-SHA1' . '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0'; $this->assertEquals($expectedSignString, $signString); } public function testCreatesStringToSignIgnoringPostFields() { $config = $this->config; $config['disable_post_params'] = true; $p = new OauthPlugin($config); $request = $this->getRequest(); $sts = rawurldecode($p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); $this->assertNotContains('&e=f', $sts); } 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%3D'. self::NONCE .'%26' . 'oauth_signature_method%3DHMAC-SHA1' . '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE) ); } /** * @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, self::NONCE)); } public function testCreatesStringToSignFromPostRequestWithNullValues() { $config = array( 'consumer_key' => 'foo', 'consumer_secret' => 'bar', 'token' => null, 'token_secret' => 'dracula' ); $p = new OauthPlugin($config); $request = $this->getRequest(); $signString = $p->getStringToSign($request, self::TIMESTAMP, self::NONCE); $this->assertContains('&e=f', rawurldecode($signString)); $expectedSignString = // 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%3De7aa11195ca58349bec8b5ebe351d3497eb9e603%26' . 'oauth_signature_method%3DHMAC-SHA1' . '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_version%3D1.0'; $this->assertEquals($expectedSignString, $signString); } /** * @depends testCreatesStringToSignFromPostRequest */ public function testMultiDimensionalArray() { $p = new OauthPlugin($this->config); $request = $this->getRequest(); $request->getQuery()->set('a', array('b' => array('e' => 'f', 'c' => 'd'))); $this->assertContains('a%255Bb%255D%255Bc%255D%3Dd%26a%255Bb%255D%255Be%255D%3Df%26c%3Dd%26e%3Df%26', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); } /** * @depends testMultiDimensionalArray */ public function testMultiDimensionalArrayWithNonDefaultQueryAggregator() { $p = new OauthPlugin($this->config); $request = $this->getRequest(); $aggregator = new CommaAggregator(); $query = $request->getQuery()->setAggregator($aggregator) ->set('g', array('h', 'i', 'j')) ->set('k', array('l')) ->set('m', array('n', 'o')); $this->assertContains('a%3Db%26c%3Dd%26e%3Df%26g%3Dh%2Ci%2Cj%26k%3Dl%26m%3Dn%2Co', $p->getStringToSign($request, self::TIMESTAMP, self::NONCE)); } /** * @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, self::NONCE); $this->assertEquals( '_POST&http%3A%2F%2Fwww.test.com%2Fpath&a%3Db%26c%3Dd%26e%3Df%26oauth_consumer_key%3Dfoo' . '%26oauth_nonce%3D'. self::NONCE .'%26oauth_signature_method%3DHMAC-SHA1' . '%26oauth_timestamp%3D' . self::TIMESTAMP . '%26oauth_token%3Dcount%26oauth_version%3D1.0|' . 'bar&dracula_', base64_decode($sig) ); } /** * Test that the Oauth is signed correctly and that extra strings haven't been added * to the authorization header. */ public function testSignsOauthRequests() { $p = new OauthPlugin($this->config); $event = new Event(array( 'request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP )); $params = $p->onRequestBeforeSend($event); $this->assertTrue($event['request']->hasHeader('Authorization')); $authorizationHeader = (string)$event['request']->getHeader('Authorization'); $this->assertStringStartsWith('OAuth ', $authorizationHeader); $stringsToCheck = array( 'oauth_consumer_key="foo"', 'oauth_nonce="'.urlencode($params['oauth_nonce']).'"', 'oauth_signature="'.urlencode($params['oauth_signature']).'"', 'oauth_signature_method="HMAC-SHA1"', 'oauth_timestamp="' . self::TIMESTAMP . '"', 'oauth_token="count"', 'oauth_version="1.0"', ); $totalLength = strlen('OAuth '); //Separator is not used before first parameter. $separator = ''; foreach ($stringsToCheck as $stringToCheck) { $this->assertContains($stringToCheck, $authorizationHeader); $totalLength += strlen($separator); $totalLength += strlen($stringToCheck); $separator = ', '; } // Technically this test is not universally valid. It would be allowable to have extra \n characters // in the Authorization header. However Guzzle does not do this, so we just perform a simple check // on length to validate the Authorization header is composed of only the strings above. $this->assertEquals($totalLength, strlen($authorizationHeader), 'Authorization has extra characters i.e. contains extra elements compared to stringsToCheck.'); } public function testSignsOauthQueryStringRequest() { $config = array_merge( $this->config, array('request_method' => OauthPlugin::REQUEST_METHOD_QUERY) ); $p = new OauthPlugin($config); $event = new Event(array( 'request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP )); $params = $p->onRequestBeforeSend($event); $this->assertFalse($event['request']->hasHeader('Authorization')); $stringsToCheck = array( 'a=b', 'c=d', 'oauth_consumer_key=foo', 'oauth_nonce='.urlencode($params['oauth_nonce']), 'oauth_signature='.urlencode($params['oauth_signature']), 'oauth_signature_method=HMAC-SHA1', 'oauth_timestamp='.self::TIMESTAMP, 'oauth_token=count', 'oauth_version=1.0', ); $queryString = (string) $event['request']->getQuery(); $totalLength = strlen('?'); //Separator is not used before first parameter. $separator = ''; foreach ($stringsToCheck as $stringToCheck) { $this->assertContains($stringToCheck, $queryString); $totalLength += strlen($separator); $totalLength += strlen($stringToCheck); $separator = '&'; } // Removes the last query string separator '&' $totalLength -= 1; $this->assertEquals($totalLength, strlen($queryString), 'Query string has extra characters i.e. contains extra elements compared to stringsToCheck.'); } /** * @expectedException \InvalidArgumentException */ public function testInvalidArgumentExceptionOnMethodError() { $config = array_merge( $this->config, array('request_method' => 'FakeMethod') ); $p = new OauthPlugin($config); $event = new Event(array( 'request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP )); $p->onRequestBeforeSend($event); } public function testDoesNotAddFalseyValuesToAuthorization() { unset($this->config['token']); $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->assertNotContains('oauth_token=', (string) $event['request']->getHeader('Authorization')); } public function testOptionalOauthParametersAreNotAutomaticallyAdded() { // The only required Oauth parameters are the consumer key and secret. That is enough credentials // for signing oauth requests. $config = array( 'consumer_key' => 'foo', 'consumer_secret' => 'bar', ); $plugin = new OauthPlugin($config); $event = new Event(array( 'request' => $this->getRequest(), 'timestamp' => self::TIMESTAMP )); $timestamp = $plugin->getTimestamp($event); $request = $event['request']; $nonce = $plugin->generateNonce($request); $paramsToSign = $plugin->getParamsToSign($request, $timestamp, $nonce); $optionalParams = array( 'callback' => 'oauth_callback', 'token' => 'oauth_token', 'verifier' => 'oauth_verifier', 'token_secret' => 'token_secret' ); foreach ($optionalParams as $optionName => $oauthName) { $this->assertArrayNotHasKey($oauthName, $paramsToSign, "Optional Oauth param '$oauthName' was not set via config variable '$optionName', but it is listed in getParamsToSign()."); } } } guzzle/tests/Guzzle/Tests/Plugin/ErrorResponse/ErrorResponsePluginTest.php 0000604 00000011775 15173213567 0023237 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\ErrorResponse; use Guzzle\Service\Client; use Guzzle\Http\Message\Response; use Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Tests\Mock\ErrorResponseMock; /** * @covers \Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin */ class ErrorResponsePluginTest extends \Guzzle\Tests\GuzzleTestCase { protected $client; public static function tearDownAfterClass() { self::getServer()->flush(); } public function setUp() { $mockError = 'Guzzle\Tests\Mock\ErrorResponseMock'; $description = ServiceDescription::factory(array( 'operations' => array( 'works' => array( 'httpMethod' => 'GET', 'errorResponses' => array( array('code' => 500, 'class' => $mockError), array('code' => 503, 'reason' => 'foo', 'class' => $mockError), array('code' => 200, 'reason' => 'Error!', 'class' => $mockError) ) ), 'bad_class' => array( 'httpMethod' => 'GET', 'errorResponses' => array( array('code' => 500, 'class' => 'Does\\Not\\Exist') ) ), 'does_not_implement' => array( 'httpMethod' => 'GET', 'errorResponses' => array( array('code' => 500, 'class' => __CLASS__) ) ), 'no_errors' => array('httpMethod' => 'GET'), 'no_class' => array( 'httpMethod' => 'GET', 'errorResponses' => array( array('code' => 500) ) ), ) )); $this->client = new Client($this->getServer()->getUrl()); $this->client->setDescription($description); } /** * @expectedException \Guzzle\Http\Exception\ServerErrorResponseException */ public function testSkipsWhenErrorResponsesIsNotSet() { $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $this->client->getCommand('no_errors')->execute(); } public function testSkipsWhenErrorResponsesIsNotSetAndAllowsSuccess() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $this->client->getCommand('no_errors')->execute(); } /** * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException * @expectedExceptionMessage Does\Not\Exist does not exist */ public function testEnsuresErrorResponseExists() { $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $this->client->getCommand('bad_class')->execute(); } /** * @expectedException \Guzzle\Plugin\ErrorResponse\Exception\ErrorResponseException * @expectedExceptionMessage must implement Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface */ public function testEnsuresErrorResponseImplementsInterface() { $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $this->client->getCommand('does_not_implement')->execute(); } public function testThrowsSpecificErrorResponseOnMatch() { try { $this->getServer()->enqueue("HTTP/1.1 500 Foo\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $command = $this->client->getCommand('works'); $command->execute(); $this->fail('Exception not thrown'); } catch (ErrorResponseMock $e) { $this->assertSame($command, $e->command); $this->assertEquals(500, $e->response->getStatusCode()); } } /** * @expectedException \Guzzle\Tests\Mock\ErrorResponseMock */ public function testThrowsWhenCodeAndPhraseMatch() { $this->getServer()->enqueue("HTTP/1.1 200 Error!\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $this->client->getCommand('works')->execute(); } public function testSkipsWhenReasonDoesNotMatch() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $this->client->getCommand('works')->execute(); } public function testSkipsWhenNoClassIsSet() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); $this->client->addSubscriber(new ErrorResponsePlugin()); $this->client->getCommand('no_class')->execute(); } } guzzle/tests/Guzzle/Tests/Plugin/Mock/MockPluginTest.php 0000604 00000013765 15173213567 0017402 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Mock; use Guzzle\Common\Event; use Guzzle\Http\EntityBody; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Mock\MockPlugin; use Guzzle\Http\Client; use Guzzle\Http\Exception\CurlException; /** * @covers Guzzle\Plugin\Mock\MockPlugin */ class MockPluginTest extends \Guzzle\Tests\GuzzleTestCase { public function testDescribesSubscribedEvents() { $this->assertInternalType('array', MockPlugin::getSubscribedEvents()); } public function testDescribesEvents() { $this->assertInternalType('array', MockPlugin::getAllEvents()); } public function testCanBeTemporary() { $plugin = new MockPlugin(); $this->assertFalse($plugin->isTemporary()); $plugin = new MockPlugin(null, true); $this->assertTrue($plugin->isTemporary()); } 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)); } /** * @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)); } 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)); } public function testRetrievesResponsesFromFiles() { $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); $this->assertInstanceOf('Guzzle\\Http\\Message\\Response', $response); $this->assertEquals(200, $response->getStatusCode()); } /** * @expectedException InvalidArgumentException */ public function testThrowsExceptionWhenResponseFileIsNotFound() { MockPlugin::getMockFile('missing/filename'); } /** * @expectedException InvalidArgumentException */ public function testInvalidResponsesThrowAnException() { $p = new MockPlugin(); $p->addResponse($this); } 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()); } public function testAddsResponseFilesToQueue() { $p = new MockPlugin(); $p->addResponse(__DIR__ . '/../../TestData/mock_response'); $this->assertEquals(1, count($p)); } /** * @depends testAddsResponseFilesToQueue */ public function testAddsMockResponseToRequestFromClient() { $p = new MockPlugin(); $response = MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response'); $p->addResponse($response); $client = new Client('http://127.0.0.1:123/'); $client->getEventDispatcher()->addSubscriber($p, 9999); $request = $client->get(); $request->send(); $this->assertSame($response, $request->getResponse()); $this->assertEquals(0, count($p)); } /** * @depends testAddsResponseFilesToQueue * @expectedException \OutOfBoundsException */ public function testUpdateThrowsExceptionWhenEmpty() { $p = new MockPlugin(); $p->onRequestBeforeSend(new Event()); } /** * @depends testAddsMockResponseToRequestFromClient */ public function testDetachesTemporaryWhenEmpty() { $p = new MockPlugin(null, true); $p->addResponse(MockPlugin::getMockFile(__DIR__ . '/../../TestData/mock_response')); $client = new Client('http://127.0.0.1:123/'); $client->getEventDispatcher()->addSubscriber($p, 9999); $request = $client->get(); $request->send(); $this->assertFalse($this->hasSubscriber($client, $p)); } public function testLoadsResponsesFromConstructor() { $p = new MockPlugin(array(new Response(200))); $this->assertEquals(1, $p->count()); } public function testStoresMockedRequests() { $p = new MockPlugin(array(new Response(200), new Response(200))); $client = new Client('http://127.0.0.1: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()); } public function testReadsBodiesFromMockedRequests() { $p = new MockPlugin(array(new Response(200))); $p->readBodies(true); $client = new Client('http://127.0.0.1:123/'); $client->getEventDispatcher()->addSubscriber($p, 9999); $body = EntityBody::factory('foo'); $request = $client->put(); $request->setBody($body); $request->send(); $this->assertEquals(3, $body->ftell()); } public function testCanMockBadRequestExceptions() { $client = new Client('http://127.0.0.1:123/'); $ex = new CurlException('Foo'); $mock = new MockPlugin(array($ex)); $client->addSubscriber($mock); $request = $client->get('foo'); try { $request->send(); $this->fail('Did not dequeue an exception'); } catch (CurlException $e) { $this->assertSame($e, $ex); $this->assertSame($request, $ex->getRequest()); } } } guzzle/tests/Guzzle/Tests/Plugin/CurlAuth/CurlAuthPluginTest.php 0000604 00000002632 15173213567 0021065 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\CurlAuth; use Guzzle\Common\Version; use Guzzle\Plugin\CurlAuth\CurlAuthPlugin; use Guzzle\Http\Client; /** * @covers Guzzle\Plugin\CurlAuth\CurlAuthPlugin */ class CurlAuthPluginTest extends \Guzzle\Tests\GuzzleTestCase { public function testAddsBasicAuthentication() { Version::$emitWarnings = false; $plugin = new CurlAuthPlugin('michael', 'test'); $client = new Client('http://www.test.com/'); $client->getEventDispatcher()->addSubscriber($plugin); $request = $client->get('/'); $this->assertEquals('michael', $request->getUsername()); $this->assertEquals('test', $request->getPassword()); Version::$emitWarnings = true; } public function testAddsDigestAuthentication() { Version::$emitWarnings = false; $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)); Version::$emitWarnings = true; } } guzzle/tests/Guzzle/Tests/Plugin/Md5/CommandContentMd5PluginTest.php 0000604 00000006347 15173213567 0021522 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Md5; use Guzzle\Common\Event; use Guzzle\Plugin\Md5\CommandContentMd5Plugin; use Guzzle\Service\Description\ServiceDescription; use Guzzle\Service\Client; /** * @covers Guzzle\Plugin\Md5\CommandContentMd5Plugin */ class CommandContentMd5PluginTest extends \Guzzle\Tests\GuzzleTestCase { protected function getClient() { $description = new ServiceDescription(array( 'operations' => array( 'test' => array( 'httpMethod' => 'PUT', 'parameters' => array( 'ContentMD5' => array(), 'Body' => array( 'location' => 'body' ) ) ) ) )); $client = new Client(); $client->setDescription($description); return $client; } public function testHasEvents() { $this->assertNotEmpty(CommandContentMd5Plugin::getSubscribedEvents()); } public function testValidatesMd5WhenParamExists() { $client = $this->getClient(); $command = $client->getCommand('test', array( 'Body' => 'Foo', 'ContentMD5' => true )); $event = new Event(array('command' => $command)); $request = $command->prepare(); $plugin = new CommandContentMd5Plugin(); $plugin->onCommandBeforeSend($event); $this->assertEquals('E1bGfXrRY42Ba/uCLdLCXQ==', (string) $request->getHeader('Content-MD5')); } public function testDoesNothingWhenNoPayloadExists() { $client = $this->getClient(); $client->getDescription()->getOperation('test')->setHttpMethod('GET'); $command = $client->getCommand('test'); $event = new Event(array('command' => $command)); $request = $command->prepare(); $plugin = new CommandContentMd5Plugin(); $plugin->onCommandBeforeSend($event); $this->assertNull($request->getHeader('Content-MD5')); } public function testAddsValidationToResponsesOfContentMd5() { $client = $this->getClient(); $client->getDescription()->getOperation('test')->setHttpMethod('GET'); $command = $client->getCommand('test', array( 'ValidateMD5' => true )); $event = new Event(array('command' => $command)); $request = $command->prepare(); $plugin = new CommandContentMd5Plugin(); $plugin->onCommandBeforeSend($event); $listeners = $request->getEventDispatcher()->getListeners('request.complete'); $this->assertNotEmpty($listeners); } public function testIgnoresValidationWhenDisabled() { $client = $this->getClient(); $client->getDescription()->getOperation('test')->setHttpMethod('GET'); $command = $client->getCommand('test', array( 'ValidateMD5' => false )); $event = new Event(array('command' => $command)); $request = $command->prepare(); $plugin = new CommandContentMd5Plugin(); $plugin->onCommandBeforeSend($event); $listeners = $request->getEventDispatcher()->getListeners('request.complete'); $this->assertEmpty($listeners); } } guzzle/tests/Guzzle/Tests/Plugin/Md5/Md5ValidatorPluginTest.php 0000604 00000007712 15173213567 0020533 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Md5; use Guzzle\Http\EntityBody; use Guzzle\Http\Message\RequestFactory; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Md5\Md5ValidatorPlugin; /** * @covers Guzzle\Plugin\Md5\Md5ValidatorPlugin */ class Md5ValidatorPluginTest extends \Guzzle\Tests\GuzzleTestCase { public function testValidatesMd5() { $plugin = new Md5ValidatorPlugin(); $request = RequestFactory::getInstance()->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 )); } /** * @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') )); } 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') )); } 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 )); } } guzzle/tests/Guzzle/Tests/Plugin/Log/LogPluginTest.php 0000604 00000006164 15173213567 0017055 0 ustar 00 <?php namespace Guzzle\Tests\Plugin\Log; use Guzzle\Log\ClosureLogAdapter; use Guzzle\Http\Client; use Guzzle\Http\EntityBody; use Guzzle\Http\Message\Request; use Guzzle\Http\Message\Response; use Guzzle\Plugin\Log\LogPlugin; use Guzzle\Common\Event; /** * @group server * @covers Guzzle\Plugin\Log\LogPlugin */ class LogPluginTest extends \Guzzle\Tests\GuzzleTestCase { protected $adapter; public function setUp() { $this->adapter = new ClosureLogAdapter(function ($message) { echo $message; }); } public function testIgnoresCurlEventsWhenNotWiringBodies() { $p = new LogPlugin($this->adapter); $this->assertNotEmpty($p->getSubscribedEvents()); $event = new Event(array('request' => new Request('GET', 'http://foo.com'))); $p->onCurlRead($event); $p->onCurlWrite($event); $p->onRequestBeforeSend($event); } public function testLogsWhenComplete() { $output = ''; $p = new LogPlugin(new ClosureLogAdapter(function ($message) use (&$output) { $output = $message; }), '{method} {resource} | {code} {res_body}'); $p->onRequestSent(new Event(array( 'request' => new Request('GET', 'http://foo.com'), 'response' => new Response(200, array(), 'Foo') ))); $this->assertEquals('GET / | 200 Foo', $output); } public function testWiresBodiesWhenNeeded() { $client = new Client($this->getServer()->getUrl()); $plugin = new LogPlugin($this->adapter, '{req_body} | {res_body}', true); $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(sys_get_temp_dir(), 'non_repeatable'); $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(); unlink($tmpFile); $this->assertContains("send", $message); $this->assertContains("response", $message); } public function testHasHelpfulStaticFactoryMethod() { $s = fopen('php://temp', 'r+'); $client = new Client(); $client->addSubscriber(LogPlugin::getDebugPlugin(true, $s)); $request = $client->put('http://foo.com', array('Content-Type' => 'Foo'), 'Bar'); $request->setresponse(new Response(200), true); $request->send(); rewind($s); $contents = stream_get_contents($s); $this->assertContains('# Request:', $contents); $this->assertContainsIns('PUT / HTTP/1.1', $contents); $this->assertContains('# Response:', $contents); $this->assertContainsIns('HTTP/1.1 200 OK', $contents); $this->assertContains('# Errors:', $contents); } } guzzle/tests/Guzzle/Tests/GuzzleTestCase.php 0000604 00000015221 15173213567 0015244 0 ustar 00 <?php namespace Guzzle\Tests; use Guzzle\Common\HasDispatcherInterface; use Guzzle\Common\Event; use Guzzle\Http\Message\Response; use Guzzle\Http\Message\RequestInterface; use Guzzle\Tests\Http\Message\HeaderComparison; use Guzzle\Plugin\Mock\MockPlugin; use Guzzle\Http\Client; use Guzzle\Service\Builder\ServiceBuilderInterface; use Guzzle\Service\Builder\ServiceBuilder; use Guzzle\Tests\Mock\MockObserver; use Guzzle\Tests\Http\Server; use RuntimeException; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** * Base testcase class for all Guzzle testcases. */ abstract class GuzzleTestCase extends \PHPUnit_Framework_TestCase { protected static $mockBasePath; public static $serviceBuilder; public static $server; private $requests = array(); public $mockObserver; /** * Get the global server object used throughout the unit tests of Guzzle * * @return Server */ public static function getServer() { if (!self::$server) { self::$server = new Server(); if (self::$server->isRunning()) { self::$server->flush(); } else { self::$server->start(); } } return self::$server; } /** * Set the service builder to use for tests * * @param ServiceBuilderInterface $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 HasDispatcherInterface $hasDispatcher * * @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 * * @return self */ 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 $path instanceof Response ? $path : 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']); }); if ($paths instanceof Response) { // A single response instance has been specified, create an array with that instance // as the only element for the following loop to work as expected $paths = array($paths); } 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|bool 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); } } guzzle/tests/Guzzle/Tests/Mock/MockSubject.php 0000604 00000000150 15173213567 0015425 0 ustar 00 <?php namespace Guzzle\Tests\Mock; class MockSubject extends \Guzzle\Common\Event\AbstractSubject { } guzzle/tests/Guzzle/Tests/Mock/MockObserver.php 0000604 00000002503 15173213567 0015621 0 ustar 00 <?php namespace Guzzle\Tests\Mock; use Guzzle\Common\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class MockObserver implements \Countable, EventSubscriberInterface { public $events = array(); public static function getSubscribedEvents() { return array(); } public function has($eventName) { foreach ($this->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; } public function update(Event $event) { $this->events[] = $event; } } guzzle/tests/Guzzle/Tests/Mock/MockMulti.php 0000604 00000000254 15173213567 0015125 0 ustar 00 <?php namespace Guzzle\Tests\Mock; class MockMulti extends \Guzzle\Http\Curl\CurlMulti { public function getHandle() { return $this->multiHandle; } } guzzle/tests/Guzzle/Tests/Mock/CustomResponseModel.php 0000604 00000000647 15173213567 0017201 0 ustar 00 <?php namespace Guzzle\Tests\Mock; use Guzzle\Service\Command\ResponseClassInterface; use Guzzle\Service\Command\OperationCommand; class CustomResponseModel implements ResponseClassInterface { public $command; public static function fromCommand(OperationCommand $command) { return new self($command); } public function __construct($command) { $this->command = $command; } } guzzle/tests/Guzzle/Tests/Mock/ExceptionMock.php 0000604 00000000223 15173213567 0015765 0 ustar 00 <?php namespace Guzzle\Tests\Mock; class ExceptionMock { public function __construct() { throw new \Exception('Oh no!'); } } guzzle/tests/Guzzle/Tests/Mock/ErrorResponseMock.php 0000604 00000001212 15173213567 0016636 0 ustar 00 <?php namespace Guzzle\Tests\Mock; use Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface; use Guzzle\Service\Command\CommandInterface; use Guzzle\Http\Message\Response; class ErrorResponseMock extends \Exception implements ErrorResponseExceptionInterface { public $command; public $response; public static function fromCommand(CommandInterface $command, Response $response) { return new self($command, $response); } public function __construct($command, $response) { $this->command = $command; $this->response = $response; $this->message = 'Error from ' . $response; } } guzzle/tests/Guzzle/Tests/Stream/PhpStreamRequestFactoryTest.php 0000604 00000016711 15173213567 0021234 0 ustar 00 <?php namespace Guzzle\Tests\Stream; use Guzzle\Stream\Stream; use Guzzle\Stream\PhpStreamRequestFactory; use Guzzle\Http\Client; /** * @group server * @covers \Guzzle\Stream\PhpStreamRequestFactory */ class PhpStreamRequestFactoryTest extends \Guzzle\Tests\GuzzleTestCase { /** @var Client */ protected $client; /** @var PhpStreamRequestFactory */ protected $factory; protected function setUp() { $this->client = new Client($this->getServer()->getUrl()); $this->factory = new PhpStreamRequestFactory(); } public function testOpensValidStreamByCreatingContext() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); $request = $this->client->get('/'); $stream = $this->factory->fromRequest($request); $this->assertEquals('hi', (string) $stream); $headers = $this->factory->getLastResponseHeaders(); $this->assertContains('HTTP/1.1 200 OK', $headers); $this->assertContains('Content-Length: 2', $headers); $this->assertSame($headers, $stream->getCustomData('response_headers')); $this->assertEquals(2, $stream->getSize()); } public function testOpensValidStreamByPassingContextAndMerging() { $request = $this->client->get('/'); $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') ->setMethods(array('createContext', 'createStream')) ->getMock(); $this->factory->expects($this->never()) ->method('createContext'); $this->factory->expects($this->once()) ->method('createStream') ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); $context = array('http' => array('method' => 'HEAD', 'ignore_errors' => false)); $this->factory->fromRequest($request, stream_context_create($context)); $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); $this->assertEquals('HEAD', $options['http']['method']); $this->assertFalse($options['http']['ignore_errors']); $this->assertEquals('1.1', $options['http']['protocol_version']); } public function testAppliesProxySettings() { $request = $this->client->get('/'); $request->getCurlOptions()->set(CURLOPT_PROXY, 'tcp://foo.com'); $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') ->setMethods(array('createStream')) ->getMock(); $this->factory->expects($this->once()) ->method('createStream') ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); $this->factory->fromRequest($request); $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); $this->assertEquals('tcp://foo.com', $options['http']['proxy']); } public function testAddsPostFields() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); $request = $this->client->post('/', array('Foo' => 'Bar'), array('foo' => 'baz bar')); $stream = $this->factory->fromRequest($request); $this->assertEquals('hi', (string) $stream); $headers = $this->factory->getLastResponseHeaders(); $this->assertContains('HTTP/1.1 200 OK', $headers); $this->assertContains('Content-Length: 2', $headers); $this->assertSame($headers, $stream->getCustomData('response_headers')); $received = $this->getServer()->getReceivedRequests(); $this->assertEquals(1, count($received)); $this->assertContains('POST / HTTP/1.1', $received[0]); $this->assertContains('host: ', $received[0]); $this->assertContains('user-agent: Guzzle/', $received[0]); $this->assertContains('foo: Bar', $received[0]); $this->assertContains('content-length: 13', $received[0]); $this->assertContains('foo=baz%20bar', $received[0]); } public function testAddsBody() { $this->getServer()->flush(); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); $request = $this->client->put('/', array('Foo' => 'Bar'), 'Testing...123'); $stream = $this->factory->fromRequest($request); $this->assertEquals('hi', (string) $stream); $headers = $this->factory->getLastResponseHeaders(); $this->assertContains('HTTP/1.1 200 OK', $headers); $this->assertContains('Content-Length: 2', $headers); $this->assertSame($headers, $stream->getCustomData('response_headers')); $received = $this->getServer()->getReceivedRequests(); $this->assertEquals(1, count($received)); $this->assertContains('PUT / HTTP/1.1', $received[0]); $this->assertContains('host: ', $received[0]); $this->assertContains('user-agent: Guzzle/', $received[0]); $this->assertContains('foo: Bar', $received[0]); $this->assertContains('content-length: 13', $received[0]); $this->assertContains('Testing...123', $received[0]); } public function testCanDisableSslValidation() { $request = $this->client->get('/'); $request->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false); $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') ->setMethods(array('createStream')) ->getMock(); $this->factory->expects($this->once()) ->method('createStream') ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); $this->factory->fromRequest($request); $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); $this->assertFalse($options['ssl']['verify_peer']); } public function testUsesSslValidationByDefault() { $request = $this->client->get('/'); $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') ->setMethods(array('createStream')) ->getMock(); $this->factory->expects($this->once()) ->method('createStream') ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); $this->factory->fromRequest($request); $options = stream_context_get_options($this->readAttribute($this->factory, 'context')); $this->assertTrue($options['ssl']['verify_peer']); $this->assertSame($request->getCurlOptions()->get(CURLOPT_CAINFO), $options['ssl']['cafile']); } public function testBasicAuthAddsUserAndPassToUrl() { $request = $this->client->get('/'); $request->setAuth('Foo', 'Bar'); $this->factory = $this->getMockBuilder('Guzzle\Stream\PhpStreamRequestFactory') ->setMethods(array('createStream')) ->getMock(); $this->factory->expects($this->once()) ->method('createStream') ->will($this->returnValue(new Stream(fopen('php://temp', 'r')))); $this->factory->fromRequest($request); $this->assertContains('Foo:Bar@', (string) $this->readAttribute($this->factory, 'url')); } public function testCanCreateCustomStreamClass() { $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nhi"); $request = $this->client->get('/'); $stream = $this->factory->fromRequest($request, array(), array('stream_class' => 'Guzzle\Http\EntityBody')); $this->assertInstanceOf('Guzzle\Http\EntityBody', $stream); } } guzzle/tests/Guzzle/Tests/Stream/StreamTest.php 0000604 00000014213 15173213567 0015656 0 ustar 00 <?php namespace Guzzle\Tests\Stream; use Guzzle\Stream\Stream; /** * @group server * @covers Guzzle\Stream\Stream */ class StreamTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException \InvalidArgumentException */ public function testConstructorThrowsExceptionOnInvalidArgument() { $stream = new Stream(true); } public function testConstructor() { $handle = fopen('php://temp', 'r+'); fwrite($handle, 'data'); $stream = new Stream($handle); $this->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); } public function testCanModifyStream() { $handle1 = fopen('php://temp', 'r+'); $handle2 = fopen('php://temp', 'r+'); $stream = new Stream($handle1); $this->assertSame($handle1, $stream->getStream()); $stream->setStream($handle2, 10); $this->assertEquals(10, $stream->getSize()); $this->assertSame($handle2, $stream->getStream()); } public function testStreamClosesHandleOnDestruct() { $handle = fopen('php://temp', 'r'); $stream = new Stream($handle); unset($stream); $this->assertFalse(is_resource($handle)); } 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', 'r'); $stream = new Stream($handle); $this->assertEquals('', (string) $stream); unset($stream); } public function testConvertsToStringAndRestoresCursorPos() { $handle = fopen('php://temp', 'w+'); $stream = new Stream($handle); $stream->write('foobazbar'); $stream->seek(3); $this->assertEquals('foobazbar', (string) $stream); $this->assertEquals(3, $stream->ftell()); } 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()); } 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); } 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)); } 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-Length: 0\r\n\r\n"); $handle = fopen('http://127.0.0.1:' . $this->getServer()->getPort(), 'r'); $stream = new Stream($handle); $this->assertEquals(false, $stream->getSize()); unset($stream); } public function testEnsuresSizeIsConsistent() { $h = fopen('php://temp', 'r+'); fwrite($h, 'foo'); $stream = new Stream($h); $this->assertEquals(3, $stream->getSize()); $stream->write('test'); $this->assertEquals(7, $stream->getSize()); fclose($h); } 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()); } public function testDoesNotAttemptToWriteToReadonlyStream() { $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); $stream = new Stream($handle); $this->assertEquals(0, $stream->write('foo')); } public function testProvidesStreamPosition() { $handle = fopen(__DIR__ . '/../../../bootstrap.php', 'r'); $stream = new Stream($handle); $stream->read(2); $this->assertSame(ftell($handle), $stream->ftell()); $this->assertEquals(2, $stream->ftell()); } public function testRewindIsSeekZero() { $stream = new Stream(fopen('php://temp', 'w+')); $stream->write('foobazbar'); $this->assertTrue($stream->rewind()); $this->assertEquals('foobazbar', $stream->read(9)); } public function testCanDetachStream() { $r = fopen('php://temp', 'w+'); $stream = new Stream($r); $stream->detachStream(); $this->assertNull($stream->getStream()); } } guzzle/tests/Guzzle/Tests/Batch/BatchClosureTransferTest.php 0000604 00000002601 15173213567 0020272 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\BatchClosureTransfer; /** * @covers Guzzle\Batch\BatchClosureTransfer */ class BatchClosureTransferTest extends \Guzzle\Tests\GuzzleTestCase { /** @var \Guzzle\Batch\BatchClosureTransfer The transfer fixture */ protected $transferStrategy; /** @var array|null An array for keeping track of items passed into the transfer closure */ protected $itemsTransferred; protected function setUp() { $this->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!'); } } guzzle/tests/Guzzle/Tests/Batch/BatchRequestTransferTest.php 0000604 00000004562 15173213567 0020316 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\BatchRequestTransfer; use Guzzle\Http\Client; use Guzzle\Http\Curl\CurlMulti; /** * @covers Guzzle\Batch\BatchRequestTransfer */ class BatchRequestTransferTest extends \Guzzle\Tests\GuzzleTestCase { public function testCreatesBatchesBasedOnCurlMultiHandles() { $client1 = new Client('http://www.example.com'); $client1->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://127.0.0.1:123'); $request = $client->get(); // For some reason... PHP unit clones the request, which emits a request.clone event. This causes the // 'sorted' property of the event dispatcher to contain an array in the cloned request that is not present in // the original. $request->dispatch('request.clone'); $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()); } } guzzle/tests/Guzzle/Tests/Batch/HistoryBatchTest.php 0000604 00000001302 15173213567 0016607 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\HistoryBatch; use Guzzle\Batch\Batch; /** * @covers Guzzle\Batch\HistoryBatch */ class HistoryBatchTest extends \Guzzle\Tests\GuzzleTestCase { public function testMaintainsHistoryOfItemsAddedToBatch() { $batch = new Batch( $this->getMock('Guzzle\Batch\BatchTransferInterface'), $this->getMock('Guzzle\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()); } } guzzle/tests/Guzzle/Tests/Batch/AbstractBatchDecoratorTest.php 0000604 00000002044 15173213567 0020560 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\Batch; /** * @covers Guzzle\Batch\AbstractBatchDecorator */ class AbstractBatchDecoratorTest extends \Guzzle\Tests\GuzzleTestCase { public function testProxiesToWrappedObject() { $batch = new Batch( $this->getMock('Guzzle\Batch\BatchTransferInterface'), $this->getMock('Guzzle\Batch\BatchDivisorInterface') ); $decoratorA = $this->getMockBuilder('Guzzle\Batch\AbstractBatchDecorator') ->setConstructorArgs(array($batch)) ->getMockForAbstractClass(); $decoratorB = $this->getMockBuilder('Guzzle\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()); } } guzzle/tests/Guzzle/Tests/Batch/FlushingBatchTest.php 0000604 00000002276 15173213567 0016740 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\FlushingBatch; use Guzzle\Batch\Batch; /** * @covers Guzzle\Batch\FlushingBatch */ class FlushingBatchTest extends \Guzzle\Tests\GuzzleTestCase { public function testFlushesWhenSizeMeetsThreshold() { $t = $this->getMock('Guzzle\Batch\BatchTransferInterface', array('transfer')); $d = $this->getMock('Guzzle\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)); } } guzzle/tests/Guzzle/Tests/Batch/NotifyingBatchTest.php 0000604 00000002405 15173213567 0017121 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\NotifyingBatch; use Guzzle\Batch\Batch; /** * @covers Guzzle\Batch\NotifyingBatch */ class NotifyingBatchTest extends \Guzzle\Tests\GuzzleTestCase { public function testNotifiesAfterFlush() { $batch = $this->getMock('Guzzle\Batch\Batch', array('flush'), array( $this->getMock('Guzzle\Batch\BatchTransferInterface'), $this->getMock('Guzzle\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\Batch\BatchTransferInterface'), $this->getMock('Guzzle\Batch\BatchDivisorInterface') ); $decorator = new NotifyingBatch($batch, 'foo'); } } guzzle/tests/Guzzle/Tests/Batch/BatchCommandTransferTest.php 0000604 00000004650 15173213567 0020242 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\BatchCommandTransfer; use Guzzle\Service\Client; use Guzzle\Tests\Service\Mock\Command\MockCommand as Mc; /** * @covers Guzzle\Batch\BatchCommandTransfer */ class BatchCommandTransferTest extends \Guzzle\Tests\GuzzleTestCase { public function testCreatesBatchesBasedOnClient() { $client1 = new Client('http://www.example.com'); $client2 = new Client('http://www.example.com'); $commands = array(new Mc(), new Mc(), new Mc(), new Mc(), new Mc()); $queue = new \SplQueue(); foreach ($commands as $i => $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)); } } guzzle/tests/Guzzle/Tests/Batch/BatchClosureDivisorTest.php 0000604 00000001543 15173213567 0020131 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\BatchClosureDivisor; /** * @covers Guzzle\Batch\BatchClosureDivisor */ class BatchClosureDivisorTest extends \Guzzle\Tests\GuzzleTestCase { /** * @expectedException Guzzle\Common\Exception\InvalidArgumentException */ public function testEnsuresCallableIsCallable() { $d = new BatchClosureDivisor(new \stdClass()); } public function testDividesBatch() { $queue = new \SplQueue(); $queue[] = 'foo'; $queue[] = 'baz'; $d = new BatchClosureDivisor(function (\SplQueue $queue, $context) { return array( array('foo'), array('baz') ); }, 'Bar!'); $batches = $d->createBatches($queue); $this->assertEquals(array(array('foo'), array('baz')), $batches); } } guzzle/tests/Guzzle/Tests/Batch/BatchBuilderTest.php 0000604 00000005041 15173213567 0016540 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\BatchBuilder; /** * @covers Guzzle\Batch\BatchBuilder */ class BatchBuilderTest extends \Guzzle\Tests\GuzzleTestCase { private function getMockTransfer() { return $this->getMock('Guzzle\Batch\BatchTransferInterface'); } private function getMockDivisor() { return $this->getMock('Guzzle\Batch\BatchDivisorInterface'); } private function getMockBatchBuilder() { return BatchBuilder::factory() ->transferWith($this->getMockTransfer()) ->createBatchesWith($this->getMockDivisor()); } public function testFactoryCreatesInstance() { $builder = BatchBuilder::factory(); $this->assertInstanceOf('Guzzle\Batch\BatchBuilder', $builder); } public function testAddsAutoFlush() { $batch = $this->getMockBatchBuilder()->autoFlushAt(10)->build(); $this->assertInstanceOf('Guzzle\Batch\FlushingBatch', $batch); } public function testAddsExceptionBuffering() { $batch = $this->getMockBatchBuilder()->bufferExceptions()->build(); $this->assertInstanceOf('Guzzle\Batch\ExceptionBufferingBatch', $batch); } public function testAddHistory() { $batch = $this->getMockBatchBuilder()->keepHistory()->build(); $this->assertInstanceOf('Guzzle\Batch\HistoryBatch', $batch); } public function testAddsNotify() { $batch = $this->getMockBatchBuilder()->notify(function() {})->build(); $this->assertInstanceOf('Guzzle\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\Batch\BatchRequestTransfer', $this->readAttribute($batch, 'transferStrategy')); } public function testTransfersCommands() { $batch = BatchBuilder::factory()->transferCommands(10)->build(); $this->assertInstanceOf('Guzzle\Batch\BatchCommandTransfer', $this->readAttribute($batch, 'transferStrategy')); } } guzzle/tests/Guzzle/Tests/Batch/BatchTest.php 0000604 00000005425 15173213567 0015237 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\Batch; use Guzzle\Batch\Exception\BatchTransferException; /** * @covers Guzzle\Batch\Batch */ class BatchTest extends \Guzzle\Tests\GuzzleTestCase { private function getMockTransfer() { return $this->getMock('Guzzle\Batch\BatchTransferInterface'); } private function getMockDivisor() { return $this->getMock('Guzzle\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)); } } } guzzle/tests/Guzzle/Tests/Batch/BatchSizeDivisorTest.php 0000604 00000001116 15173213567 0017423 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\BatchSizeDivisor; /** * @covers Guzzle\Batch\BatchSizeDivisor */ class BatchSizeDivisorTest extends \Guzzle\Tests\GuzzleTestCase { public function testDividesBatch() { $queue = new \SplQueue(); $queue[] = 'foo'; $queue[] = 'baz'; $queue[] = 'bar'; $d = new BatchSizeDivisor(3); $this->assertEquals(3, $d->getSize()); $d->setSize(2); $batches = $d->createBatches($queue); $this->assertEquals(array(array('foo', 'baz'), array('bar')), $batches); } } guzzle/tests/Guzzle/Tests/Batch/ExceptionBufferingBatchTest.php 0000604 00000002471 15173213567 0020744 0 ustar 00 <?php namespace Guzzle\Tests\Batch; use Guzzle\Batch\ExceptionBufferingBatch; use Guzzle\Batch\Batch; use Guzzle\Batch\BatchSizeDivisor; /** * @covers Guzzle\Batch\ExceptionBufferingBatch */ class ExceptionBufferingBatchTest extends \Guzzle\Tests\GuzzleTestCase { public function testFlushesEntireBatchWhileBufferingErroredBatches() { $t = $this->getMockBuilder('Guzzle\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); } } guzzle/tests/Guzzle/Tests/Log/MessageFormatterTest.php 0000604 00000011546 15173213567 0017167 0 ustar 00 <?php namespace Guzzle\Tests\Log; use Guzzle\Http\Client; use Guzzle\Http\Curl\CurlHandle; use Guzzle\Http\Message\EntityEnclosingRequest; use Guzzle\Http\EntityBody; use Guzzle\Http\Message\Response; use Guzzle\Log\MessageFormatter; use Guzzle\Plugin\Log\LogPlugin; use Guzzle\Log\ClosureLogAdapter; /** * @covers Guzzle\Log\MessageFormatter */ class MessageFormatterTest extends \Guzzle\Tests\GuzzleTestCase { protected $request; protected $response; protected $handle; public function setUp() { $this->request = new EntityEnclosingRequest('POST', 'http://foo.com?q=test', array( 'X-Foo' => 'bar', 'Authorization' => 'Baz' )); $this->request->setBody(EntityBody::factory('Hello')); $this->response = new Response(200, array( 'X-Test' => 'Abc' ), 'Foo'); $this->handle = $this->getMockBuilder('Guzzle\Http\Curl\CurlHandle') ->disableOriginalConstructor() ->setMethods(array('getError', 'getErrorNo', 'getStderr', 'getInfo')) ->getMock(); $this->handle->expects($this->any()) ->method('getError') ->will($this->returnValue('e')); $this->handle->expects($this->any()) ->method('getErrorNo') ->will($this->returnValue('123')); $this->handle->expects($this->any()) ->method('getStderr') ->will($this->returnValue('testing')); $this->handle->expects($this->any()) ->method('getInfo') ->will($this->returnValueMap(array( array(CURLINFO_CONNECT_TIME, '123'), array(CURLINFO_TOTAL_TIME, '456') ))); } public function logProvider() { return array( // Uses the cache for the second time array('{method} - {method}', 'POST - POST'), array('{url}', 'http://foo.com?q=test'), array('{port}', '80'), array('{resource}', '/?q=test'), array('{host}', 'foo.com'), array('{hostname}', gethostname()), array('{protocol}/{version}', 'HTTP/1.1'), array('{code} {phrase}', '200 OK'), array('{req_header_Foo}', ''), array('{req_header_X-Foo}', 'bar'), array('{req_header_Authorization}', 'Baz'), array('{res_header_foo}', ''), array('{res_header_X-Test}', 'Abc'), array('{req_body}', 'Hello'), array('{res_body}', 'Foo'), array('{curl_stderr}', 'testing'), array('{curl_error}', 'e'), array('{curl_code}', '123'), array('{connect_time}', '123'), array('{total_time}', '456') ); } /** * @dataProvider logProvider */ public function testFormatsMessages($template, $output) { $formatter = new MessageFormatter($template); $this->assertEquals($output, $formatter->format($this->request, $this->response, $this->handle)); } public function testFormatsRequestsAndResponses() { $formatter = new MessageFormatter(); $formatter->setTemplate('{request}{response}'); $this->assertEquals($this->request . $this->response, $formatter->format($this->request, $this->response)); } public function testAddsTimestamp() { $formatter = new MessageFormatter('{ts}'); $this->assertNotEmpty($formatter->format($this->request, $this->response)); } public function testUsesResponseWhenNoHandleAndGettingCurlInformation() { $formatter = new MessageFormatter('{connect_time}/{total_time}'); $response = $this->getMockBuilder('Guzzle\Http\Message\Response') ->setConstructorArgs(array(200)) ->setMethods(array('getInfo')) ->getMock(); $response->expects($this->exactly(2)) ->method('getInfo') ->will($this->returnValueMap(array( array('connect_time', '1'), array('total_time', '2'), ))); $this->assertEquals('1/2', $formatter->format($this->request, $response)); } public function testUsesEmptyStringWhenNoHandleAndNoResponse() { $formatter = new MessageFormatter('{connect_time}/{total_time}'); $this->assertEquals('/', $formatter->format($this->request)); } public function testInjectsTotalTime() { $out = ''; $formatter = new MessageFormatter('{connect_time}/{total_time}'); $adapter = new ClosureLogAdapter(function ($m) use (&$out) { $out .= $m; }); $log = new LogPlugin($adapter, $formatter); $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI"); $client = new Client($this->getServer()->getUrl()); $client->addSubscriber($log); $client->get('/')->send(); $this->assertNotEquals('/', $out); } } guzzle/tests/Guzzle/Tests/Log/ClosureLogAdapterTest.php 0000604 00000001447 15173213567 0017275 0 ustar 00 <?php namespace Guzzle\Tests\Log; use Guzzle\Log\ClosureLogAdapter; /** * @covers Guzzle\Log\ClosureLogAdapter */ class ClosureLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase { public function testClosure() { $that = $this; $modified = null; $this->adapter = new ClosureLogAdapter(function($message, $priority, $extras = null) use ($that, &$modified) { $modified = array($message, $priority, $extras); }); $this->adapter->log('test', LOG_NOTICE, '127.0.0.1'); $this->assertEquals(array('test', LOG_NOTICE, '127.0.0.1'), $modified); } /** * @expectedException InvalidArgumentException */ public function testThrowsExceptionWhenNotCallable() { $this->adapter = new ClosureLogAdapter(123); } } guzzle/tests/Guzzle/Tests/Log/Zf2LogAdapterTest.php 0000604 00000002443 15173213567 0016317 0 ustar 00 <?php namespace Guzzle\Tests\Log; use Guzzle\Log\Zf2LogAdapter; use Zend\Log\Logger; use Zend\Log\Writer\Stream; /** * @covers Guzzle\Log\Zf2LogAdapter */ class Zf2LogAdapterTest extends \Guzzle\Tests\GuzzleTestCase { /** @var Zf2LogAdapter */ protected $adapter; /** @var Logger */ protected $log; /** @var resource */ protected $stream; protected function setUp() { $this->stream = fopen('php://temp', 'r+'); $this->log = new Logger(); $this->log->addWriter(new Stream($this->stream)); $this->adapter = new Zf2LogAdapter($this->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!')); } public function testExposesAdaptedLogObject() { $this->assertEquals($this->log, $this->adapter->getLogObject()); } } guzzle/tests/Guzzle/Tests/Log/PsrLogAdapterTest.php 0000604 00000001206 15173213567 0016416 0 ustar 00 <?php namespace Guzzle\Tests\Log; use Guzzle\Log\PsrLogAdapter; use Monolog\Logger; use Monolog\Handler\TestHandler; /** * @covers Guzzle\Log\PsrLogAdapter * @covers Guzzle\Log\AbstractLogAdapter */ class PsrLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase { public function testLogsMessagesToAdaptedObject() { $log = new Logger('test'); $handler = new TestHandler(); $log->pushHandler($handler); $adapter = new PsrLogAdapter($log); $adapter->log('test!', LOG_INFO); $this->assertTrue($handler->hasInfoRecords()); $this->assertSame($log, $adapter->getLogObject()); } } guzzle/tests/Guzzle/Tests/Log/ArrayLogAdapterTest.php 0000604 00000001213 15173213567 0016726 0 ustar 00 <?php namespace Guzzle\Tests\Log; use Guzzle\Log\ArrayLogAdapter; class ArrayLogAdapterTest extends \Guzzle\Tests\GuzzleTestCase { public function testLog() { $adapter = new ArrayLogAdapter(); $adapter->log('test', \LOG_NOTICE, '127.0.0.1'); $this->assertEquals(array(array('message' => 'test', 'priority' => \LOG_NOTICE, 'extras' => '127.0.0.1')), $adapter->getLogs()); } public function testClearLog() { $adapter = new ArrayLogAdapter(); $adapter->log('test', \LOG_NOTICE, '127.0.0.1'); $adapter->clearLogs(); $this->assertEquals(array(), $adapter->getLogs()); } } guzzle/tests/bootstrap.php 0000604 00000000623 15173213567 0011763 0 ustar 00 <?php error_reporting(E_ALL | E_STRICT); require_once 'PHPUnit/TextUI/TestRunner.php'; require dirname(__DIR__) . '/vendor/autoload.php'; // Add the services file to the default service builder $servicesFile = __DIR__ . '/Guzzle/Tests/TestData/services/services.json'; $builder = Guzzle\Service\Builder\ServiceBuilder::factory($servicesFile); Guzzle\Tests\GuzzleTestCase::setServiceBuilder($builder); guzzle/build.xml 0000604 00000003344 15173213567 0007717 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?> <project name="guzzle" default="test"> <!-- set local values, like git location --> <property file="phing/build.properties.dist" override="true" /> <property file="phing/build.properties" override="true" /> <property name="dir.output" value="${project.basedir}/build/artifacts" /> <property name="dir.imports" value="${project.basedir}/phing/imports" /> <property name="dir.bin" value="${project.basedir}/bin" /> <property name="repo.dir" value="${project.basedir}" /> <import file="${dir.imports}/dependencies.xml"/> <import file="${dir.imports}/deploy.xml"/> <target name="composer-lint" description="lint-check composer.json only"> <composerlint dir="${project.basedir}/src" file="{$project.basedir}/composer.json" /> </target> <target name="test" description="Run unit tests"> <exec passthru="true" command="vendor/bin/phpunit" checkReturn="true" /> </target> <target name="build-init" description="Initialize local phing properties"> <copy file="phing/build.properties.dist" tofile="phing/build.properties" overwrite="false" /> </target> <target name="clean"> <delete dir="${dir.output}"/> <delete dir="${project.basedir}/build/pearwork"/> </target> <target name="prepare" depends="clean,build-init"> <mkdir dir="${dir.output}"/> <mkdir dir="${dir.output}/logs" /> </target> <target name="coverage" depends="prepare"> <exec passthru="true" command="vendor/bin/phpunit --coverage-html=${dir.output}/coverage" /> </target> <target name="view-coverage"> <exec passthru="true" command="open ${dir.output}/coverage/index.html" /> </target> </project> guzzle/README.md 0000604 00000004757 15173213567 0007366 0 ustar 00 Guzzle, PHP HTTP client and webservice framework ================================================ # This is an old version of Guzzle This repository is for Guzzle 3.x. Guzzle 5.x, the new version of Guzzle, has been released and is available at [https://github.com/guzzle/guzzle](https://github.com/guzzle/guzzle). The documentation for Guzzle version 5+ can be found at [http://guzzlephp.org](http://guzzlephp.org). Guzzle 3 is only maintained for bug and security fixes. Guzzle 3 will be EOL at some point in late 2015. ### About Guzzle 3 [](https://packagist.org/packages/guzzle/guzzle) [](http://travis-ci.org/guzzle/guzzle3) - 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. Get answers with: [Documentation](http://guzzle3.readthedocs.org/en/latest/), [Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle), IRC ([#guzzlephp](irc://irc.freenode.net/#guzzlephp) @ irc.freenode.net) ### Installing via Composer The recommended way to install Guzzle is through [Composer](http://getcomposer.org). ```bash # Install Composer curl -sS https://getcomposer.org/installer | php # Add Guzzle as a dependency php composer.phar require guzzle/guzzle:~3.9 ``` After installing, you need to require Composer's autoloader: ```php require 'vendor/autoload.php'; ``` ## Known Issues 1. Problem following a specific redirect: https://github.com/guzzle/guzzle/issues/385. This has been fixed in Guzzle 4/5. 2. Root XML attributes not serialized in a service description: https://github.com/guzzle/guzzle3/issues/5. This has been fixed in Guzzle 4/5. 3. Accept-Encoding not preserved when following redirect: https://github.com/guzzle/guzzle3/issues/9 Fixed in Guzzle 4/5. 4. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 Fixed in Guzzle 4/5. 5. Recursive model references with array items: https://github.com/guzzle/guzzle3/issues/13 Fixed in Guzzle 4/5 6. String "Array" Transmitted w/ PostFiles and Duplicate Aggregator: https://github.com/guzzle/guzzle3/issues/10 Fixed in Guzzle 4/5. guzzle/docs/_templates/index.html 0000604 00000011270 15173213567 0013155 0 ustar 00 <script type="text/javascript" src="{{ pathto('_static/prettify.js', 1) }}"></script> <link rel="stylesheet" type="text/css" href="{{ pathto('_static/prettify.css', 1) }}" /> <link rel="stylesheet" type="text/css" href="{{ pathto('_static/homepage.css', 1) }}" /> <div class="jumbotron masthead"> <div class="container"> <img src="{{ pathto('_static/logo.png', 1) }}" alt="guzzle" width="199" height="260" /> <h1>Guzzle</h1> <p>Guzzle is a PHP HTTP client<br />& framework for building RESTful web service clients.</p> <p> <a class="btn btn-primary btn-lg" href="https://github.com/guzzle/guzzle">View Guzzle on GitHub</a> <a class="btn btn-default btn-lg" href="{{ pathto('docs') }}">Read the docs</a> </p> </div> </div> <div class="social"> <ul class="social-buttons"> <li> <iframe src="http://ghbtns.com/github-btn.html?user=guzzle&repo=guzzle&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="110" height="20"></iframe> </li> <li> <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://guzzlephp.org" data-text="Guzzle, PHP HTTP client & framework for building RESTful web service clients" data-via="mtdowling">Tweet</a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="http://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script> </li> <li> <a href="https://twitter.com/mtdowling" class="twitter-follow-button" data-show-count="false">Follow @mtdowling</a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="http://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script> </li> </ul> </div> <div class="container"> <h1>Introducing Guzzle</h1> <p>Guzzle takes the pain out of sending HTTP requests and the redundancy out of creating web service clients. It's a framework that includes the tools needed to create a robust web service client, including: Service descriptions for defining the inputs and outputs of an API, resource iterators for traversing paginated resources, batching for sending a large number of requests as efficiently as possible.</p> <ul> <li>All the power of cURL with a simple interface.</li> <li>Persistent connections and parallel requests.</li> <li>Streams request and response bodies</li> <li><a href="{{ pathto('webservice-client/guzzle-service-descriptions') }}">Service descriptions</a> for quickly building clients.</li> <li>Powered by the Symfony2 EventDispatcher.</li> <li>Use all of the code or only <a href="https://packagist.org/packages/guzzle/">specific components</a>.</li> <li><a href="{{ pathto('plugins/plugins-overview') }}">Plugins</a> for caching, logging, OAuth, mocks, and more</li> <li>Includes a custom node.js webserver to <a href="{{ pathto('testing/unit-testing') }}">test your clients</a>.</li> </ul> <div class="center-announcement"> Guzzle is now part of Drupal 8 core and powers the official <a href="https://github.com/aws/aws-sdk-php">AWS SDK for PHP</a> </div> <h2>GitHub Example</h2> <pre class="prettyprint"><?php require_once 'vendor/autoload.php'; use Guzzle\Http\Client; // Create a client and provide a base URL $client = new Client('https://api.github.com'); // Create a request with basic Auth $request = $client->get('/user')->setAuth('user', 'pass'); // Send the request and get the response $response = $request->send(); echo $response->getBody(); // >>> {"type":"User", ... echo $response->getHeader('Content-Length'); // >>> 792 </pre> <h2>Twitter Example</h2> <pre class="prettyprint"><?php // Create a client to work with the Twitter API $client = new Client('https://api.twitter.com/{version}', array( 'version' => '1.1' )); // Sign all requests with the OauthPlugin $client->addSubscriber(new Guzzle\Plugin\Oauth\OauthPlugin(array( 'consumer_key' => '***', 'consumer_secret' => '***', 'token' => '***', 'token_secret' => '***' ))); echo $client->get('statuses/user_timeline.json')->send()->getBody(); // >>> {"public_gists":6,"type":"User" ... // Create a tweet using POST $request = $client->post('statuses/update.json', null, array( 'status' => 'Tweeted with Guzzle, http://guzzlephp.org' )); // Send the request and parse the JSON response into an array $data = $request->send()->json(); echo $data['text']; // >>> Tweeted with Guzzle, http://t.co/kngJMfRk </pre> </div> <script type="text/javascript">prettyPrint();</script> guzzle/docs/_templates/nav_links.html 0000604 00000000474 15173213567 0014036 0 ustar 00 <li><a href="{{ pathto('docs') }}">Docs</a></li> <li><a href="http://guzzlephp.org/api/index.html">API</a></li> <li><a href="https://github.com/guzzle/guzzle">GitHub</a></li> <li><a href="https://groups.google.com/forum/?hl=en#!forum/guzzle">Forum</a></li> <li><a href="irc:irc.freenode.com/#guzzlephp">IRC</a></li> guzzle/docs/_templates/leftbar.html 0000604 00000000000 15173213567 0013452 0 ustar 00 guzzle/docs/testing/unit-testing.rst 0000604 00000023027 15173213567 0013667 0 ustar 00 =========================== Unit Testing Guzzle clients =========================== Guzzle provides several tools that will enable you to easily unit test your web service clients. * PHPUnit integration * Mock responses * node.js web server for integration testing PHPUnit integration ------------------- Guzzle is unit tested using `PHPUnit <http://www.phpunit.de/>`_. Your web service client's unit tests should extend ``Guzzle\Tests\GuzzleTestCase`` so that you can take advantage of some of the built in helpers. In order to unit test your client, a developer would need to copy phpunit.xml.dist to phpunit.xml and make any needed modifications. As a best practice and security measure for you and your contributors, it is recommended to add an ignore statement to your SCM so that phpunit.xml is ignored. Bootstrapping ~~~~~~~~~~~~~ Your web service client should have a tests/ folder that contains a bootstrap.php file. The bootstrap.php file responsible for autoloading and configuring a ``Guzzle\Service\Builder\ServiceBuilder`` that is used throughout your unit tests for loading a configured client. You can add custom parameters to your phpunit.xml file that expects users to provide the path to their configuration data. .. code-block:: php Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Aws\Common\Aws::factory($_SERVER['CONFIG'])); Guzzle\Tests\GuzzleTestCase::setServiceBuilder(Guzzle\Service\Builder\ServiceBuilder::factory(array( 'test.unfuddle' => array( 'class' => 'Guzzle.Unfuddle.UnfuddleClient', 'params' => array( 'username' => 'test_user', 'password' => '****', 'subdomain' => 'test' ) ) ))); The above code registers a service builder that can be used throughout your unit tests. You would then be able to retrieve an instantiated and configured Unfuddle client by calling ``$this->getServiceBuilder()->get('test.unfuddle)``. The above code assumes that ``$_SERVER['CONFIG']`` contains the path to a file that stores service description configuration. Unit testing remote APIs ------------------------ Mock responses ~~~~~~~~~~~~~~ One of the benefits of unit testing is the ability to quickly determine if there are errors in your code. If your unit tests run slowly, then they become tedious and will likely be run less frequently. Guzzle's philosophy on unit testing web service clients is that no network access should be required to run the unit tests. This means that responses are served from mock responses or local servers. By adhering to this principle, tests will run much faster and will not require an external resource to be available. The problem with this approach is that your mock responses must first be gathered and then subsequently updated each time the remote API changes. Integration testing over the internet ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can perform integration testing with a web service over the internet by making calls directly to the service. If the web service you are requesting uses a complex signing algorithm or some other specific implementation, then you may want to include at least one actual network test that can be run specifically through the command line using `PHPUnit group annotations <http://www.phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group>`_. @group internet annotation ^^^^^^^^^^^^^^^^^^^^^^^^^^ When creating tests that require an internet connection, it is recommended that you add ``@group internet`` annotations to your unit tests to specify which tests require network connectivity. You can then `run PHPUnit tests <http://www.phpunit.de/manual/current/en/textui.html>`_ that exclude the @internet group by running ``phpunit --exclude-group internet``. API credentials ^^^^^^^^^^^^^^^ If API credentials are required to run your integration tests, you must add ``<php>`` parameters to your phpunit.xml.dist file and extract these parameters in your bootstrap.php file. .. code-block:: xml <?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="./tests/bootstrap.php" colors="true"> <php> <!-- Specify the path to a service configuration file --> <server name="CONFIG" value="test_services.json" /> <!-- Or, specify each require parameter individually --> <server name="API_USER" value="change_me" /> <server name="API_PASSWORD" value="****" /> </php> <testsuites> <testsuite name="guzzle-service"> <directory suffix="Test.php">./Tests</directory> </testsuite> </testsuites> </phpunit> You can then extract the ``server`` variables in your bootstrap.php file by grabbing them from the ``$_SERVER`` superglobal: ``$apiUser = $_SERVER['API_USER'];`` Further reading ^^^^^^^^^^^^^^^ A good discussion on the topic of testing remote APIs can be found in Sebastian Bergmann's `Real-World Solutions for Developing High-Quality PHP Frameworks and Applications <http://www.amazon.com/dp/0470872497>`_. Queueing Mock responses ----------------------- Mock responses can be used to test if requests are being generated correctly and responses and handled correctly by your client. Mock responses can be queued up for a client using the ``$this->setMockResponse($client, $path)`` method of your test class. Pass the client you are adding mock responses to and a single path or array of paths to mock response files relative to the ``/tests/mock/ folder``. This will queue one or more mock responses for your client by creating a simple observer on the client. Mock response files must contain a full HTTP response message: .. code-block:: none HTTP/1.1 200 OK Date: Wed, 25 Nov 2009 12:00:00 GMT Connection: close Server: AmazonS3 Content-Type: application/xml <?xml version="1.0" encoding="UTF-8"?> <LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/">EU</LocationConstraint> After queuing mock responses for a client, you can get an array of the requests that were sent by the client that were issued a mock response by calling ``$this->getMockedRequests()``. You can also use the ``Guzzle\Plugin\Mock\MockPlugin`` object directly with your clients. .. code-block:: php $plugin = new Guzzle\Plugin\Mock\MockPlugin(); $plugin->addResponse(new Guzzle\Http\Message\Response(200)); $client = new Guzzle\Http\Client(); $client->addSubscriber($plugin); // The following request will get the mock response from the plugin in FIFO order $request = $client->get('http://www.test.com/'); $request->send(); // The MockPlugin maintains a list of requests that were mocked $this->assertContainsOnly($request, $plugin->getReceivedRequests()); node.js web server for integration testing ------------------------------------------ Using mock responses is usually enough when testing a web service client. If your client needs to add custom cURL options to requests, then you should use the node.js test web server to ensure that your HTTP request message is being created correctly. Guzzle is based around PHP's libcurl bindings. cURL sometimes modifies an HTTP request message based on ``CURLOPT_*`` options. Headers that are added to your request by cURL will not be accounted for if you inject mock responses into your tests. Additionally, some request entity bodies cannot be loaded by the client before transmitting it to the sever (for example, when using a client as a sort of proxy and streaming content from a remote server). You might also need to inspect the entity body of a ``multipart/form-data`` POST request. .. note:: You can skip all of the tests that require the node.js test web server by excluding the ``server`` group: ``phpunit --exclude-group server`` Using the test server ~~~~~~~~~~~~~~~~~~~~~ The node.js test server receives requests and returns queued responses. The test server exposes a simple API that is used to enqueue responses and inspect the requests that it has received. Retrieve the server object by calling ``$this->getServer()``. If the node.js server is not running, it will be started as a forked process and an object that interfaces with the server will be returned. (note: stopping the server is handled internally by Guzzle.) You can queue an HTTP response or an array of responses by calling ``$this->getServer()->enqueue()``: .. code-block:: php $this->getServer()->enqueue("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"); The above code queues a single 200 response with an empty body. Responses are queued using a FIFO order; this response will be returned by the server when it receives the first request and then removed from the queue. If a request is received by a server with no queued responses, an exception will be thrown in your unit test. You can inspect the requests that the server has retrieved by calling ``$this->getServer()->getReceivedRequests()``. This method accepts an optional ``$hydrate`` parameter that specifies if you are retrieving an array of string HTTP requests or an array of ``Guzzle\Http\RequestInterface`` subclassed objects. "Hydrating" the requests will allow greater flexibility in your unit tests so that you can easily assert the state of the various parts of a request. You will need to modify the base_url of your web service client in order to use it against the test server. .. code-block:: php $client = $this->getServiceBuilder()->get('my_client'); $client->setBaseUrl($this->getServer()->getUrl()); After running the above code, all calls made from the ``$client`` object will be sent to the test web server. guzzle/docs/Makefile 0000604 00000012674 15173213567 0010474 0 ustar 00 # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make <target>' where <target> is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Guzzle.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Guzzle.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Guzzle" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Guzzle" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." guzzle/docs/conf.py 0000604 00000005747 15173213567 0010336 0 ustar 00 import sys, os from sphinx.highlighting import lexers from pygments.lexers.web import PhpLexer lexers['php'] = PhpLexer(startinline=True, linenos=1) lexers['php-annotations'] = PhpLexer(startinline=True, linenos=1) primary_domain = 'php' # -- General configuration ----------------------------------------------------- extensions = [] templates_path = ['_templates'] source_suffix = '.rst' master_doc = 'index' project = u'Guzzle' copyright = u'2012, Michael Dowling' version = '3.0.0' release = '3.0.0' exclude_patterns = ['_build'] # -- Options for HTML output --------------------------------------------------- # The name for this set of Sphinx documents. If None, it defaults to # "<project> v<release> documentation". html_title = "Guzzle documentation" html_short_title = "Guzzle" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, maps document names to template names. html_sidebars = { '**': ['localtoc.html', 'leftbar.html', 'searchbox.html'] } # Output file base name for HTML help builder. htmlhelp_basename = 'Guzzledoc' # -- Guzzle Sphinx theme setup ------------------------------------------------ sys.path.insert(0, '/Users/dowling/projects/guzzle_sphinx_theme') import guzzle_sphinx_theme html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' html_theme_path = guzzle_sphinx_theme.html_theme_path() html_theme = 'guzzle_sphinx_theme' # Guzzle theme options (see theme.conf for more information) html_theme_options = { "index_template": "index.html", "project_nav_name": "Guzzle", "github_user": "guzzle", "github_repo": "guzzle", "disqus_comments_shortname": "guzzle", "google_analytics_account": "UA-22752917-1" } # -- Options for LaTeX output -------------------------------------------------- latex_elements = {} # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'Guzzle.tex', u'Guzzle Documentation', u'Michael Dowling', 'manual'), ] # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'guzzle', u'Guzzle Documentation', [u'Michael Dowling'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Guzzle', u'Guzzle Documentation', u'Michael Dowling', 'Guzzle', 'One line description of project.', 'Miscellaneous'), ] guzzle/docs/http-client/uri-templates.rst 0000604 00000004434 15173213567 0014607 0 ustar 00 ============= URI templates ============= The ``$uri`` passed to one of the client's request creational methods or the base URL of a client can utilize URI templates. Guzzle supports the entire `URI templates RFC <http://tools.ietf.org/html/rfc6570>`_. URI templates add a special syntax to URIs that replace template place holders with user defined variables. Every request created by a Guzzle HTTP client passes through a URI template so that URI template expressions are automatically expanded: .. code-block:: php $client = new Guzzle\Http\Client('https://example.com/', array('a' => 'hi')); $request = $client->get('/{a}'); Because of URI template expansion, the URL of the above request will become ``https://example.com/hi``. Notice that the template was expanded using configuration variables of the client. You can pass in custom URI template variables by passing the URI of your request as an array where the first index of the array is the URI template and the second index of the array are template variables that are merged into the client's configuration variables. .. code-block:: php $request = $client->get(array('/test{?a,b}', array('b' => 'there'))); The URL for this request will become ``https://test.com?a=hi&b=there``. URI templates aren't limited to just simple variable replacements; URI templates can provide an enormous amount of flexibility when creating request URIs. .. code-block:: php $request = $client->get(array('http://example.com{+path}{/segments*}{?query,data*}', array( 'path' => '/foo/bar', 'segments' => array('one', 'two'), 'query' => 'test', 'data' => array( 'more' => 'value' ) ))); The resulting URL would become ``http://example.com/foo/bar/one/two?query=test&more=value``. By default, URI template expressions are enclosed in an opening and closing brace (e.g. ``{var}``). If you are working with a web service that actually uses braces (e.g. Solr), then you can specify a custom regular expression to use to match URI template expressions. .. code-block:: php $client->getUriTemplate()->setRegex('/\<\$(.+)\>/'); $client->get('/<$a>'); You can learn about all of the different features of URI templates by reading the `URI templates RFC <http://tools.ietf.org/html/rfc6570>`_. guzzle/docs/http-client/client.rst 0000604 00000056204 15173213567 0013274 0 ustar 00 ====================== The Guzzle HTTP client ====================== Guzzle gives PHP developers complete control over HTTP requests while utilizing HTTP/1.1 best practices. Guzzle's HTTP functionality is a robust framework built on top of the `PHP libcurl bindings <http://www.php.net/curl>`_. The three main parts of the Guzzle HTTP client are: +--------------+-------------------------------------------------------------------------------------------------------+ | Clients | ``Guzzle\Http\Client`` (creates and sends requests, associates a response with a request) | +--------------+-------------------------------------------------------------------------------------------------------+ | Requests | ``Guzzle\Http\Message\Request`` (requests with no body), | | | ``Guzzle\Http\Message\EntityEnclosingRequest`` (requests with a body) | +--------------+-------------------------------------------------------------------------------------------------------+ | Responses | ``Guzzle\Http\Message\Response`` | +--------------+-------------------------------------------------------------------------------------------------------+ Creating a Client ----------------- Clients create requests, send requests, and set responses on a request object. When instantiating a client object, you can pass an optional "base URL" and optional array of configuration options. A base URL is a :doc:`URI template <uri-templates>` that contains the URL of a remote server. When creating requests with a relative URL, the base URL of a client will be merged into the request's URL. .. code-block:: php use Guzzle\Http\Client; // Create a client and provide a base URL $client = new Client('https://api.github.com'); $request = $client->get('/user'); $request->setAuth('user', 'pass'); echo $request->getUrl(); // >>> https://api.github.com/user // You must send a request in order for the transfer to occur $response = $request->send(); echo $response->getBody(); // >>> {"type":"User", ... echo $response->getHeader('Content-Length'); // >>> 792 $data = $response->json(); echo $data['type']; // >>> User Base URLs ~~~~~~~~~ Notice that the URL provided to the client's ``get()`` method is relative. Relative URLs will always merge into the base URL of the client. There are a few rules that control how the URLs are merged. .. tip:: Guzzle follows `RFC 3986 <http://tools.ietf.org/html/rfc3986#section-5.2>`_ when merging base URLs and relative URLs. In the above example, we passed ``/user`` to the ``get()`` method of the client. This is a relative URL, so it will merge into the base URL of the client-- resulting in the derived URL of ``https://api.github.com/users``. ``/user`` is a relative URL but uses an absolute path because it contains the leading slash. Absolute paths will overwrite any existing path of the base URL. If an absolute path is provided (e.g. ``/path/to/something``), then the path specified in the base URL of the client will be replaced with the absolute path, and the query string provided by the relative URL will replace the query string of the base URL. Omitting the leading slash and using relative paths will add to the path of the base URL of the client. So using a client base URL of ``https://api.twitter.com/v1.1`` and creating a GET request with ``statuses/user_timeline.json`` will result in a URL of ``https://api.twitter.com/v1.1/statuses/user_timeline.json``. If a relative path and a query string are provided, then the relative path will be appended to the base URL path, and the query string provided will be merged into the query string of the base URL. If an absolute URL is provided (e.g. ``http://httpbin.org/ip``), then the request will completely use the absolute URL as-is without merging in any of the URL parts specified in the base URL. Configuration options ~~~~~~~~~~~~~~~~~~~~~ The second argument of the client's constructor is an array of configuration data. This can include URI template data or special options that alter the client's behavior: +-------------------------------+-------------------------------------------------------------------------------------+ | ``request.options`` | Associative array of :ref:`Request options <request-options>` to apply to every | | | request created by the client. | +-------------------------------+-------------------------------------------------------------------------------------+ | ``redirect.disable`` | Disable HTTP redirects for every request created by the client. | +-------------------------------+-------------------------------------------------------------------------------------+ | ``curl.options`` | Associative array of cURL options to apply to every request created by the client. | | | if either the key or value of an entry in the array is a string, Guzzle will | | | attempt to find a matching defined cURL constant automatically (e.g. | | | "CURLOPT_PROXY" will be converted to the constant ``CURLOPT_PROXY``). | +-------------------------------+-------------------------------------------------------------------------------------+ | ``ssl.certificate_authority`` | Set to true to use the Guzzle bundled SSL certificate bundle (this is used by | | | default, 'system' to use the bundle on your system, a string pointing to a file to | | | use a specific certificate file, a string pointing to a directory to use multiple | | | certificates, or ``false`` to disable SSL validation (not recommended). | | | | | | When using Guzzle inside of a phar file, the bundled SSL certificate will be | | | extracted to your system's temp folder, and each time a client is created an MD5 | | | check will be performed to ensure the integrity of the certificate. | +-------------------------------+-------------------------------------------------------------------------------------+ | ``command.params`` | When using a ``Guzzle\Service\Client`` object, this is an associative array of | | | default options to set on each command created by the client. | +-------------------------------+-------------------------------------------------------------------------------------+ Here's an example showing how to set various configuration options, including default headers to send with each request, default query string parameters to add to each request, a default auth scheme for each request, and a proxy to use for each request. Values can be injected into the client's base URL using variables from the configuration array. .. code-block:: php use Guzzle\Http\Client; $client = new Client('https://api.twitter.com/{version}', array( 'version' => 'v1.1', 'request.options' => array( 'headers' => array('Foo' => 'Bar'), 'query' => array('testing' => '123'), 'auth' => array('username', 'password', 'Basic|Digest|NTLM|Any'), 'proxy' => 'tcp://localhost:80' ) )); Setting a custom User-Agent ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The default Guzzle User-Agent header is ``Guzzle/<Guzzle_Version> curl/<curl_version> PHP/<PHP_VERSION>``. You can customize the User-Agent header of a client by calling the ``setUserAgent()`` method of a Client object. .. code-block:: php // Completely override the default User-Agent $client->setUserAgent('Test/123'); // Prepend a string to the default User-Agent $client->setUserAgent('Test/123', true); Creating requests with a client ------------------------------- A Client object exposes several methods used to create Request objects: * Create a custom HTTP request: ``$client->createRequest($method, $uri, array $headers, $body, $options)`` * Create a GET request: ``$client->get($uri, array $headers, $options)`` * Create a HEAD request: ``$client->head($uri, array $headers, $options)`` * Create a DELETE request: ``$client->delete($uri, array $headers, $body, $options)`` * Create a POST request: ``$client->post($uri, array $headers, $postBody, $options)`` * Create a PUT request: ``$client->put($uri, array $headers, $body, $options)`` * Create a PATCH request: ``$client->patch($uri, array $headers, $body, $options)`` .. code-block:: php use Guzzle\Http\Client; $client = new Client('http://baseurl.com/api/v1'); // Create a GET request using Relative to base URL // URL of the request: http://baseurl.com/api/v1/path?query=123&value=abc) $request = $client->get('path?query=123&value=abc'); $response = $request->send(); // Create HEAD request using a relative URL with an absolute path // URL of the request: http://baseurl.com/path?query=123&value=abc $request = $client->head('/path?query=123&value=abc'); $response = $request->send(); // Create a DELETE request using an absolute URL $request = $client->delete('http://www.example.com/path?query=123&value=abc'); $response = $request->send(); // Create a PUT request using the contents of a PHP stream as the body // Specify custom HTTP headers $request = $client->put('http://www.example.com/upload', array( 'X-Header' => 'My Header' ), fopen('http://www.test.com/', 'r')); $response = $request->send(); // 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')); $response = $request->send(); // Check if a resource supports the DELETE method $supportsDelete = $client->options('/path')->send()->isMethodAllowed('DELETE'); $response = $request->send(); Client objects create Request objects using a request factory (``Guzzle\Http\Message\RequestFactoryInterface``). You can inject a custom request factory into the Client using ``$client->setRequestFactory()``, but you can typically rely on a Client's default request factory. Static clients -------------- You can use Guzzle's static client facade to more easily send simple HTTP requests. .. code-block:: php // Mount the client so that you can access it at \Guzzle Guzzle\Http\StaticClient::mount(); $response = Guzzle::get('http://guzzlephp.org'); Each request method of the static client (e.g. ``get()``, ``post()`, ``put()``, etc) accepts an associative array of request options to apply to the request. .. code-block:: php $response = Guzzle::post('http://test.com', array( 'headers' => array('X-Foo' => 'Bar'), 'body' => array('Test' => '123'), 'timeout' => 10 )); .. _request-options: Request options --------------- Request options can be specified when creating a request or in the ``request.options`` parameter of a client. These options can control various aspects of a request including: headers to send, query string data, where the response should be downloaded, proxies, auth, etc. headers ~~~~~~~ Associative array of headers to apply to the request. When specified in the ``$options`` argument of a client creational method (e.g. ``get()``, ``post()``, etc), the headers in the ``$options`` array will overwrite headers specified in the ``$headers`` array. .. code-block:: php $request = $client->get($url, array(), array( 'headers' => array('X-Foo' => 'Bar') )); Headers can be specified on a client to add default headers to every request sent by a client. .. code-block:: php $client = new Guzzle\Http\Client(); // Set a single header using path syntax $client->setDefaultOption('headers/X-Foo', 'Bar'); // Set all headers $client->setDefaultOption('headers', array('X-Foo' => 'Bar')); .. note:: In addition to setting request options when creating requests or using the ``setDefaultOption()`` method, any default client request option can be set using a client's config object: .. code-block:: php $client->getConfig()->setPath('request.options/headers/X-Foo', 'Bar'); query ~~~~~ Associative array of query string parameters to the request. When specified in the ``$options`` argument of a client creational method, the query string parameters in the ``$options`` array will overwrite query string parameters specified in the `$url`. .. code-block:: php $request = $client->get($url, array(), array( 'query' => array('abc' => '123') )); Query string parameters can be specified on a client to add default query string parameters to every request sent by a client. .. code-block:: php $client = new Guzzle\Http\Client(); // Set a single query string parameter using path syntax $client->setDefaultOption('query/abc', '123'); // Set an array of default query string parameters $client->setDefaultOption('query', array('abc' => '123')); body ~~~~ Sets the body of a request. The value supplied to the body option can be a ``Guzzle\Http\EntityBodyInterface``, string, fopen resource, or array when sending POST requests. When a ``body`` request option is supplied, the option value will overwrite the ``$body`` argument of a client creational method. auth ~~~~ Specifies and array of HTTP authorization parameters parameters to use with the request. The array must contain the username in index [0], the password in index [1], and can optionally contain the authentication type in index [2]. The available authentication types are: "Basic" (default), "Digest", "NTLM", or "Any". .. code-block:: php $request = $client->get($url, array(), array( 'auth' => array('username', 'password', 'Digest') )); // You can add auth headers to every request of a client $client->setDefaultOption('auth', array('username', 'password', 'Digest')); cookies ~~~~~~~ Specifies an associative array of cookies to add to the request. allow_redirects ~~~~~~~~~~~~~~~ Specifies whether or not the request should follow redirects. Requests will follow redirects by default. Set ``allow_redirects`` to ``false`` to disable redirects. save_to ~~~~~~~ The ``save_to`` option specifies where the body of a response is downloaded. You can pass the path to a file, an fopen resource, or a ``Guzzle\Http\EntityBodyInterface`` object. See :ref:`Changing where a response is downloaded <request-set-response-body>` for more information on setting the `save_to` option. events ~~~~~~ The `events` option makes it easy to attach listeners to the various events emitted by a request object. The `events` options must be an associative array mapping an event name to a Closure or array the contains a Closure and the priority of the event. .. code-block:: php $request = $client->get($url, array(), array( 'events' => array( 'request.before_send' => function (\Guzzle\Common\Event $e) { echo 'About to send ' . $e['request']; } ) )); // Using the static client: Guzzle::get($url, array( 'events' => array( 'request.before_send' => function (\Guzzle\Common\Event $e) { echo 'About to send ' . $e['request']; } ) )); plugins ~~~~~~~ The `plugins` options makes it easy to attach an array of plugins to a request. .. code-block:: php // Using the static client: Guzzle::get($url, array( 'plugins' => array( new Guzzle\Plugin\Cache\CachePlugin(), new Guzzle\Plugin\Cookie\CookiePlugin() ) )); exceptions ~~~~~~~~~~ The `exceptions` option can be used to disable throwing exceptions for unsuccessful HTTP response codes (e.g. 404, 500, etc). Set `exceptions` to false to not throw exceptions. params ~~~~~~ The `params` options can be used to specify an associative array of data parameters to add to a request. Note that these are not query string parameters. timeout / connect_timeout ~~~~~~~~~~~~~~~~~~~~~~~~~ You can specify the maximum number of seconds to allow for an entire transfer to take place before timing out using the `timeout` request option. You can specify the maximum number of seconds to wait while trying to connect using the `connect_timeout` request option. Set either of these options to 0 to wait indefinitely. .. code-block:: php $request = $client->get('http://www.example.com', array(), array( 'timeout' => 20, 'connect_timeout' => 1.5 )); verify ~~~~~~ Set to true to enable SSL certificate validation (the default), false to disable SSL certificate validation, or supply the path to a CA bundle to enable verification using a custom certificate. cert ~~~~ The `cert` option lets you specify a PEM formatted SSL client certificate to use with servers that require one. If the certificate requires a password, provide an array with the password as the second item. This would typically be used in conjunction with the `ssl_key` option. .. code-block:: php $request = $client->get('https://www.example.com', array(), array( 'cert' => '/etc/pki/client_certificate.pem' ) $request = $client->get('https://www.example.com', array(), array( 'cert' => array('/etc/pki/client_certificate.pem', 's3cr3tp455w0rd') ) ssl_key ~~~~~~~ The `ssl_key` option lets you specify a file containing your PEM formatted private key, optionally protected by a password. Note: your password is sensitive, keep the PHP script containing it safe. This would typically be used in conjunction with the `cert` option. .. code-block:: php $request = $client->get('https://www.example.com', array(), array( 'ssl_key' => '/etc/pki/private_key.pem' ) $request = $client->get('https://www.example.com', array(), array( 'ssl_key' => array('/etc/pki/private_key.pem', 's3cr3tp455w0rd') ) proxy ~~~~~ The `proxy` option is used to specify an HTTP proxy (e.g. `http://username:password@192.168.16.1:10`). debug ~~~~~ The `debug` option is used to show verbose cURL output for a transfer. stream ~~~~~~ When using a static client, you can set the `stream` option to true to return a `Guzzle\Stream\Stream` object that can be used to pull data from a stream as needed (rather than have cURL download the entire contents of a response to a stream all at once). .. code-block:: php $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); while (!$stream->feof()) { echo $stream->readLine(); } Sending requests ---------------- Requests can be sent by calling the ``send()`` method of a Request object, but you can also send requests using the ``send()`` method of a Client. .. code-block:: php $request = $client->get('http://www.amazon.com'); $response = $client->send($request); Sending requests in parallel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Client's ``send()`` method accept a single ``Guzzle\Http\Message\RequestInterface`` object or an array of RequestInterface objects. When an array is specified, the requests will be sent in parallel. Sending many HTTP requests serially (one at a time) can cause an unnecessary delay in a script's execution. Each request must complete before a subsequent request can be sent. By sending requests in parallel, a pool of HTTP requests can complete at the speed of the slowest request in the pool, significantly reducing the amount of time needed to execute multiple HTTP requests. Guzzle provides a wrapper for the curl_multi functions in PHP. Here's an example of sending three requests in parallel using a client object: .. code-block:: php use Guzzle\Common\Exception\MultiTransferException; try { $responses = $client->send(array( $client->get('http://www.google.com/'), $client->head('http://www.google.com/'), $client->get('https://www.github.com/') )); } catch (MultiTransferException $e) { echo "The following exceptions were encountered:\n"; foreach ($e as $exception) { echo $exception->getMessage() . "\n"; } echo "The following requests failed:\n"; foreach ($e->getFailedRequests() as $request) { echo $request . "\n\n"; } echo "The following requests succeeded:\n"; foreach ($e->getSuccessfulRequests() as $request) { echo $request . "\n\n"; } } If the requests succeed, an array of ``Guzzle\Http\Message\Response`` objects are returned. A single request failure will not cause the entire pool of requests to fail. Any exceptions thrown while transferring a pool of requests will be aggregated into a ``Guzzle\Common\Exception\MultiTransferException`` exception. Plugins and events ------------------ Guzzle provides easy to use request plugins that add behavior to requests based on signal slot event notifications powered by the `Symfony2 Event Dispatcher component <http://symfony.com/doc/2.0/components/event_dispatcher/introduction.html>`_. Any event listener or subscriber attached to a Client object will automatically be attached to each request created by the client. Using the same cookie session for each request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Attach a ``Guzzle\Plugin\Cookie\CookiePlugin`` to a client which will in turn add support for cookies to every request created by a client, and each request will use the same cookie session: .. code-block:: php use Guzzle\Plugin\Cookie\CookiePlugin; use Guzzle\Plugin\Cookie\CookieJar\ArrayCookieJar; // Create a new cookie plugin $cookiePlugin = new CookiePlugin(new ArrayCookieJar()); // Add the cookie plugin to the client $client->addSubscriber($cookiePlugin); .. _client-events: Events emitted from a client ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A ``Guzzle\Http\Client`` object emits the following events: +------------------------------+--------------------------------------------+------------------------------------------+ | Event name | Description | Event data | +==============================+============================================+==========================================+ | client.create_request | Called when a client creates a request | * client: The client | | | | * request: The created request | +------------------------------+--------------------------------------------+------------------------------------------+ .. code-block:: php use Guzzle\Common\Event; use Guzzle\Http\Client; $client = new Client(); // Add a listener that will echo out requests as they are created $client->getEventDispatcher()->addListener('client.create_request', function (Event $e) { echo 'Client object: ' . spl_object_hash($e['client']) . "\n"; echo "Request object: {$e['request']}\n"; }); guzzle/docs/http-client/response.rst 0000604 00000011364 15173213567 0013652 0 ustar 00 ====================== Using Response objects ====================== Sending a request will return a ``Guzzle\Http\Message\Response`` object. You can view the raw HTTP response message by casting the Response object to a string. Casting the response to a string will return the entity body of the response as a string too, so this might be an expensive operation if the entity body is stored in a file or network stream. If you only want to see the response headers, you can call ``getRawHeaders()``. Response status line -------------------- The different parts of a response's `status line <http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1>`_ (the first line of the response HTTP message) are easily retrievable. .. code-block:: php $response = $client->get('http://www.amazon.com')->send(); echo $response->getStatusCode(); // >>> 200 echo $response->getReasonPhrase(); // >>> OK echo $response->getProtocol(); // >>> HTTP echo $response->getProtocolVersion(); // >>> 1.1 You can determine the type of the response using several helper methods: .. code-block:: php $response->isSuccessful(); // true $response->isInformational(); $response->isRedirect(); $response->isClientError(); $response->isServerError(); Response headers ---------------- The Response object contains helper methods for retrieving common response headers. These helper methods normalize the variations of HTTP response headers. .. code-block:: php $response->getCacheControl(); $response->getContentType(); $response->getContentLength(); $response->getContentEncoding(); $response->getContentMd5(); $response->getEtag(); // etc... There are methods for every known response header You can interact with the Response headers using the same exact methods used to interact with Request headers. See :ref:`http-message-headers` for more information. .. code-block:: php echo $response->getHeader('Content-Type'); echo $response->getHeader('Content-Length'); echo $response->getHeaders()['Content-Type']; // PHP 5.4 Response body ------------- The entity body object of a response can be retrieved by calling ``$response->getBody()``. The response EntityBody can be cast to a string, or you can pass ``true`` to this method to retrieve the body as a string. .. code-block:: php $request = $client->get('http://www.amazon.com'); $response = $request->send(); echo $response->getBody(); See :doc:`/http-client/entity-bodies` for more information on entity bodies. JSON Responses ~~~~~~~~~~~~~~ You can easily parse and use a JSON response as an array using the ``json()`` method of a response. This method will always return an array if the response is valid JSON or if the response body is empty. You will get an exception if you call this method and the response is not valid JSON. .. code-block:: php $data = $response->json(); echo gettype($data); // >>> array XML Responses ~~~~~~~~~~~~~ You can easily parse and use a XML response as SimpleXMLElement object using the ``xml()`` method of a response. This method will always return a SimpleXMLElement object if the response is valid XML or if the response body is empty. You will get an exception if you call this method and the response is not valid XML. .. code-block:: php $xml = $response->xml(); echo $xml->foo; // >>> Bar! Streaming responses ------------------- Some web services provide streaming APIs that allow a client to keep a HTTP request open for an extended period of time while polling and reading. Guzzle provides a simple way to convert HTTP request messages into ``Guzzle\Stream\Stream`` objects so that you can send the initial headers of a request, read the response headers, and pull in the response body manually as needed. Here's an example using the Twitter Streaming API to track the keyword "bieber": .. code-block:: php use Guzzle\Http\Client; use Guzzle\Stream\PhpStreamRequestFactory; $client = new Client('https://stream.twitter.com/1'); $request = $client->post('statuses/filter.json', null, array( 'track' => 'bieber' )); $request->setAuth('myusername', 'mypassword'); $factory = new PhpStreamRequestFactory(); $stream = $factory->fromRequest($request); // Read until the stream is closed while (!$stream->feof()) { // Read a line from the stream $line = $stream->readLine(); // JSON decode the line of data $data = json_decode($line, true); } You can use the ``stream`` request option when using a static client to more easily create a streaming response. .. code-block:: php $stream = Guzzle::get('http://guzzlephp.org', array('stream' => true)); while (!$stream->feof()) { echo $stream->readLine(); } guzzle/docs/http-client/request.rst 0000604 00000077027 15173213567 0013514 0 ustar 00 ===================== Using Request objects ===================== HTTP request messages --------------------- Request objects are all about building an HTTP message. Each part of an HTTP request message can be set individually using methods on the request object or set in bulk using the ``setUrl()`` method. Here's the format of an HTTP request with each part of the request referencing the method used to change it:: PUT(a) /path(b)?query=123(c) HTTP/1.1(d) X-Header(e): header Content-Length(e): 4 data(f) +-------------------------+---------------------------------------------------------------------------------+ | a. **Method** | The request method can only be set when instantiating a request | +-------------------------+---------------------------------------------------------------------------------+ | b. **Path** | ``$request->setPath('/path');`` | +-------------------------+---------------------------------------------------------------------------------+ | c. **Query** | ``$request->getQuery()->set('query', '123');`` | +-------------------------+---------------------------------------------------------------------------------+ | d. **Protocol version** | ``$request->setProtocolVersion('1.1');`` | +-------------------------+---------------------------------------------------------------------------------+ | e. **Header** | ``$request->setHeader('X-Header', 'header');`` | +-------------------------+---------------------------------------------------------------------------------+ | f. **Entity Body** | ``$request->setBody('data'); // Only available with PUT, POST, PATCH, DELETE`` | +-------------------------+---------------------------------------------------------------------------------+ Creating requests with a client ------------------------------- Client objects are responsible for creating HTTP request objects. GET requests ~~~~~~~~~~~~ `GET requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3>`_ are the most common form of HTTP requests. When you visit a website in your browser, the HTML of the website is downloaded using a GET request. GET requests are idempotent requests that are typically used to download content (an entity) identified by a request URL. .. code-block:: php use Guzzle\Http\Client; $client = new Client(); // Create a request that has a query string and an X-Foo header $request = $client->get('http://www.amazon.com?a=1', array('X-Foo' => 'Bar')); // Send the request and get the response $response = $request->send(); You can change where the body of a response is downloaded on any request using the ``$request->setResponseBody(string|EntityBodyInterface|resource)`` method of a request. You can also set the ``save_to`` option of a request: .. code-block:: php // Send the response body to a file $request = $client->get('http://test.com', array(), array('save_to' => '/path/to/file')); // Send the response body to an fopen resource $request = $client->get('http://test.com', array(), array('save_to' => fopen('/path/to/file', 'w'))); HEAD requests ~~~~~~~~~~~~~ `HEAD requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4>`_ work exactly like GET requests except that they do not actually download the response body (entity) of the response message. HEAD requests are useful for retrieving meta information about an entity identified by a Request-URI. .. code-block:: php $client = new Guzzle\Http\Client(); $request = $client->head('http://www.amazon.com'); $response = $request->send(); echo $response->getContentLength(); // >>> Will output the Content-Length header value DELETE requests ~~~~~~~~~~~~~~~ A `DELETE method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.7>`_ requests that the origin server delete the resource identified by the Request-URI. .. code-block:: php $client = new Guzzle\Http\Client(); $request = $client->delete('http://example.com'); $response = $request->send(); POST requests ~~~~~~~~~~~~~ While `POST requests <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5>`_ can be used for a number of reasons, POST requests are often used when submitting HTML form data to a website. POST requests can include an entity body in the HTTP request. POST requests in Guzzle are sent with an ``application/x-www-form-urlencoded`` Content-Type header if POST fields are present but no files are being sent in the POST. If files are specified in the POST request, then the Content-Type header will become ``multipart/form-data``. The ``post()`` method of a client object accepts four arguments: the URL, optional headers, post fields, and an array of request options. To send files in the POST request, prepend the ``@`` symbol to the array value (just like you would if you were using the PHP ``curl_setopt`` function). Here's how to create a multipart/form-data POST request containing files and fields: .. code-block:: php $request = $client->post('http://httpbin.org/post', array(), array( 'custom_field' => 'my custom value', 'file_field' => '@/path/to/file.xml' )); $response = $request->send(); .. note:: Remember to **always** sanitize user input when sending POST requests: .. code-block:: php // Prevent users from accessing sensitive files by sanitizing input $_POST = array('firstname' => '@/etc/passwd'); $request = $client->post('http://www.example.com', array(), array ( 'firstname' => str_replace('@', '', $_POST['firstname']) )); You can alternatively build up the contents of a POST request. .. code-block:: php $request = $client->post('http://httpbin.org/post') ->setPostField('custom_field', 'my custom value') ->addPostFile('file', '/path/to/file.xml'); $response = $request->send(); Raw POST data ^^^^^^^^^^^^^ POST requests can also contain raw POST data that is not related to HTML forms. .. code-block:: php $request = $client->post('http://httpbin.org/post', array(), 'this is the body'); $response = $request->send(); You can set the body of POST request using the ``setBody()`` method of the ``Guzzle\Http\Message\EntityEnclosingRequest`` object. This method accepts a string, a resource returned from ``fopen``, or a ``Guzzle\Http\EntityBodyInterface`` object. .. code-block:: php $request = $client->post('http://httpbin.org/post'); // Set the body of the POST to stream the contents of /path/to/large_body.txt $request->setBody(fopen('/path/to/large_body.txt', 'r')); $response = $request->send(); PUT requests ~~~~~~~~~~~~ The `PUT method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6>`_ requests that the enclosed entity be stored under the supplied Request-URI. PUT requests are similar to POST requests in that they both can send an entity body in the request message. The body of a PUT request (any any ``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object) is always stored as a ``Guzzle\Http\Message\EntityBodyInterface`` object. This allows a great deal of flexibility when sending data to a remote server. For example, you can stream the contents of a stream returned by fopen, stream the contents of a callback function, or simply send a string of data. .. code-block:: php $request = $client->put('http://httpbin.org/put', array(), 'this is the body'); $response = $request->send(); Just like with POST, PATH, and DELETE requests, you can set the body of a PUT request using the ``setBody()`` method. .. code-block:: php $request = $client->put('http://httpbin.org/put'); $request->setBody(fopen('/path/to/large_body.txt', 'r')); $response = $request->send(); PATCH requests ~~~~~~~~~~~~~~ `PATCH requests <http://tools.ietf.org/html/rfc5789>`_ are used to modify a resource. .. code-block:: php $request = $client->patch('http://httpbin.org', array(), 'this is the body'); $response = $request->send(); OPTIONS requests ~~~~~~~~~~~~~~~~ The `OPTIONS method <http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2>`_ represents a request for information about the communication options available on the request/response chain identified by the Request-URI. .. code-block:: php $request = $client->options('http://httpbin.org'); $response = $request->send(); // Check if the PUT method is supported by this resource var_export($response->isMethodAllows('PUT')); Custom requests ~~~~~~~~~~~~~~~ You can create custom HTTP requests that use non-standard HTTP methods using the ``createRequest()`` method of a client object. .. code-block:: php $request = $client->createRequest('COPY', 'http://example.com/foo', array( 'Destination' => 'http://example.com/bar', 'Overwrite' => 'T' )); $response = $request->send(); Query string parameters ----------------------- Query string parameters of a request are owned by a request's ``Guzzle\Http\Query`` object that is accessible by calling ``$request->getQuery()``. The Query class extends from ``Guzzle\Common\Collection`` and allows you to set one or more query string parameters as key value pairs. You can set a parameter on a Query object using the ``set($key, $value)`` method or access the query string object like an associative array. Any previously specified value for a key will be overwritten when using ``set()``. Use ``add($key, $value)`` to add a value to query string object, and in the event of a collision with an existing value at a specific key, the value will be converted to an array that contains all of the previously set values. .. code-block:: php $request = new Guzzle\Http\Message\Request('GET', 'http://www.example.com?foo=bar&abc=123'); $query = $request->getQuery(); echo "{$query}\n"; // >>> foo=bar&abc=123 $query->remove('abc'); echo "{$query}\n"; // >>> foo=bar $query->set('foo', 'baz'); echo "{$query}\n"; // >>> foo=baz $query->add('foo', 'bar'); echo "{$query}\n"; // >>> foo%5B0%5D=baz&foo%5B1%5D=bar Whoah! What happened there? When ``foo=bar`` was added to the existing ``foo=baz`` query string parameter, the aggregator associated with the Query object was used to help convert multi-value query string parameters into a string. Let's disable URL-encoding to better see what's happening. .. code-block:: php $query->useUrlEncoding(false); echo "{$query}\n"; // >>> foo[0]=baz&foo[1]=bar .. note:: URL encoding can be disabled by passing false, enabled by passing true, set to use RFC 1738 by passing ``Query::FORM_URLENCODED`` (internally uses PHP's ``urlencode`` function), or set to RFC 3986 by passing ``Query::RFC_3986`` (this is the default and internally uses PHP's ``rawurlencode`` function). As you can see, the multiple values were converted into query string parameters following the default PHP convention of adding numerically indexed square bracket suffixes to each key (``foo[0]=baz&foo[1]=bar``). The strategy used to convert multi-value parameters into a string can be customized using the ``setAggregator()`` method of the Query class. Guzzle ships with the following query string aggregators by default: 1. ``Guzzle\Http\QueryAggregator\PhpAggregator``: Aggregates using PHP style brackets (e.g. ``foo[0]=baz&foo[1]=bar``) 2. ``Guzzle\Http\QueryAggregator\DuplicateAggregator``: Performs no aggregation and allows for key value pairs to be repeated in a URL (e.g. ``foo=baz&foo=bar``) 3. ``Guzzle\Http\QueryAggregator\CommaAggregator``: Aggregates using commas (e.g. ``foo=baz,bar``) .. _http-message-headers: HTTP Message Headers -------------------- HTTP message headers are case insensitive, multiple occurrences of any header can be present in an HTTP message (whether it's valid or not), and some servers require specific casing of particular headers. Because of this, request and response headers are stored in ``Guzzle\Http\Message\Header`` objects. The Header object can be cast as a string, counted, or iterated to retrieve each value from the header. Casting a Header object to a string will return all of the header values concatenated together using a glue string (typically ", "). A request (and response) object have several methods that allow you to retrieve and modify headers. * ``getHeaders()``: Get all of the headers of a message as a ``Guzzle\Http\Message\Header\HeaderCollection`` object. * ``getHeader($header)``: Get a specific header from a message. If the header exists, you'll get a ``Guzzle\Http\Message\Header`` object. If the header does not exist, this methods returns ``null``. * ``hasHeader($header)``: Returns true or false based on if the message has a particular header. * ``setHeader($header, $value)``: Set a header value and overwrite any previously set value for this header. * ``addHeader($header, $value)``: Add a header with a particular name. If a previous value was already set by the same, then the header will contain multiple values. * ``removeHeader($header)``: Remove a header by name from the message. .. code-block:: php $request = new Request('GET', 'http://httpbin.com/cookies'); // addHeader will set and append to any existing header values $request->addHeader('Foo', 'bar'); $request->addHeader('foo', 'baz'); // setHeader overwrites any existing values $request->setHeader('Test', '123'); // Request headers can be cast as a string echo $request->getHeader('Foo'); // >>> bar, baz echo $request->getHeader('Test'); // >>> 123 // You can count the number of headers of a particular case insensitive name echo count($request->getHeader('foO')); // >>> 2 // You can iterate over Header objects foreach ($request->getHeader('foo') as $header) { echo $header . "\n"; } // You can get all of the request headers as a Guzzle\Http\Message\Header\HeaderCollection object $headers = $request->getHeaders(); // Missing headers return NULL var_export($request->getHeader('Missing')); // >>> null // You can see all of the different variations of a header by calling raw() on the Header var_export($request->getHeader('foo')->raw()); Setting the body of a request ----------------------------- Requests that can send a body (e.g. PUT, POST, DELETE, PATCH) are instances of ``Guzzle\Http\Message\EntityEnclosingRequestInterface``. Entity enclosing requests contain several methods that allow you to specify the body to send with a request. Use the ``setBody()`` method of a request to set the body that will be sent with a request. This method accepts a string, a resource returned by ``fopen()``, an array, or an instance of ``Guzzle\Http\EntityBodyInterface``. The body will then be streamed from the underlying ``EntityBodyInterface`` object owned by the request. When setting the body of the request, you can optionally specify a Content-Type header and whether or not to force the request to use chunked Transfer-Encoding. .. code-block:: php $request = $client->put('/user.json'); $request->setBody('{"foo":"baz"}', 'application/json'); Content-Type header ~~~~~~~~~~~~~~~~~~~ Guzzle will automatically add a Content-Type header to a request if the Content-Type can be guessed based on the file extension of the payload being sent or the file extension present in the path of a request. .. code-block:: php $request = $client->put('/user.json', array(), '{"foo":"bar"}'); // The Content-Type was guessed based on the path of the request echo $request->getHeader('Content-Type'); // >>> application/json $request = $client->put('/user.json'); $request->setBody(fopen('/tmp/user_data.json', 'r')); // The Content-Type was guessed based on the path of the entity body echo $request->getHeader('Content-Type'); // >>> application/json Transfer-Encoding: chunked header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When sending HTTP requests that contain a payload, you must let the remote server know how to determine when the entire message has been sent. This usually is done by supplying a ``Content-Length`` header that tells the origin server the size of the body that is to be sent. In some cases, the size of the payload being sent in a request cannot be known before initiating the transfer. In these cases (when using HTTP/1.1), you can use the ``Transfer-Encoding: chunked`` header. If the Content-Length cannot be determined (i.e. using a PHP ``http://`` stream), then Guzzle will automatically add the ``Transfer-Encoding: chunked`` header to the request. .. code-block:: php $request = $client->put('/user.json'); $request->setBody(fopen('http://httpbin.org/get', 'r')); // The Content-Length could not be determined echo $request->getHeader('Transfer-Encoding'); // >>> chunked See :doc:`/http-client/entity-bodies` for more information on entity bodies. Expect: 100-Continue header ~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``Expect: 100-Continue`` header is used to help a client prevent sending a large payload to a server that will reject the request. This allows clients to fail fast rather than waste bandwidth sending an erroneous payload. Guzzle will automatically add the ``Expect: 100-Continue`` header to a request when the size of the payload exceeds 1MB or if the body of the request is not seekable (this helps to prevent errors when a non-seekable body request is redirected). .. note:: If you find that your larger requests are taking too long to complete, you should first check if the ``Expect: 100-Continue`` header is being sent with the request. Some servers do not respond well to this header, which causes cURL to sleep for `1 second <http://curl.haxx.se/mail/lib-2010-01/0182.html>`_. POST fields and files ~~~~~~~~~~~~~~~~~~~~~ Any entity enclosing request can send POST style fields and files. This includes POST, PUT, PATCH, and DELETE requests. Any request that has set POST fields or files will use cURL's POST message functionality. .. code-block:: php $request = $client->post('/post'); // Set an overwrite any previously specified value $request->setPostField('foo', 'bar'); // Append a value to any existing values $request->getPostFields()->add('foo', 'baz'); // Remove a POST field by name $request->removePostField('fizz'); // Add a file to upload (forces multipart/form-data) $request->addPostFile('my_file', '/path/to/file', 'plain/text'); // Remove a POST file by POST key name $request->removePostFile('my_other_file'); .. tip:: Adding a large number of POST fields to a POST request is faster if you use the ``addPostFields()`` method so that you can add and process multiple fields with a single call. Adding multiple POST files is also faster using ``addPostFiles()``. Working with cookies -------------------- Cookies can be modified and retrieved from a request using the following methods: .. code-block:: php $request->addCookie($name, $value); $request->removeCookie($name); $value = $request->getCookie($name); $valueArray = $request->getCookies(); Use the :doc:`cookie plugin </plugins/cookie-plugin>` if you need to reuse cookies between requests. .. _request-set-response-body: Changing where a response is downloaded ---------------------------------------- When a request is sent, the body of the response will be stored in a PHP temp stream by default. You can change the location in which the response will be downloaded using ``$request->setResponseBody($body)`` or the ``save_to`` request option. This can be useful for downloading the contents of a URL to a specific file. Here's an example of using request options: .. code-block:: php $request = $this->client->get('http://example.com/large.mov', array(), array( 'save_to' => '/tmp/large_file.mov' )); $request->send(); var_export(file_exists('/tmp/large_file.mov')); // >>> true Here's an example of using ``setResponseBody()``: .. code-block:: php $body = fopen('/tmp/large_file.mov', 'w'); $request = $this->client->get('http://example.com/large.mov'); $request->setResponseBody($body); // You can more easily specify the name of a file to save the contents // of the response to by passing a string to ``setResponseBody()``. $request = $this->client->get('http://example.com/large.mov'); $request->setResponseBody('/tmp/large_file.mov'); Custom cURL options ------------------- Most of the functionality implemented in the libcurl bindings has been simplified and abstracted by Guzzle. Developers who need access to `cURL specific functionality <http://www.php.net/curl_setopt>`_ can still add cURL handle specific behavior to Guzzle HTTP requests by modifying the cURL options collection of a request: .. code-block:: php $request->getCurlOptions()->set(CURLOPT_LOW_SPEED_LIMIT, 200); Other special options that can be set in the ``curl.options`` array include: +-------------------------+---------------------------------------------------------------------------------+ | debug | Adds verbose cURL output to a temp stream owned by the cURL handle object | +-------------------------+---------------------------------------------------------------------------------+ | progress | Instructs cURL to emit events when IO events occur. This allows you to be | | | notified when bytes are transferred over the wire by subscribing to a request's | | | ``curl.callback.read``, ``curl.callback.write``, and ``curl.callback.progress`` | | | events. | +-------------------------+---------------------------------------------------------------------------------+ Request options --------------- Requests options can be specified when creating a request or in the ``request.options`` parameter of a client. These options can control various aspects of a request including: headers to send, query string data, where the response should be downloaded, proxies, auth, etc. .. code-block:: php $request = $client->get($url, $headers, array('proxy' => 'http://proxy.com')); See :ref:`Request options <request-options>` for more information. Working with errors ------------------- HTTP errors ~~~~~~~~~~~ Requests that receive a 4xx or 5xx response will throw a ``Guzzle\Http\Exception\BadResponseException``. More specifically, 4xx errors throw a ``Guzzle\Http\Exception\ClientErrorResponseException``, and 5xx errors throw a ``Guzzle\Http\Exception\ServerErrorResponseException``. You can catch the specific exceptions or just catch the BadResponseException to deal with either type of error. Here's an example of catching a generic BadResponseException: .. code-block:: php try { $response = $client->get('/not_found.xml')->send(); } catch (Guzzle\Http\Exception\BadResponseException $e) { echo 'Uh oh! ' . $e->getMessage(); echo 'HTTP request URL: ' . $e->getRequest()->getUrl() . "\n"; echo 'HTTP request: ' . $e->getRequest() . "\n"; echo 'HTTP response status: ' . $e->getResponse()->getStatusCode() . "\n"; echo 'HTTP response: ' . $e->getResponse() . "\n"; } Throwing an exception when a 4xx or 5xx response is encountered is the default behavior of Guzzle requests. This behavior can be overridden by adding an event listener with a higher priority than -255 that stops event propagation. You can subscribe to ``request.error`` to receive notifications any time an unsuccessful response is received. You can change the response that will be associated with the request by calling ``setResponse()`` on the ``$event['request']`` object passed into your listener, or by changing the ``$event['response']`` value of the ``Guzzle\Common\Event`` object that is passed to your listener. Transparently changing the response associated with a request by modifying the event allows you to retry failed requests without complicating the code that uses the client. This might be useful for sending requests to a web service that has expiring auth tokens. When a response shows that your token has expired, you can get a new token, retry the request with the new token, and return the successful response to the user. Here's an example of retrying a request using updated authorization credentials when a 401 response is received, overriding the response of the original request with the new response, and still allowing the default exception behavior to be called when other non-200 response status codes are encountered: .. code-block:: php // Add custom error handling to any request created by this client $client->getEventDispatcher()->addListener('request.error', function(Event $event) { if ($event['response']->getStatusCode() == 401) { $newRequest = $event['request']->clone(); $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken()); $newResponse = $newRequest->send(); // Set the response object of the request without firing more events $event['response'] = $newResponse; // You can also change the response and fire the normal chain of // events by calling $event['request']->setResponse($newResponse); // Stop other events from firing when you override 401 responses $event->stopPropagation(); } }); cURL errors ~~~~~~~~~~~ Connection problems and cURL specific errors can also occur when transferring requests using Guzzle. When Guzzle encounters cURL specific errors while transferring a single request, a ``Guzzle\Http\Exception\CurlException`` is thrown with an informative error message and access to the cURL error message. A ``Guzzle\Http\Exception\MultiTransferException`` exception is thrown when a cURL specific error occurs while transferring multiple requests in parallel. You can then iterate over all of the exceptions encountered during the transfer. Plugins and events ------------------ Guzzle request objects expose various events that allow you to hook in custom logic. A request object owns a ``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling ``$request->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or event subscribers (classes that listen to specific events of a dispatcher). You can add event subscribers to a request directly by just calling ``$request->addSubscriber($mySubscriber);``. .. _request-events: Events emitted from a request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A ``Guzzle\Http\Message\Request`` and ``Guzzle\Http\Message\EntityEnclosingRequest`` object emit the following events: +------------------------------+--------------------------------------------+------------------------------------------+ | Event name | Description | Event data | +==============================+============================================+==========================================+ | request.before_send | About to send request | * request: Request to be sent | +------------------------------+--------------------------------------------+------------------------------------------+ | request.sent | Sent the request | * request: Request that was sent | | | | * response: Received response | +------------------------------+--------------------------------------------+------------------------------------------+ | request.complete | Completed a full HTTP transaction | * request: Request that was sent | | | | * response: Received response | +------------------------------+--------------------------------------------+------------------------------------------+ | request.success | Completed a successful request | * request: Request that was sent | | | | * response: Received response | +------------------------------+--------------------------------------------+------------------------------------------+ | request.error | Completed an unsuccessful request | * request: Request that was sent | | | | * response: Received response | +------------------------------+--------------------------------------------+------------------------------------------+ | request.exception | An unsuccessful response was | * request: Request | | | received. | * response: Received response | | | | * exception: BadResponseException | +------------------------------+--------------------------------------------+------------------------------------------+ | request.receive.status_line | Received the start of a response | * line: Full response start line | | | | * status_code: Status code | | | | * reason_phrase: Reason phrase | | | | * previous_response: (e.g. redirect) | +------------------------------+--------------------------------------------+------------------------------------------+ | curl.callback.progress | cURL progress event (only dispatched when | * handle: CurlHandle | | | ``emit_io`` is set on a request's curl | * download_size: Total download size | | | options) | * downloaded: Bytes downloaded | | | | * upload_size: Total upload bytes | | | | * uploaded: Bytes uploaded | +------------------------------+--------------------------------------------+------------------------------------------+ | curl.callback.write | cURL event called when data is written to | * request: Request | | | an outgoing stream | * write: Data being written | +------------------------------+--------------------------------------------+------------------------------------------+ | curl.callback.read | cURL event called when data is written to | * request: Request | | | an incoming stream | * read: Data being read | +------------------------------+--------------------------------------------+------------------------------------------+ Creating a request event listener ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here's an example that listens to the ``request.complete`` event of a request and prints the request and response. .. code-block:: php use Guzzle\Common\Event; $request = $client->get('http://www.google.com'); // Echo out the response that was received $request->getEventDispatcher()->addListener('request.complete', function (Event $e) { echo $e['request'] . "\n\n"; echo $e['response']; }); guzzle/docs/http-client/entity-bodies.rst 0000604 00000013254 15173213567 0014573 0 ustar 00 =========================== Request and response bodies =========================== `Entity body <http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html>`_ is the term used for the body of an HTTP message. The entity body of requests and responses is inherently a `PHP stream <http://php.net/manual/en/book.stream.php>`_ in Guzzle. The body of the request can be either a string or a PHP stream which are converted into a ``Guzzle\Http\EntityBody`` object using its factory method. When using a string, the entity body is stored in a `temp PHP stream <http://www.php.net/manual/en/wrappers.php.php>`_. The use of temp PHP streams helps to protect your application from running out of memory when sending or receiving large entity bodies in your messages. When more than 2MB of data is stored in a temp stream, it automatically stores the data on disk rather than in memory. EntityBody objects provide a great deal of functionality: compression, decompression, calculate the Content-MD5, calculate the Content-Length (when the resource is repeatable), guessing the Content-Type, and more. Guzzle doesn't need to load an entire entity body into a string when sending or retrieving data; entity bodies are streamed when being uploaded and downloaded. Here's an example of gzip compressing a text file then sending the file to a URL: .. code-block:: php use Guzzle\Http\EntityBody; $body = EntityBody::factory(fopen('/path/to/file.txt', 'r+')); echo $body->read(1024); $body->seek(0, SEEK_END); $body->write('foo'); echo $body->ftell(); $body->rewind(); // Send a request using the body $response = $client->put('http://localhost:8080/uploads', null, $body)->send(); The body of the request can be specified in the ``Client::put()`` or ``Client::post()`` method, or, you can specify the body of the request by calling the ``setBody()`` method of any ``Guzzle\Http\Message\EntityEnclosingRequestInterface`` object. Compression ----------- You can compress the contents of an EntityBody object using the ``compress()`` method. The compress method accepts a filter that must match to one of the supported `PHP stream filters <http://www.php.net/manual/en/filters.compression.php>`_ on your system (e.g. `zlib.deflate`, ``bzip2.compress``, etc). Compressing an entity body will stream the entire entity body through a stream compression filter into a temporary PHP stream. You can uncompress an entity body using the ``uncompress()`` method and passing the PHP stream filter to use when decompressing the stream (e.g. ``zlib.inflate``). .. code-block:: php use Guzzle\Http\EntityBody; $body = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); echo $body->getSize(); // >>> 1048576 // Compress using the default zlib.deflate filter $body->compress(); echo $body->getSize(); // >>> 314572 // Decompress the stream $body->uncompress(); echo $body->getSize(); // >>> 1048576 Decorators ---------- Guzzle provides several EntityBody decorators that can be used to add functionality to an EntityBody at runtime. IoEmittingEntityBody ~~~~~~~~~~~~~~~~~~~~ This decorator will emit events when data is read from a stream or written to a stream. Add an event subscriber to the entity body's ``body.read`` or ``body.write`` methods to receive notifications when data data is transferred. .. code-block:: php use Guzzle\Common\Event; use Guzzle\Http\EntityBody; use Guzzle\Http\IoEmittingEntityBody; $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); $body = new IoEmittingEntityBody($original); // Listen for read events $body->getEventDispatcher()->addListener('body.read', function (Event $e) { // Grab data from the event $entityBody = $e['body']; // Amount of data retrieved from the body $lengthOfData = $e['length']; // The actual data that was read $data = $e['read']; }); // Listen for write events $body->getEventDispatcher()->addListener('body.write', function (Event $e) { // Grab data from the event $entityBody = $e['body']; // The data that was written $data = $e['write']; // The actual amount of data that was written $data = $e['read']; }); ReadLimitEntityBody ~~~~~~~~~~~~~~~~~~~ The ReadLimitEntityBody decorator can be used to transfer a subset or slice of an existing EntityBody object. This can be useful for breaking a large file into smaller pieces to be sent in chunks (e.g. Amazon S3's multipart upload API). .. code-block:: php use Guzzle\Http\EntityBody; use Guzzle\Http\ReadLimitEntityBody; $original = EntityBody::factory(fopen('/tmp/test.txt', 'r+')); echo $original->getSize(); // >>> 1048576 // Limit the size of the body to 1024 bytes and start reading from byte 2048 $body = new ReadLimitEntityBody($original, 1024, 2048); echo $body->getSize(); // >>> 1024 echo $body->ftell(); // >>> 0 CachingEntityBody ~~~~~~~~~~~~~~~~~ The CachingEntityBody decorator is used to allow seeking over previously read bytes on non-seekable read streams. This can be useful when transferring a non-seekable entity body fails due to needing to rewind the stream (for example, resulting from a redirect). Data that is read from the remote stream will be buffered in a PHP temp stream so that previously read bytes are cached first in memory, then on disk. .. code-block:: php use Guzzle\Http\EntityBody; use Guzzle\Http\CachingEntityBody; $original = EntityBody::factory(fopen('http://www.google.com', 'r')); $body = new CachingEntityBody($original); $body->read(1024); echo $body->ftell(); // >>> 1024 $body->seek(0); echo $body->ftell(); // >>> 0 guzzle/docs/http-client/http-redirects.rst 0000604 00000006526 15173213567 0014761 0 ustar 00 ============== HTTP redirects ============== By default, Guzzle will automatically follow redirects using the non-RFC compliant implementation used by most web browsers. This means that redirects for POST requests are followed by a GET request. You can force RFC compliance by enabling the strict mode on a request's parameter object: .. code-block:: php // Set per request $request = $client->post(); $request->getParams()->set('redirect.strict', true); // You can set globally on a client so all requests use strict redirects $client->getConfig()->set('request.params', array( 'redirect.strict' => true )); By default, Guzzle will redirect up to 5 times before throwing a ``Guzzle\Http\Exception\TooManyRedirectsException``. You can raise or lower this value using the ``redirect.max`` parameter of a request object: .. code-block:: php $request->getParams()->set('redirect.max', 2); Redirect history ---------------- You can get the number of redirects of a request using the resulting response object's ``getRedirectCount()`` method. Similar to cURL's ``effective_url`` property, Guzzle provides the effective URL, or the last redirect URL that returned the request, in a response's ``getEffectiveUrl()`` method. When testing or debugging, it is often useful to see a history of redirects for a particular request. This can be achieved using the HistoryPlugin. .. code-block:: php $request = $client->get('/'); $history = new Guzzle\Plugin\History\HistoryPlugin(); $request->addSubscriber($history); $response = $request->send(); // Get the last redirect URL or the URL of the request that received // this response echo $response->getEffectiveUrl(); // Get the number of redirects echo $response->getRedirectCount(); // Iterate over each sent request and response foreach ($history->getAll() as $transaction) { // Request object echo $transaction['request']->getUrl() . "\n"; // Response object echo $transaction['response']->getEffectiveUrl() . "\n"; } // Or, simply cast the HistoryPlugin to a string to view each request and response echo $history; Disabling redirects ------------------- You can disable redirects on a client by passing a configuration option in the client's constructor: .. code-block:: php $client = new Client(null, array('redirect.disable' => true)); You can also disable redirects per request: .. code-block:: php $request = $client->get($url, array(), array('allow_redirects' => false)); Redirects and non-repeatable streams ------------------------------------ If you are redirected when sending data from a non-repeatable stream and some of the data has been read off of the stream, then you will get a ``Guzzle\Http\Exception\CouldNotRewindStreamException``. You can get around this error by adding a custom rewind method to the entity body object being sent in the request. .. code-block:: php $request = $client->post( 'http://httpbin.com/redirect/2', null, fopen('http://httpbin.com/get', 'r') ); // Add a custom function that can be used to rewind the stream // (reopen in this example) $request->getBody()->setRewindFunction(function ($body) { $body->setStream(fopen('http://httpbin.com/get', 'r')); return true; ); $response = $client->send(); guzzle/docs/getting-started/overview.rst 0000604 00000007066 15173213567 0014540 0 ustar 00 ================= Welcome to Guzzle ================= What is Guzzle? ~~~~~~~~~~~~~~~ Guzzle is a PHP HTTP client and framework for building web service clients. Guzzle takes the pain out of sending HTTP requests and the redundancy out of creating web service clients. Features at a glance -------------------- - All the power of cURL with a simple interface. - Persistent connections and parallel requests. - Streams request and response bodies - Service descriptions for quickly building clients. - Powered by the Symfony2 EventDispatcher. - Use all of the code or only specific components. - Plugins for caching, logging, OAuth, mocks, and more - Includes a custom node.js webserver to test your clients. - Service descriptions for defining the inputs and outputs of an API - Resource iterators for traversing paginated resources - Batching for sending a large number of requests as efficiently as possible .. code-block:: php // Really simple using a static facade Guzzle\Http\StaticClient::mount(); $response = Guzzle::get('http://guzzlephp.org'); // More control using a client class $client = new \Guzzle\Http\Client('http://guzzlephp.org'); $request = $client->get('/'); $response = $request->send(); License ------- Licensed using the `MIT license <http://opensource.org/licenses/MIT>`_. Copyright (c) 2013 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. Contributing ------------ Guidelines ~~~~~~~~~~ This is still a work in progress, but there are only a few rules: 1. Guzzle follows PSR-0, PSR-1, and PSR-2 2. All pull requests must include unit tests to ensure the change works as expected and to prevent future regressions Reporting a security vulnerability ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We want to ensure that Guzzle is a secure HTTP client library for everyone. If you've discovered a security vulnerability in Guzzle, we appreciate your help in disclosing it to us in a `responsible manner <http://en.wikipedia.org/wiki/Responsible_disclosure>`_. Publicly disclosing a vulnerability can put the entire community at risk. If you've discovered a security concern, please email us at security@guzzlephp.org. We'll work with you to make sure that we understand the scope of the issue, and that we fully address your concern. We consider correspondence sent to security@guzzlephp.org our highest priority, and work to address any issues that arise as quickly as possible. After a security vulnerability has been corrected, a security hotfix release will be deployed as soon as possible. guzzle/docs/getting-started/installation.rst 0000604 00000022041 15173213567 0015361 0 ustar 00 ============ Installation ============ Requirements ------------ #. PHP 5.3.3+ compiled with the cURL extension #. A recent version of cURL 7.16.2+ compiled with OpenSSL and zlib Installing Guzzle ----------------- Composer ~~~~~~~~ The recommended way to install Guzzle is with `Composer <http://getcomposer.org>`_. Composer is a dependency management tool for PHP that allows you to declare the dependencies your project needs and installs them into your project. .. code-block:: bash # Install Composer curl -sS https://getcomposer.org/installer | php # Add Guzzle as a dependency php composer.phar require guzzle/guzzle:~3.9 After installing, you need to require Composer's autoloader: .. code-block:: php 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>`_. Using only specific parts of Guzzle ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ While you can always just rely on ``guzzle/guzzle``, Guzzle provides several smaller parts of Guzzle as individual packages available through Composer. +-----------------------------------------------------------------------------------------------+------------------------------------------+ | Package name | Description | +===============================================================================================+==========================================+ | `guzzle/common <https://packagist.org/packages/guzzle/common>`_ | Provides ``Guzzle\Common`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/http <https://packagist.org/packages/guzzle/http>`_ | Provides ``Guzzle\Http`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/parser <https://packagist.org/packages/guzzle/parser>`_ | Provides ``Guzzle\Parser`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/batch <https://packagist.org/packages/guzzle/batch>`_ | Provides ``Guzzle\Batch`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/cache <https://packagist.org/packages/guzzle/cache>`_ | Provides ``Guzzle\Cache`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/inflection <https://packagist.org/packages/guzzle/inflection>`_ | Provides ``Guzzle\Inflection`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/iterator <https://packagist.org/packages/guzzle/iterator>`_ | Provides ``Guzzle\Iterator`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/log <https://packagist.org/packages/guzzle/log>`_ | Provides ``Guzzle\Log`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin <https://packagist.org/packages/guzzle/plugin>`_ | Provides ``Guzzle\Plugin`` (all plugins) | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-async <https://packagist.org/packages/guzzle/plugin-async>`_ | Provides ``Guzzle\Plugin\Async`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-backoff <https://packagist.org/packages/guzzle/plugin-backoff>`_ | Provides ``Guzzle\Plugin\BackoffPlugin`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-cache <https://packagist.org/packages/guzzle/plugin-cache>`_ | Provides ``Guzzle\Plugin\Cache`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-cookie <https://packagist.org/packages/guzzle/plugin-cookie>`_ | Provides ``Guzzle\Plugin\Cookie`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-error-response <https://packagist.org/packages/guzzle/plugin-error-response>`_ | Provides ``Guzzle\Plugin\ErrorResponse`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-history <https://packagist.org/packages/guzzle/plugin-history>`_ | Provides ``Guzzle\Plugin\History`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-log <https://packagist.org/packages/guzzle/plugin-log>`_ | Provides ``Guzzle\Plugin\Log`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-md5 <https://packagist.org/packages/guzzle/plugin-md5>`_ | Provides ``Guzzle\Plugin\Md5`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-mock <https://packagist.org/packages/guzzle/plugin-mock>`_ | Provides ``Guzzle\Plugin\Mock`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/plugin-oauth <https://packagist.org/packages/guzzle/plugin-oauth>`_ | Provides ``Guzzle\Plugin\Oauth`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/service <https://packagist.org/packages/guzzle/service>`_ | Provides ``Guzzle\Service`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ | `guzzle/stream <https://packagist.org/packages/guzzle/stream>`_ | Provides ``Guzzle\Stream`` | +-----------------------------------------------------------------------------------------------+------------------------------------------+ Bleeding edge ^^^^^^^^^^^^^ During your development, you can keep up with the latest changes on the master branch by setting the version requirement for Guzzle to ``dev-master``. .. code-block:: js { "require": { "guzzle/guzzle": "dev-master" } } PEAR ~~~~ Guzzle can be installed through PEAR: .. code-block:: bash pear channel-discover guzzlephp.org/pear pear install guzzle/guzzle You can install a specific version of Guzzle by providing a version number suffix: .. code-block:: bash pear install guzzle/guzzle-3.9.0 Contributing to Guzzle ---------------------- In order to contribute, you'll need to checkout the source from GitHub and install Guzzle's dependencies using Composer: .. code-block:: bash git clone https://github.com/guzzle/guzzle.git cd guzzle && curl -s http://getcomposer.org/installer | php && ./composer.phar install --dev Guzzle is unit tested with PHPUnit. You will need to create your own phpunit.xml file in order to run the unit tests (or just copy phpunit.xml.dist to phpunit.xml). Run the tests using the vendored PHPUnit binary: .. code-block:: bash vendor/bin/phpunit You'll need to install node.js v0.5.0 or newer in order to test the cURL implementation. Framework integrations ---------------------- Using Guzzle with Symfony ~~~~~~~~~~~~~~~~~~~~~~~~~ Bundles are available on GitHub: - `DdeboerGuzzleBundle <https://github.com/ddeboer/GuzzleBundle>`_ for Guzzle 2 - `MisdGuzzleBundle <https://github.com/misd-service-development/guzzle-bundle>`_ for Guzzle 3 Using Guzzle with Silex ~~~~~~~~~~~~~~~~~~~~~~~ A `Guzzle Silex service provider <https://github.com/guzzle/guzzle-silex-extension>`_ is available on GitHub. guzzle/docs/getting-started/faq.rst 0000604 00000002512 15173213567 0013430 0 ustar 00 === FAQ === What should I do if I get this error: Fatal error: Maximum function nesting level of '100' reached, aborting! ------------------------------------------------------------------------------------------------------------- You could run into this error if you have the XDebug extension installed and you execute a lot of requests in callbacks. This error message comes specifically from the XDebug extension. PHP itself does not have a function nesting limit. Change this setting in your php.ini to increase the limit:: xdebug.max_nesting_level = 1000 [`source <http://stackoverflow.com/a/4293870/151504>`_] How can I speed up my client? ----------------------------- There are several things you can do to speed up your client: 1. Utilize a C based HTTP message parser (e.g. ``Guzzle\Parser\Message\PeclHttpMessageParser``) 2. Disable operation validation by setting the ``command.disable_validation`` option to true on a command Why am I getting a 417 error response? -------------------------------------- This can occur for a number of reasons, but if you are sending PUT, POST, or PATCH requests with an ``Expect: 100-Continue`` header, a server that does not support this header will return a 417 response. You can work around this by calling ``$request->removeHeader('Expect');`` after setting the entity body of a request. guzzle/docs/docs.rst 0000604 00000002452 15173213567 0010507 0 ustar 00 .. title:: Guzzle | PHP HTTP client and framework for consuming RESTful web services ==================== Guzzle Documentation ==================== Getting started --------------- .. toctree:: :maxdepth: 1 getting-started/overview getting-started/installation getting-started/faq The HTTP client --------------- .. toctree:: :maxdepth: 2 http-client/client http-client/request http-client/response http-client/entity-bodies http-client/http-redirects http-client/uri-templates Plugins ------- .. toctree:: :maxdepth: 1 plugins/plugins-overview plugins/creating-plugins plugins/async-plugin plugins/backoff-plugin plugins/cache-plugin plugins/cookie-plugin plugins/curl-auth-plugin plugins/history-plugin plugins/log-plugin plugins/md5-validator-plugin plugins/mock-plugin plugins/oauth-plugin The web service client ---------------------- .. toctree:: :maxdepth: 1 webservice-client/webservice-client webservice-client/using-the-service-builder webservice-client/guzzle-service-descriptions batching/batching iterators/resource-iterators iterators/guzzle-iterators Testing ------- .. toctree:: :maxdepth: 2 testing/unit-testing API Docs -------- `Read the API docs <http://guzzlephp.org/api/index.html>`_ guzzle/docs/iterators/guzzle-iterators.rst 0000604 00000004556 15173213567 0015134 0 ustar 00 ================ Guzzle iterators ================ Guzzle provides several SPL iterators that can be used with other SPL iterators, including Guzzle resource iterators. Guzzle's ``guzzle/iterator`` component can also be used independently of the rest of Guzzle through Packagist and Composer: https://packagist.org/packages/guzzle/iterator ChunkedIterator --------------- Pulls out multiple values from an inner iterator and yields and array of values for each outer iteration -- essentially pulling out chunks of values from the inner iterator. .. code-block:: php use Guzzle\Iterator\ChunkedIterator; $inner = new ArrayIterator(range(0, 8)); $chunkedIterator = new ChunkedIterator($inner, 2); foreach ($chunkedIterator as $chunk) { echo implode(', ', $chunk) . "\n"; } // >>> 0, 1 // >>> 2, 3 // >>> 4, 5 // >>> 6, 7 // >>> 8 FilterIterator -------------- This iterator is used to filter values out of the inner iterator. This iterator can be used when PHP 5.4's CallbackFilterIterator is not available. .. code-block:: php use Guzzle\Iterator\FilterIterator; $inner = new ArrayIterator(range(1, 10)); $filterIterator = new FilterIterator($inner, function ($value) { return $value % 2; }); foreach ($filterIterator as $value) { echo $value . "\n"; } // >>> 2 // >>> 4 // >>> 6 // >>> 8 // >>> 10 MapIterator ----------- This iterator modifies the values of the inner iterator before yielding. .. code-block:: php use Guzzle\Iterator\MapIterator; $inner = new ArrayIterator(range(0, 3)); $mapIterator = new MapIterator($inner, function ($value) { return $value * 10; }); foreach ($mapIterator as $value) { echo $value . "\n"; } // >>> 0 // >>> 10 // >>> 20 // >>> 30 MethodProxyIterator ------------------- This decorator is useful when you need to expose a specific method from an inner iterator that might be wrapper by one or more iterator decorators. This decorator proxies missing method calls to each inner iterator until one of the inner iterators can fulfill the call. .. code-block:: php use Guzzle\Iterator\MethodProxyIterator; $inner = new \ArrayIterator(); $proxy = new MethodProxyIterator($inner); // Proxy method calls to the ArrayIterator $proxy->append('a'); $proxy->append('b'); guzzle/docs/iterators/resource-iterators.rst 0000604 00000017167 15173213567 0015445 0 ustar 00 ================== Resource iterators ================== Web services often implement pagination in their responses which requires the end-user to issue a series of consecutive requests in order to fetch all of the data they asked for. Users of your web service client should not be responsible for implementing the logic involved in iterating through pages of results. Guzzle provides a simple resource iterator foundation to make it easier on web service client developers to offer a useful abstraction layer. Getting an iterator from a client --------------------------------- ResourceIteratorInterface Guzzle\Service\Client::getIterator($command [, array $commandOptions, array $iteratorOptions ]) The ``getIterator`` method of a ``Guzzle\Service\ClientInterface`` object provides a convenient interface for instantiating a resource iterator for a specific command. This method implicitly uses a ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object to create resource iterators. Pass an instantiated command object or the name of a command in the first argument. When passing the name of a command, the command factory of the client will create the command by name using the ``$commandOptions`` array. The third argument may be used to pass an array of options to the constructor of the instantiated ``ResourceIteratorInterface`` object. .. code-block:: php $iterator = $client->getIterator('get_users'); foreach ($iterator as $user) { echo $user['name'] . ' age ' . $user['age'] . PHP_EOL; } The above code sample might execute a single request or a thousand requests. As a consumer of a web service, I don't care. I just want to iterate over all of the users. Iterator options ~~~~~~~~~~~~~~~~ The two universal options that iterators should support are ``limit`` and ``page_size``. Using the ``limit`` option tells the resource iterator to attempt to limit the total number of iterated resources to a specific amount. Keep in mind that this is not always possible due to limitations that may be inherent to a web service. The ``page_size`` option is used to tell a resource iterator how many resources to request per page of results. Much like the ``limit`` option, you can not rely on getting back exactly the number of resources your specify in the ``page_size`` option. .. note:: The ``limit`` and ``page_size`` options can also be specified on an iterator using the ``setLimit($limit)`` and ``setPageSize($pageSize)`` methods. Resolving iterator class names ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The default resource iterator factory of a client object expects that your iterators are stored under the ``Model`` folder of your client and that an iterator is names after the CamelCase name of a command followed by the word "Iterator". For example, if you wanted to create an iterator for the ``get_users`` command, then your iterator class would be ``Model\GetUsersIterator`` and would be stored in ``Model/GetUsersIterator.php``. Creating an iterator -------------------- While not required, resource iterators in Guzzle typically iterate using a ``Guzzle\Service\Command\CommandInterface`` object. ``Guzzle\Service\Resource\ResourceIterator``, the default iterator implementation that you should extend, accepts a command object and array of iterator options in its constructor. The command object passed to the resource iterator is expected to be ready to execute and not previously executed. The resource iterator keeps a reference of this command and clones the original command each time a subsequent request needs to be made to fetch more data. Implement the sendRequest method ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The most important thing (and usually the only thing) you need to do when creating a resource iterator is to implement the ``sendRequest()`` method of the resource iterator. The ``sendRequest()`` method is called when you begin iterating or if there are no resources left to iterate and it you expect to retrieve more resources by making a subsequent request. The ``$this->command`` property of the resource iterator is updated with a cloned copy of the original command object passed into the constructor of the iterator. Use this command object to issue your subsequent requests. The ``sendRequest()`` method must return an array of the resources you retrieved from making the subsequent call. Returning an empty array will stop the iteration. If you suspect that your web service client will occasionally return an empty result set but still requires further iteration, then you must implement a sort of loop in your ``sendRequest()`` method that will continue to issue subsequent requests until your reach the end of the paginated result set or until additional resources are retrieved from the web service. Update the nextToken property ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Beyond fetching more results, the ``sendRequest()`` method is responsible for updating the ``$this->nextToken`` property of the iterator. Setting this property to anything other than null tells the iterator that issuing a subsequent request using the nextToken value will probably return more results. You must continually update this value in your ``sendRequest()`` method as each response is received from the web service. Example iterator ---------------- Let's say you want to implement a resource iterator for the ``get_users`` command of your web service. The ``get_users`` command receives a response that contains a list of users, and if there are more pages of results to retrieve, returns a value called ``next_user``. This return value is known as the **next token** and should be used to issue subsequent requests. Assume the response to a ``get_users`` command returns JSON data that looks like this: .. code-block:: javascript { "users": [ { "name": "Craig Johnson", "age": 10 }, { "name": "Tom Barker", "age": 20 }, { "name": "Bob Mitchell", "age": 74 } ], "next_user": "Michael Dowling" } Assume that because there is a ``next_user`` value, there will be more users if a subsequent request is issued. If the ``next_user`` value is missing or null, then we know there are no more results to fetch. Let's implement a resource iterator for this command. .. code-block:: php namespace MyService\Model; use Guzzle\Service\Resource\ResourceIterator; /** * Iterate over a get_users command */ class GetUsersIterator extends ResourceIterator { protected function sendRequest() { // If a next token is set, then add it to the command if ($this->nextToken) { $this->command->set('next_user', $this->nextToken); } // Execute the command and parse the result $result = $this->command->execute(); // Parse the next token $this->nextToken = isset($result['next_user']) ? $result['next_user'] : false; return $result['users']; } } As you can see, it's pretty simple to implement an iterator. There are a few things that you should notice from this example: 1. You do not need to create a new command in the ``sendRequest()`` method. A new command object is cloned from the original command passed into the constructor of the iterator before the ``sendRequest()`` method is called. Remember that the resource iterator expects a command that has not been executed. 2. When the ``sendRequest()`` method is first called, you will not have a ``$this->nextToken`` value, so always check before setting it on a command. Notice that the next token is being updated each time a request is sent. 3. After fetching more resources from the service, always return an array of resources. guzzle/docs/webservice-client/webservice-client.rst 0000604 00000070137 15173213567 0016610 0 ustar 00 ====================== The web service client ====================== The ``Guzzle\Service`` namespace contains various abstractions that help to make it easier to interact with a web service API, including commands, service descriptions, and resource iterators. In this chapter, we'll build a simple `Twitter API client <https://dev.twitter.com/docs/api/1.1>`_. Creating a client ================= A class that extends from ``Guzzle\Service\Client`` or implements ``Guzzle\Service\ClientInterface`` must implement a ``factory()`` method in order to be used with a :doc:`service builder <using-the-service-builder>`. Factory method -------------- You can use the ``factory()`` method of a client directly if you do not need a service builder. .. code-block:: php use mtdowling\TwitterClient; // Create a client and pass an array of configuration data $twitter = TwitterClient::factory(array( 'consumer_key' => '****', 'consumer_secret' => '****', 'token' => '****', 'token_secret' => '****' )); .. note:: If you'd like to follow along, here's how to get your Twitter API credentials: 1. Visit https://dev.twitter.com/apps 2. Click on an application that you've created 3. Click on the "OAuth tool" tab 4. Copy all of the settings under "OAuth Settings" Implementing a factory method ----------------------------- Creating a client and its factory method is pretty simple. You just need to implement ``Guzzle\Service\ClientInterface`` or extend from ``Guzzle\Service\Client``. .. code-block:: php namespace mtdowling; use Guzzle\Common\Collection; use Guzzle\Plugin\Oauth\OauthPlugin; use Guzzle\Service\Client; use Guzzle\Service\Description\ServiceDescription; /** * A simple Twitter API client */ class TwitterClient extends Client { public static function factory($config = array()) { // Provide a hash of default client configuration options $default = array('base_url' => 'https://api.twitter.com/1.1'); // The following values are required when creating the client $required = array( 'base_url', 'consumer_key', 'consumer_secret', 'token', 'token_secret' ); // Merge in default settings and validate the config $config = Collection::fromConfig($config, $default, $required); // Create a new Twitter client $client = new self($config->get('base_url'), $config); // Ensure that the OauthPlugin is attached to the client $client->addSubscriber(new OauthPlugin($config->toArray())); return $client; } } Service Builder --------------- A service builder is used to easily create web service clients, provides a simple configuration driven approach to creating clients, and allows you to share configuration settings across multiple clients. You can find out more about Guzzle's service builder in :doc:`using-the-service-builder`. .. code-block:: php use Guzzle\Service\Builder\ServiceBuilder; // Create a service builder and provide client configuration data $builder = ServiceBuilder::factory('/path/to/client_config.json'); // Get the client from the service builder by name $twitter = $builder->get('twitter'); The above example assumes you have JSON data similar to the following stored in "/path/to/client_config.json": .. code-block:: json { "services": { "twitter": { "class": "mtdowling\\TwitterClient", "params": { "consumer_key": "****", "consumer_secret": "****", "token": "****", "token_secret": "****" } } } } .. note:: A service builder becomes much more valuable when using multiple web service clients in a single application or if you need to utilize the same client with varying configuration settings (e.g. multiple accounts). Commands ======== Commands are a concept in Guzzle that helps to hide the underlying implementation of an API by providing an easy to use parameter driven object for each action of an API. A command is responsible for accepting an array of configuration parameters, serializing an HTTP request, and parsing an HTTP response. Following the `command pattern <http://en.wikipedia.org/wiki/Command_pattern>`_, commands in Guzzle offer a greater level of flexibility when implementing and utilizing a web service client. Executing commands ------------------ You must explicitly execute a command after creating a command using the ``getCommand()`` method. A command has an ``execute()`` method that may be called, or you can use the ``execute()`` method of a client object and pass in the command object. Calling either of these execute methods will return the result value of the command. The result value is the result of parsing the HTTP response with the ``process()`` method. .. code-block:: php // Get a command from the client and pass an array of parameters $command = $twitter->getCommand('getMentions', array( 'count' => 5 )); // Other parameters can be set on the command after it is created $command['trim_user'] = false; // Execute the command using the command object. // The result value contains an array of JSON data from the response $result = $command->execute(); // You can retrieve the result of the command later too $result = $command->getResult(). Command object also contains methods that allow you to inspect the HTTP request and response that was utilized with the command. .. code-block:: php $request = $command->getRequest(); $response = $command->getResponse(); .. note:: The format and notation used to retrieve commands from a client can be customized by injecting a custom command factory, ``Guzzle\Service\Command\Factory\FactoryInterface``, on the client using ``$client->setCommandFactory()``. Executing with magic methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When using method missing magic methods with a command, the command will be executed right away and the result of the command is returned. .. code-block:: php $jsonData = $twitter->getMentions(array( 'count' => 5, 'trim_user' => true )); Creating commands ----------------- Commands are created using either the ``getCommand()`` method of a client or a magic missing method of a client. Using the ``getCommand()`` method allows you to create a command without executing it, allowing for customization of the command or the request serialized by the command. When a client attempts to create a command, it uses the client's ``Guzzle\Service\Command\Factory\FactoryInterface``. By default, Guzzle will utilize a command factory that first looks for a concrete class for a particular command (concrete commands) followed by a command defined by a service description (operation commands). We'll learn more about concrete commands and operation commands later in this chapter. .. code-block:: php // Get a command from the twitter client. $command = $twitter->getCommand('getMentions'); $result = $command->execute(); Unless you've skipped ahead, running the above code will throw an exception. PHP Fatal error: Uncaught exception 'Guzzle\Common\Exception\InvalidArgumentException' with message 'Command was not found matching getMentions' This exception was thrown because the "getMentions" command has not yet been implemented. Let's implement one now. Concrete commands ~~~~~~~~~~~~~~~~~ Commands can be created in one of two ways: create a concrete command class that extends ``Guzzle\Service\Command\AbstractCommand`` or :doc:`create an OperationCommand based on a service description <guzzle-service-descriptions>`. The recommended approach is to use a service description to define your web service, but you can use concrete commands when custom logic must be implemented for marshaling or unmarshaling a HTTP message. Commands are the method in which you abstract away the underlying format of the requests that need to be sent to take action on a web service. Commands in Guzzle are meant to be built by executing a series of setter methods on a command object. Commands are only validated right before they are executed. A ``Guzzle\Service\Client`` object is responsible for executing commands. Commands created for your web service must implement ``Guzzle\Service\Command\CommandInterface``, but it's easier to extend the ``Guzzle\Service\Command\AbstractCommand`` class, implement the ``build()`` method, and optionally implement the ``process()`` method. Serializing requests ^^^^^^^^^^^^^^^^^^^^ The ``build()`` method of a command is responsible for using the arguments of the command to build and serialize a HTTP request and set the request on the ``$request`` property of the command object. This step is usually taken care of for you when using a service description driven command that uses the default ``Guzzle\Service\Command\OperationCommand``. You may wish to implement the process method yourself when you aren't using a service description or need to implement more complex request serialization. .. important:::: When implementing a custom ``build()`` method, be sure to set the class property of ``$this->request`` to an instantiated and ready to send request. The following example shows how to implement the ``getMentions`` `Twitter API <https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline>`_ method using a concrete command. .. code-block:: php namespace mtdowling\Twitter\Command; use Guzzle\Service\Command\AbstractCommand; class GetMentions extends AbstractCommand { protected function build() { // Create the request property of the command $this->request = $this->client->get('statuses/mentions_timeline.json'); // Grab the query object of the request because we will use it for // serializing command parameters on the request $query = $this->request->getQuery(); if ($this['count']) { $query->set('count', $this['count']); } if ($this['since_id']) { $query->set('since_id', $this['since_id']); } if ($this['max_id']) { $query->set('max_id', $this['max_id']); } if ($this['trim_user'] !== null) { $query->set('trim_user', $this['trim_user'] ? 'true' : 'false'); } if ($this['contributor_details'] !== null) { $query->set('contributor_details', $this['contributor_details'] ? 'true' : 'false'); } if ($this['include_entities'] !== null) { $query->set('include_entities', $this['include_entities'] ? 'true' : 'false'); } } } By default, a client will attempt to find concrete command classes under the ``Command`` namespace of a client. First the client will attempt to find an exact match for the name of the command to the name of the command class. If an exact match is not found, the client will calculate a class name using inflection. This is calculated based on the folder hierarchy of a command and converting the CamelCased named commands into snake_case. Here are some examples on how the command names are calculated: #. ``Foo\Command\JarJar`` **->** jar_jar #. ``Foo\Command\Test`` **->** test #. ``Foo\Command\People\GetCurrentPerson`` **->** people.get_current_person Notice how any sub-namespace beneath ``Command`` is converted from ``\`` to ``.`` (a period). CamelCasing is converted to lowercased snake_casing (e.g. JarJar == jar_jar). Parsing responses ^^^^^^^^^^^^^^^^^ The ``process()`` method of a command is responsible for converting an HTTP response into something more useful. For example, a service description operation that has specified a model object in the ``responseClass`` attribute of the operation will set a ``Guzzle\Service\Resource\Model`` object as the result of the command. This behavior can be completely modified as needed-- even if you are using operations and responseClass models. Simply implement a custom ``process()`` method that sets the ``$this->result`` class property to whatever you choose. You can reuse parts of the default Guzzle response parsing functionality or get inspiration from existing code by using ``Guzzle\Service\Command\OperationResponseParser`` and ``Guzzle\Service\Command\DefaultResponseParser`` classes. If you do not implement a custom ``process()`` method and are not using a service description, then Guzzle will attempt to guess how a response should be processed based on the Content-Type header of the response. Because the Twitter API sets a ``Content-Type: application/json`` header on this response, we do not need to implement any custom response parsing. Operation commands ~~~~~~~~~~~~~~~~~~ Operation commands are commands in which the serialization of an HTTP request and the parsing of an HTTP response are driven by a Guzzle service description. Because request serialization, validation, and response parsing are described using a DSL, creating operation commands is a much faster process than writing concrete commands. Creating operation commands for our Twitter client can remove a great deal of redundancy from the previous concrete command, and allows for a deeper runtime introspection of the API. Here's an example service description we can use to create the Twitter API client: .. code-block:: json { "name": "Twitter", "apiVersion": "1.1", "baseUrl": "https://api.twitter.com/1.1", "description": "Twitter REST API client", "operations": { "GetMentions": { "httpMethod": "GET", "uri": "statuses/mentions_timeline.json", "summary": "Returns the 20 most recent mentions for the authenticating user.", "responseClass": "GetMentionsOutput", "parameters": { "count": { "description": "Specifies the number of tweets to try and retrieve", "type": "integer", "location": "query" }, "since_id": { "description": "Returns results with an ID greater than the specified ID", "type": "integer", "location": "query" }, "max_id": { "description": "Returns results with an ID less than or equal to the specified ID.", "type": "integer", "location": "query" }, "trim_user": { "description": "Limits the amount of data returned for each user", "type": "boolean", "location": "query" }, "contributor_details": { "description": "Adds more data to contributor elements", "type": "boolean", "location": "query" }, "include_entities": { "description": "The entities node will be disincluded when set to false.", "type": "boolean", "location": "query" } } } }, "models": { "GetMentionsOutput": { "type": "object", "additionalProperties": { "location": "json" } } } } If you're lazy, you can define the API in a less descriptive manner using ``additionalParameters``. ``additionalParameters`` define the serialization and validation rules of parameters that are not explicitly defined in a service description. .. code-block:: json { "name": "Twitter", "apiVersion": "1.1", "baseUrl": "https://api.twitter.com/1.1", "description": "Twitter REST API client", "operations": { "GetMentions": { "httpMethod": "GET", "uri": "statuses/mentions_timeline.json", "summary": "Returns the 20 most recent mentions for the authenticating user.", "responseClass": "GetMentionsOutput", "additionalParameters": { "location": "query" } } }, "models": { "GetMentionsOutput": { "type": "object", "additionalProperties": { "location": "json" } } } } You should attach the service description to the client at the end of the client's factory method: .. code-block:: php // ... class TwitterClient extends Client { public static function factory($config = array()) { // ... same code as before ... // Set the service description $client->setDescription(ServiceDescription::factory('path/to/twitter.json')); return $client; } } The client can now use operations defined in the service description instead of requiring you to create concrete command classes. Feel free to delete the concrete command class we created earlier. .. code-block:: php $jsonData = $twitter->getMentions(array( 'count' => 5, 'trim_user' => true )); Executing commands in parallel ------------------------------ Much like HTTP requests, Guzzle allows you to send multiple commands in parallel. You can send commands in parallel by passing an array of command objects to a client's ``execute()`` method. The client will serialize each request and send them all in parallel. If an error is encountered during the transfer, then a ``Guzzle\Service\Exception\CommandTransferException`` is thrown, which allows you to retrieve a list of commands that succeeded and a list of commands that failed. .. code-block:: php use Guzzle\Service\Exception\CommandTransferException; $commands = array(); $commands[] = $twitter->getCommand('getMentions'); $commands[] = $twitter->getCommand('otherCommandName'); // etc... try { $result = $client->execute($commands); foreach ($result as $command) { echo $command->getName() . ': ' . $command->getResponse()->getStatusCode() . "\n"; } } catch (CommandTransferException $e) { // Get an array of the commands that succeeded foreach ($e->getSuccessfulCommands() as $command) { echo $command->getName() . " succeeded\n"; } // Get an array of the commands that failed foreach ($e->getFailedCommands() as $command) { echo $command->getName() . " failed\n"; } } .. note:: All commands executed from a client using an array must originate from the same client. Special command options ----------------------- Guzzle exposes several options that help to control how commands are validated, serialized, and parsed. Command options can be specified when creating a command or in the ``command.params`` parameter in the ``Guzzle\Service\Client``. =========================== ============================================================================================ command.request_options Option used to add :ref:`Request options <request-options>` to the request created by a command command.hidden_params An array of the names of parameters ignored by the ``additionalParameters`` parameter schema command.disable_validation Set to true to disable JSON schema validation of the command's input parameters command.response_processing Determines how the default response parser will parse the command. One of "raw" no parsing, "model" (the default method used to parse commands using response models defined in service descriptions) command.headers (deprecated) Option used to specify custom headers. Use ``command.request_options`` instead command.on_complete (deprecated) Option used to add an onComplete method to a command. Use ``command.after_send`` event instead command.response_body (deprecated) Option used to change the entity body used to store a response. Use ``command.request_options`` instead =========================== ============================================================================================ Advanced client configuration ============================= Default command parameters -------------------------- When creating a client object, you can specify default command parameters to pass into all commands. Any key value pair present in the ``command.params`` settings of a client will be added as default parameters to any command created by the client. .. code-block:: php $client = new Guzzle\Service\Client(array( 'command.params' => array( 'default_1' => 'foo', 'another' => 'bar' ) )); Magic methods ------------- Client objects will, by default, attempt to create and execute commands when a missing method is invoked on a client. This powerful concept applies to both concrete commands and operation commands powered by a service description. This makes it appear to the end user that you have defined actual methods on a client object, when in fact, the methods are invoked using PHP's magic ``__call`` method. The ``__call`` method uses the ``getCommand()`` method of a client, which uses the client's internal ``Guzzle\Service\Command\Factory\FactoryInterface`` object. The default command factory allows you to instantiate operations defined in a client's service description. The method in which a client determines which command to execute is defined as follows: 1. The client will first try to find a literal match for an operation in the service description. 2. If the literal match is not found, the client will try to uppercase the first character of the operation and find the match again. 3. If a match is still not found, the command factory will inflect the method name from CamelCase to snake_case and attempt to find a matching command. 4. If a command still does not match, an exception is thrown. .. code-block:: php // Use the magic method $result = $twitter->getMentions(); // This is exactly the same as: $result = $twitter->getCommand('getMentions')->execute(); You can disable magic methods on a client by passing ``false`` to the ``enableMagicMethod()`` method. Custom command factory ---------------------- A client by default uses the ``Guzzle\Service\Command\Factory\CompositeFactory`` which allows multiple command factories to attempt to create a command by a certain name. The default CompositeFactory uses a ``ConcreteClassFactory`` and a ``ServiceDescriptionFactory`` if a service description is specified on a client. You can specify a custom command factory if your client requires custom command creation logic using the ``setCommandFactory()`` method of a client. Custom resource Iterator factory -------------------------------- Resource iterators can be retrieved from a client using the ``getIterator($name)`` method of a client. This method uses a client's internal ``Guzzle\Service\Resource\ResourceIteratorFactoryInterface`` object. A client by default uses a ``Guzzle\Service\Resource\ResourceIteratorClassFactory`` to attempt to find concrete classes that implement resource iterators. The default factory will first look for matching iterators in the ``Iterator`` subdirectory of the client followed by the ``Model`` subdirectory of a client. Use the ``setResourceIteratorFactory()`` method of a client to specify a custom resource iterator factory. Plugins and events ================== ``Guzzle\Service\Client`` exposes various events that allow you to hook in custom logic. A client object owns a ``Symfony\Component\EventDispatcher\EventDispatcher`` object that can be accessed by calling ``$client->getEventDispatcher()``. You can use the event dispatcher to add listeners (a simple callback function) or event subscribers (classes that listen to specific events of a dispatcher). .. _service-client-events: Events emitted from a Service Client ------------------------------------ A ``Guzzle\Service\Client`` object emits the following events: +------------------------------+--------------------------------------------+------------------------------------------+ | Event name | Description | Event data | +==============================+============================================+==========================================+ | client.command.create | The client created a command object | * client: Client object | | | | * command: Command object | +------------------------------+--------------------------------------------+------------------------------------------+ | command.before_prepare | Before a command is validated and built. | * command: Command being prepared | | | This is also before a request is created. | | +------------------------------+--------------------------------------------+------------------------------------------+ | command.after_prepare | After a command instantiates and | * command: Command that was prepared | | | configures its request object. | | +------------------------------+--------------------------------------------+------------------------------------------+ | command.before_send | The client is about to execute a prepared | * command: Command to execute | | | command | | +------------------------------+--------------------------------------------+------------------------------------------+ | command.after_send | The client successfully completed | * command: The command that was executed | | | executing a command | | +------------------------------+--------------------------------------------+------------------------------------------+ | command.parse_response | Called when ``responseType`` is ``class`` | * command: The command with a response | | | and the response is about to be parsed. | about to be parsed. | +------------------------------+--------------------------------------------+------------------------------------------+ .. code-block:: php use Guzzle\Common\Event; use Guzzle\Service\Client; $client = new Client(); // create an event listener that operates on request objects $client->getEventDispatcher()->addListener('command.after_prepare', function (Event $event) { $command = $event['command']; $request = $command->getRequest(); // do something with request }); .. code-block:: php use Guzzle\Common\Event; use Guzzle\Common\Client; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class EventSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { return array( 'client.command.create' => 'onCommandCreate', 'command.parse_response' => 'onParseResponse' ); } public function onCommandCreate(Event $event) { $client = $event['client']; $command = $event['command']; // operate on client and command } public function onParseResponse(Event $event) { $command = $event['command']; // operate on the command } } $client = new Client(); $client->addSubscriber(new EventSubscriber()); guzzle/docs/webservice-client/using-the-service-builder.rst 0000604 00000032547 15173213567 0020166 0 ustar 00 ======================= Using a service builder ======================= The best way to instantiate Guzzle web service clients is to let Guzzle handle building the clients for you using a ServiceBuilder. A ServiceBuilder is responsible for creating concrete client objects based on configuration settings and helps to manage credentials for different environments. You don't have to use a service builder, but they help to decouple your application from concrete classes and help to share configuration data across multiple clients. Consider the following example. Here we are creating two clients that require the same API public key and secret key. The clients are created using their ``factory()`` methods. .. code-block:: php use MyService\FooClient; use MyService\BarClient; $foo = FooClient::factory(array( 'key' => 'abc', 'secret' => '123', 'custom' => 'and above all' )); $bar = BarClient::factory(array( 'key' => 'abc', 'secret' => '123', 'custom' => 'listen to me' )); The redundant specification of the API keys can be removed using a service builder. .. code-block:: php use Guzzle\Service\Builder\ServiceBuilder; $builder = ServiceBuilder::factory(array( 'services' => array( 'abstract_client' => array( 'params' => array( 'key' => 'abc', 'secret' => '123' ) ), 'foo' => array( 'extends' => 'abstract_client', 'class' => 'MyService\FooClient', 'params' => array( 'custom' => 'and above all' ) ), 'bar' => array( 'extends' => 'abstract_client', 'class' => 'MyService\FooClient', 'params' => array( 'custom' => 'listen to me' ) ) ) )); $foo = $builder->get('foo'); $bar = $builder->get('bar'); You can make managing your API keys even easier by saving the service builder configuration in a JSON format in a .json file. Creating a service builder -------------------------- A ServiceBuilder can source information from an array, an PHP include file that returns an array, or a JSON file. .. code-block:: php use Guzzle\Service\Builder\ServiceBuilder; // Source service definitions from a JSON file $builder = ServiceBuilder::factory('services.json'); Sourcing data from an array ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Data can be source from a PHP array. The array must contain an associative ``services`` array that maps the name of a client to the configuration information used by the service builder to create the client. Clients are given names which are used to identify how a client is retrieved from a service builder. This can be useful for using multiple accounts for the same service or creating development clients vs. production clients. .. code-block:: php $services = array( 'includes' => array( '/path/to/other/services.json', '/path/to/other/php_services.php' ), 'services' => array( 'abstract.foo' => array( 'params' => array( 'username' => 'foo', 'password' => 'bar' ) ), 'bar' => array( 'extends' => 'abstract.foo', 'class' => 'MyClientClass', 'params' => array( 'other' => 'abc' ) ) ) ); A service builder configuration array contains two top-level array keys: +------------+---------------------------------------------------------------------------------------------------------+ | Key | Description | +============+=========================================================================================================+ | includes | Array of paths to JSON or PHP include files to include in the configuration. | +------------+---------------------------------------------------------------------------------------------------------+ | services | Associative array of defined services that can be created by the service builder. Each service can | | | contain the following keys: | | | | | | +------------+----------------------------------------------------------------------------------------+ | | | | Key | Description | | | | +============+========================================================================================+ | | | | class | The concrete class to instantiate that implements the | | | | | | ``Guzzle\Common\FromConfigInterface``. | | | | +------------+----------------------------------------------------------------------------------------+ | | | | extends | The name of a previously defined service to extend from | | | | +------------+----------------------------------------------------------------------------------------+ | | | | params | Associative array of parameters to pass to the factory method of the service it is | | | | | | instantiated | | | | +------------+----------------------------------------------------------------------------------------+ | | | | alias | An alias that can be used in addition to the array key for retrieving a client from | | | | | | the service builder. | | | | +------------+----------------------------------------------------------------------------------------+ | +------------+---------------------------------------------------------------------------------------------------------+ The first client defined, ``abstract.foo``, is used as a placeholder of shared configuration values. Any service extending abstract.foo will inherit its params. As an example, this can be useful when clients share the same username and password. The next client, ``bar``, extends from ``abstract.foo`` using the ``extends`` attribute referencing the client from which to extend. Additional parameters can be merged into the original service definition when extending a parent service. .. important:: Each client that you intend to instantiate must specify a ``class`` attribute that references the full class name of the client being created. The class referenced in the ``class`` parameter must implement a static ``factory()`` method that accepts an array or ``Guzzle\Common\Collection`` object and returns an instantiated object. Sourcing from a PHP include ~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can create service builder configurations using a PHP include file. This can be useful if you wish to take advantage of an opcode cache like APC to speed up the process of loading and processing the configuration. The PHP include file is the same format as an array, but you simply create a PHP script that returns an array and save the file with the .php file extension. .. code-block:: php <?php return array('services' => '...'); // Saved as config.php This configuration file can then be used with a service builder. .. code-block:: php $builder = ServiceBuilder::factory('/path/to/config.php'); Sourcing from a JSON document ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can use JSON documents to serialize your service descriptions. The JSON format uses the exact same structure as the PHP array syntax, but it's just serialized using JSON. .. code-block:: javascript { "includes": ["/path/to/other/services.json", "/path/to/other/php_services.php"], "services": { "abstract.foo": { "params": { "username": "foo", "password": "bar" } }, "bar": { "extends": "abstract.foo", "class": "MyClientClass", "params": { "other": "abc" } } } } Referencing other clients in parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If one of your clients depends on another client as one of its parameters, you can reference that client by name by enclosing the client's reference key in ``{}``. .. code-block:: javascript { "services": { "token": { "class": "My\Token\TokenFactory", "params": { "access_key": "xyz" } }, "client": { "class": "My\Client", "params": { "token_client": "{token}", "version": "1.0" } } } } When ``client`` is constructed by the service builder, the service builder will first create the ``token`` service and then inject the token service into ``client``'s factory method in the ``token_client`` parameter. Retrieving clients from a service builder ----------------------------------------- Clients are referenced using a customizable name you provide in your service definition. The ServiceBuilder is a sort of multiton object-- it will only instantiate a client once and return that client for subsequent retrievals. Clients are retrieved by name (the array key used in the configuration) or by the ``alias`` setting of a service. Here's an example of retrieving a client from your ServiceBuilder: .. code-block:: php $client = $builder->get('foo'); // You can also use the ServiceBuilder object as an array $client = $builder['foo']; Creating throwaway clients ~~~~~~~~~~~~~~~~~~~~~~~~~~ You can get a "throwaway" client (a client that is not persisted by the ServiceBuilder) by passing ``true`` in the second argument of ``ServiceBuilder::get()``. This allows you to create a client that will not be returned by other parts of your code that use the service builder. Instead of passing ``true``, you can pass an array of configuration settings that will override the configuration settings specified in the service builder. .. code-block:: php // Get a throwaway client and overwrite the "custom" setting of the client $foo = $builder->get('foo', array( 'custom' => 'in this world there are rules' )); Getting raw configuration settings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can get the raw configuration settings provided to the service builder for a specific service using the ``getData($name)`` method of a service builder. This method will null if the service was not found in the service builder or an array of configuration settings if the service was found. .. code-block:: php $data = $builder->getData('foo'); echo $data['key'] . "\n"; echo $data['secret'] . "\n"; echo $data['custom'] . "\n"; Adding a plugin to all clients ------------------------------ You can add a plugin to all clients created by a service builder using the ``addGlobalPlugin($plugin)`` method of a service builder and passing a ``Symfony\Component\EventDispatcher\EventSubscriberInterface`` object. The service builder will then attach each global plugin to every client as it is created. This allows you to, for example, add a LogPlugin to every request created by a service builder for easy debugging. .. code-block:: php use Guzzle\Plugin\Log\LogPlugin; // Add a debug log plugin to every client as it is created $builder->addGlobalPlugin(LogPlugin::getDebugPlugin()); $foo = $builder->get('foo'); $foo->get('/')->send(); // Should output all of the data sent over the wire .. _service-builder-events: Events emitted from a service builder ------------------------------------- A ``Guzzle\Service\Builder\ServiceBuilder`` object emits the following events: +-------------------------------+--------------------------------------------+-----------------------------------------+ | Event name | Description | Event data | +===============================+============================================+=========================================+ | service_builder.create_client | Called when a client is created | * client: The created client object | +-------------------------------+--------------------------------------------+-----------------------------------------+ .. code-block:: php use Guzzle\Common\Event; use Guzzle\Service\Builder\ServiceBuilder; $builder = ServiceBuilder::factory('/path/to/config.json'); // Add an event listener to print out each client client as it is created $builder->getEventDispatcher()->addListener('service_builder.create_client', function (Event $e) { echo 'Client created: ' . get_class($e['client']) . "\n"; }); $foo = $builder->get('foo'); // Should output the class used for the "foo" client guzzle/docs/webservice-client/guzzle-service-descriptions.rst 0000604 00000072732 15173213567 0020663 0 ustar 00 =========================== Guzzle service descriptions =========================== Guzzle allows you to serialize HTTP requests and parse HTTP responses using a DSL called a service descriptions. Service descriptions define web service APIs by documenting each operation, the operation's parameters, validation options for each parameter, an operation's response, how the response is parsed, and any errors that can be raised for an operation. Writing a service description for a web service allows you to more quickly consume a web service than writing concrete commands for each web service operation. Guzzle service descriptions can be representing using a PHP array or JSON document. Guzzle's service descriptions are heavily inspired by `Swagger <http://swagger.wordnik.com/>`_. Service description schema ========================== A Guzzle Service description must match the following JSON schema document. This document can also serve as a guide when implementing a Guzzle service description. Download the schema here: :download:`Guzzle JSON schema document </_downloads/guzzle-schema-1.0.json>` .. class:: overflow-height-500px .. literalinclude:: ../_downloads/guzzle-schema-1.0.json :language: json Top-level attributes -------------------- Service descriptions are comprised of the following top-level attributes: .. code-block:: json { "name": "string", "apiVersion": "string|number", "baseUrl": "string", "description": "string", "operations": {}, "models": {}, "includes": ["string.php", "string.json"] } +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | Property Name | Value | Description | +=========================================+=========================+=======================================================================================================================+ | name | string | Name of the web service | +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | apiVersion | string|number | Version identifier that the service description is compatible with | +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | baseUrl or basePath | string | Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the | | | | process defined in RFC 2396. Some clients require custom logic to determine the baseUrl. In those cases, it is best | | | | to not include a baseUrl in the service description, but rather allow the factory method of the client to configure | | | | the client’s baseUrl. | +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | description | string | Short summary of the web service | +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | operations | object containing | Operations of the service. The key is the name of the operation and value is the attributes of the operation. | | | :ref:`operation-schema` | | | | | | +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | models | object containing | Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP | | | :ref:`model-schema` | response is parsed into a ``Guzzle\Service\Resource\Model`` object when an operation uses a ``model`` ``responseType``| +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | includes | array of .js, | Service description files to include and extend from (can be a .json, .js, or .php file) | | | .json, or .php | | | | files. | | +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ | (any additional properties) | mixed | Any additional properties specified as top-level attributes are allowed and will be treated as arbitrary data | +-----------------------------------------+-------------------------+-----------------------------------------------------------------------------------------------------------------------+ .. _operation-schema: Operations ---------- Operations are the actions that can be taken on a service. Each operation is given a unique name and has a distinct endpoint and HTTP method. If an API has a ``DELETE /users/:id`` operation, a satisfactory operation name might be ``DeleteUser`` with a parameter of ``id`` that is inserted into the URI. .. class:: overflow-height-250px .. code-block:: json { "operations": { "operationName": { "extends": "string", "httpMethod": "GET|POST|PUT|DELETE|PATCH|string", "uri": "string", "summary": "string", "class": "string", "responseClass": "string", "responseNotes": "string", "type": "string", "description": "string", "responseType": "primitive|class|(model by name)|documentation|(string)", "deprecated": false, "errorResponses": [ { "code": 500, "reason": "Unexpected Error", "class": "string" } ], "data": { "foo": "bar", "baz": "bam" }, "parameters": {} } } } .. csv-table:: :header: "Property Name", "Value", "Description" :widths: 20, 15, 65 "extends", "string", "Extend from another operation by name. The parent operation must be defined before the child." "httpMethod", "string", "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" "uri", "string", "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" "summary", "string", "Short summary of what the operation does" "class", "string", "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand. Using this attribute allows you to define an operation using a service description, but allows more customized logic to be implemented in user-land code." "responseClass", "string", "Defined what is returned from the method. Can be a primitive, class name, or model name. You can specify the name of a class to return a more customized result from the operation (for example, a domain model object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``." "responseNotes", "string", "A description of the response returned by the operation" "responseType", "string", "The type of response that the operation creates: one of primitive, class, model, or documentation. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default." "deprecated", "boolean", "Whether or not the operation is deprecated" "errorResponses", "array", "Errors that could occur while executing the operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), 'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this error is encountered)" "data", "object", "Any arbitrary data to associate with the operation" "parameters", "object containing :ref:`parameter-schema` objects", "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." "additionalParameters", "A single :ref:`parameter-schema` object", "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." additionalParameters ~~~~~~~~~~~~~~~~~~~~ When a webservice offers a large number of parameters that all are set in the same location (for example the query string or a JSON document), defining each parameter individually can require a lot of time and repetition. Furthermore, some web services allow for completely arbitrary parameters to be supplied for an operation. The ``additionalParameters`` attribute can be used to solve both of these issues. As an example, we can define a Twitter API operation quite easily using ``additionalParameters``. The GetMentions operation accepts a large number of query string parameters. Defining each of these parameters is ideal because it provide much more introspection for the client and opens the possibility to use the description with other tools (e.g. a documentation generator). However, you can very quickly provide a "catch-all" serialization rule that will place any custom parameters supplied to an operation the generated request's query string parameters. .. class:: overflow-height-250px .. code-block:: json { "name": "Twitter", "apiVersion": "1.1", "baseUrl": "https://api.twitter.com/1.1", "operations": { "GetMentions": { "httpMethod": "GET", "uri": "statuses/mentions_timeline.json", "responseClass": "GetMentionsOutput", "additionalParameters": { "location": "query" } } }, "models": { "GetMentionsOutput": { "type": "object", "additionalProperties": { "location": "json" } } } } responseClass ~~~~~~~~~~~~~ The ``responseClass`` attribute is used to define the return value of an operation (what is returned by calling the ``getResult()`` method of a command object). The value set in the responseClass attribute can be one of "primitive" (meaning the result with be primitive type like a string), a class name meaning the result will be an instance of a specific user-land class, or a model name meaning the result will be a ``Guzzle\Service\Resource\Model`` object that uses a :ref:`model schema <model-schema>` to define how the HTTP response is parsed. .. note:: Using a class name with a ``responseClass`` will only work if it is supported by the ``class`` that is instantiated for the operation. Keep this in mind when specifying a custom ``class`` attribute that points to a custom ``Guzzle\Service\Command\CommandInterface`` class. The default ``class``, ``Guzzle\Service\Command\OperationCommand``, does support setting custom ``class`` attributes. You can specify the name of a class to return a more customized result from the operation (for example, a domain model object). When using the name of a PHP class, the class must implement ``Guzzle\Service\Command\ResponseClassInterface``. Here's a very simple example of implementing a custom responseClass object. .. code-block:: json { "operations": { "test": { "responseClass": "MyApplication\\User" } } } .. code-block:: php namespace MyApplication; use Guzzle\Service\Command\ResponseClassInterface; use Guzzle\Service\Command\OperationCommand; class User implements ResponseClassInterface { protected $name; public static function fromCommand(OperationCommand $command) { $response = $command->getResponse(); $xml = $response->xml(); return new self((string) $xml->name); } public function __construct($name) { $this->name = $name; } } errorResponses ~~~~~~~~~~~~~~ ``errorResponses`` is an array containing objects that define the errors that could occur while executing the operation. Each item of the array is an object that can contain a 'code' (HTTP response status code of the error), 'reason' (reason phrase or description of the error), and 'class' (an exception class that will be raised when this error is encountered). ErrorResponsePlugin ^^^^^^^^^^^^^^^^^^^ Error responses are by default only used for documentation. If you don't need very complex exception logic for your web service errors, then you can use the ``Guzzle\Plugin\ErrorResponse\ErrorResponsePlugin`` to automatically throw defined exceptions when one of the ``errorResponse`` rules are matched. The error response plugin will listen for the ``request.complete`` event of a request created by a command object. Every response (including a successful response) is checked against the list of error responses for an exact match using the following order of checks: 1. Does the errorResponse have a defined ``class``? 2. Is the errorResponse ``code`` equal to the status code of the response? 3. Is the errorResponse ``reason`` equal to the reason phrase of the response? 4. Throw the exception stored in the ``class`` attribute of the errorResponse. The ``class`` attribute must point to a class that implements ``Guzzle\Plugin\ErrorResponse\ErrorResponseExceptionInterface``. This interface requires that an error response class implements ``public static function fromCommand(CommandInterface $command, Response $response)``. This method must return an object that extends from ``\Exception``. After an exception is returned, it is thrown by the plugin. .. _parameter-schema: Parameter schema ---------------- Parameters in both operations and models are represented using the `JSON schema <http://tools.ietf.org/id/draft-zyp-json-schema-04.html>`_ syntax. .. csv-table:: :header: "Property Name", "Value", "Description" :widths: 20, 15, 65 "name", "string", "Unique name of the parameter" "type", "string|array", "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." "instanceOf", "string", "When the type is an object, you can specify the class that the object must implement" "required", "boolean", "Whether or not the parameter is required" "default", "mixed", "Default value to use if no value is supplied" "static", "boolean", "Set to true to specify that the parameter value cannot be changed from the default setting" "description", "string", "Documentation of the parameter" "location", "string", "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" "sentAs", "string", "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." "filters", "array", "Array of functions to to run a parameter value through." filters ~~~~~~~ Each value in the array must be a string containing the full class path to a static method or an array of complex filter information. You can specify static methods of classes using the full namespace class name followed by "::" (e.g. ``FooBar::baz()``). Some filters require arguments in order to properly filter a value. For complex filters, use an object containing a ``method`` attribute pointing to a function, and an ``args`` attribute containing an array of positional arguments to pass to the function. Arguments can contain keywords that are replaced when filtering a value: ``@value`` is replaced with the value being filtered, and ``@api`` is replaced with the actual Parameter object. .. code-block:: json { "filters": [ "strtolower", { "method": "MyClass::convertString", "args": [ "test", "@value", "@api" ] } ] } The above example will filter a parameter using ``strtolower``. It will then call the ``convertString`` static method of ``MyClass``, passing in "test", the actual value of the parameter, and a ``Guzzle\Service\Description\Parameter`` object. Operation parameter location attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The location field of top-level parameters control how a parameter is serialized when generating a request. uri location ^^^^^^^^^^^^ Parameters are injected into the ``uri`` attribute of the operation using `URI-template expansion <http://tools.ietf.org/html/rfc6570>`_. .. code-block:: json { "operations": { "uriTest": { "uri": "/test/{testValue}", "parameters": { "testValue": { "location": "uri" } } } } } query location ^^^^^^^^^^^^^^ Parameters are injected into the query string of a request. Query values can be nested, which would result in a PHP style nested query string. The name of a parameter is the default name of the query string parameter added to the request. You can override this behavior by specifying the ``sentAs`` attribute on the parameter. .. code-block:: json { "operations": { "queryTest": { "parameters": { "testValue": { "location": "query", "sentAs": "test_value" } } } } } header location ^^^^^^^^^^^^^^^ Parameters are injected as headers on an HTTP request. The name of the parameter is used as the name of the header by default. You can change the name of the header created by the parameter using the ``sentAs`` attribute. Headers that are of type ``object`` will be added as multiple headers to a request using the key of the input array as the header key. Setting a ``sentAs`` attribute along with a type ``object`` will use the value of ``sentAs`` as a prefix for each header key. body location ^^^^^^^^^^^^^ Parameters are injected as the body of a request. The input of these parameters may be anything that can be cast to a string or a ``Guzzle\Http\EntityBodyInterface`` object. postField location ^^^^^^^^^^^^^^^^^^ Parameters are inserted as POST fields in a request. Nested values may be supplied and will be represented using PHP style nested query strings. The POST field name is the same as the parameter name by default. You can use the ``sentAs`` parameter to override the POST field name. postFile location ^^^^^^^^^^^^^^^^^ Parameters are added as POST files. A postFile value may be a string pointing to a local filename or a ``Guzzle\Http\Message\PostFileInterface`` object. The name of the POST file will be the name of the parameter by default. You can use a custom POST file name by using the ``sentAs`` attribute. Supports "string" and "array" types. json location ^^^^^^^^^^^^^ Parameters are added to the body of a request as top level keys of a JSON document. Nested values may be specified, with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When JSON parameters are specified, the ``Content-Type`` of the request will change to ``application/json`` if a ``Content-Type`` has not already been specified on the request. xml location ^^^^^^^^^^^^ Parameters are added to the body of a request as top level nodes of an XML document. Nested values may be specified, with any number of nested ``Guzzle\Common\ToArrayInterface`` objects. When XML parameters are specified, the ``Content-Type`` of the request will change to ``application/xml`` if a ``Content-Type`` has not already been specified on the request. responseBody location ^^^^^^^^^^^^^^^^^^^^^ Specifies the EntityBody of a response. This can be used to download the response body to a file or a custom Guzzle EntityBody object. No location ^^^^^^^^^^^ If a parameter has no location attribute, then the parameter is simply used as a data value. Other locations ^^^^^^^^^^^^^^^ Custom locations can be registered as new locations or override default locations if needed. .. _model-schema: Model Schema ------------ Models are used in service descriptions to provide generic JSON schema definitions that can be extended from or used in ``$ref`` attributes. Models can also be referenced in a ``responseClass`` attribute to provide valuable output to an operation. Models are JSON schema documents and use the exact syntax and attributes used in parameters. Response Models ~~~~~~~~~~~~~~~ Response models describe how a response is parsed into a ``Guzzle\Service\Resource\Model`` object. Response models are always modeled as JSON schema objects. When an HTTP response is parsed using a response model, the rules specified on each property of a response model will translate 1:1 as keys in a PHP associative array. When a ``sentAs`` attribute is found in response model parameters, the value retrieved from the HTTP response is retrieved using the ``sentAs`` parameter but stored in the response model using the name of the parameter. The location field of top-level parameters in a response model tell response parsers how data is retrieved from a response. statusCode location ^^^^^^^^^^^^^^^^^^^ Retrieves the status code of the response. reasonPhrase location ^^^^^^^^^^^^^^^^^^^^^ Retrieves the reason phrase of the response. header location ^^^^^^^^^^^^^^^ Retrieves a header from the HTTP response. body location ^^^^^^^^^^^^^ Retrieves the body of an HTTP response. json location ^^^^^^^^^^^^^ Retrieves a top-level parameter from a JSON document contained in an HTTP response. You can use ``additionalProperties`` if the JSON document is wrapped in an outer array. This allows you to parse the contents of each item in the array using the parsing rules defined in the ``additionalProperties`` schema. xml location ^^^^^^^^^^^^ Retrieves a top-level node value from an XML document contained in an HTTP response. Other locations ^^^^^^^^^^^^^^^ Custom locations can be registered as new locations or override default locations if needed. Example service description --------------------------- Let's say you're interacting with a web service called 'Foo' that allows for the following routes and methods:: GET/POST /users GET/DELETE /users/:id The following JSON service description implements this simple web service: .. class:: overflow-height-500px .. code-block:: json { "name": "Foo", "apiVersion": "2012-10-14", "baseUrl": "http://api.foo.com", "description": "Foo is an API that allows you to Baz Bar", "operations": { "GetUsers": { "httpMethod": "GET", "uri": "/users", "summary": "Gets a list of users", "responseClass": "GetUsersOutput" }, "CreateUser": { "httpMethod": "POST", "uri": "/users", "summary": "Creates a new user", "responseClass": "CreateUserOutput", "parameters": { "name": { "location": "json", "type": "string" }, "age": { "location": "json", "type": "integer" } } }, "GetUser": { "httpMethod": "GET", "uri": "/users/{id}", "summary": "Retrieves a single user", "responseClass": "GetUserOutput", "parameters": { "id": { "location": "uri", "description": "User to retrieve by ID", "required": true } } }, "DeleteUser": { "httpMethod": "DELETE", "uri": "/users/{id}", "summary": "Deletes a user", "responseClass": "DeleteUserOutput", "parameters": { "id": { "location": "uri", "description": "User to delete by ID", "required": true } } } }, "models": { "GetUsersOutput": { "type": "array", "items": { "type": "object", "properties": { "name": { "location": "json", "type": "string" }, "age": { "location": "json", "type": "integer" } } } }, "CreateUserOutput": { "type": "object", "properties": { "id": { "location": "json", "type": "string" }, "location": { "location": "header", "sentAs": "Location", "type": "string" } } }, "GetUserOutput": { "type": "object", "properties": { "name": { "location": "json", "type": "string" }, "age": { "location": "json", "type": "integer" } } }, "DeleteUserOutput": { "type": "object", "properties": { "status": { "location": "statusCode", "type": "integer" } } } } } If you attach this service description to a client, you would completely configure the client to interact with the Foo web service and provide valuable response models for each operation. .. code-block:: php use Guzzle\Service\Description\ServiceDescription; $description = ServiceDescription::factory('/path/to/client.json'); $client->setDescription($description); $command = $client->getCommand('DeleteUser', array('id' => 123)); $responseModel = $client->execute($command); echo $responseModel['status']; .. note:: You can add the service description to your client's factory method or constructor. guzzle/docs/batching/batching.rst 0000604 00000014010 15173213567 0013106 0 ustar 00 ======== Batching ======== Guzzle provides a fairly generic and very customizable batching framework that allows developers to efficiently transfer requests in parallel. Sending requests and commands in parallel ----------------------------------------- You can send HTTP requests in parallel by passing an array of ``Guzzle\Http\Message\RequestInterface`` objects to ``Guzzle\Http\Client::send()``: .. code-block:: php $responses = $client->send(array( $client->get('http://www.example.com/foo'), $client->get('http://www.example.com/baz') $client->get('http://www.example.com/bar') )); You can send commands in parallel by passing an array of ``Guzzle\Service\Command\CommandInterface`` objects ``Guzzle\Service\Client::execute()``: .. code-block:: php $commands = $client->execute(array( $client->getCommand('foo'), $client->getCommand('baz'), $client->getCommand('bar') )); These approaches work well for most use-cases. When you need more control over the requests that are sent in parallel or you need to send a large number of requests, you need to use the functionality provided in the ``Guzzle\Batch`` namespace. Batching overview ----------------- The batch object, ``Guzzle\Batch\Batch``, is a queue. You add requests to the queue until you are ready to transfer all of the requests. In order to efficiently transfer the items in the queue, the batch object delegates the responsibility of dividing the queue into manageable parts to a divisor (``Guzzle\Batch\BatchDivisorInterface``). The batch object then iterates over each array of items created by the divisor and sends them to the batch object's ``Guzzle\Batch\BatchTransferInterface``. .. code-block:: php use Guzzle\Batch\Batch; use Guzzle\Http\BatchRequestTransfer; // BatchRequestTransfer acts as both the divisor and transfer strategy $transferStrategy = new BatchRequestTransfer(10); $divisorStrategy = $transferStrategy; $batch = new Batch($transferStrategy, $divisorStrategy); // Add some requests to the batch queue $batch->add($request1) ->add($request2) ->add($request3); // Flush the queue and retrieve the flushed items $arrayOfTransferredRequests = $batch->flush(); .. note:: You might find that your transfer strategy will need to act as both the divisor and transfer strategy. Using the BatchBuilder ---------------------- The ``Guzzle\Batch\BatchBuilder`` makes it easier to create batch objects. The batch builder also provides an easier way to add additional behaviors to your batch object. Transferring requests ~~~~~~~~~~~~~~~~~~~~~ The ``Guzzle\Http\BatchRequestTransfer`` class efficiently transfers HTTP requests in parallel by grouping batches of requests by the curl_multi handle that is used to transfer the requests. .. code-block:: php use Guzzle\Batch\BatchBuilder; $batch = BatchBuilder::factory() ->transferRequests(10) ->build(); Transferring commands ~~~~~~~~~~~~~~~~~~~~~ The ``Guzzle\Service\Command\BatchCommandTransfer`` class efficiently transfers service commands by grouping commands by the client that is used to transfer them. You can add commands to a batch object that are transferred by different clients, and the batch will handle the rest. .. code-block:: php use Guzzle\Batch\BatchBuilder; $batch = BatchBuilder::factory() ->transferCommands(10) ->build(); $batch->add($client->getCommand('foo')) ->add($client->getCommand('baz')) ->add($client->getCommand('bar')); $commands = $batch->flush(); Batch behaviors --------------- You can add various behaviors to your batch that allow for more customizable transfers. Automatically flushing a queue ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the ``Guzzle\Batch\FlushingBatch`` decorator when you want to pump a large number of items into a batch queue and have the queue automatically flush when the size of the queue reaches a certain threshold. .. code-block:: php use Guzzle\Batch\BatchBuilder; $batch = BatchBuilder::factory() ->transferRequests(10) ->autoFlushAt(10) ->build(); Batch builder method: ``autoFlushAt($threshold)`` Notifying on flush ~~~~~~~~~~~~~~~~~~ Use the ``Guzzle\Batch\NotifyingBatch`` decorator if you want a function to be notified each time the batch queue is flushed. This is useful when paired with the flushing batch decorator. Pass a callable to the ``notify()`` method of a batch builder to use this decorator with the builder. .. code-block:: php use Guzzle\Batch\BatchBuilder; $batch = BatchBuilder::factory() ->transferRequests(10) ->autoFlushAt(10) ->notify(function (array $transferredItems) { echo 'Transferred ' . count($transferredItems) . "items\n"; }) ->build(); Batch builder method:: ``notify(callable $callback)`` Keeping a history ~~~~~~~~~~~~~~~~~ Use the ``Guzzle\Batch\HistoryBatch`` decorator if you want to maintain a history of all the items transferred with the batch queue. .. code-block:: php use Guzzle\Batch\BatchBuilder; $batch = BatchBuilder::factory() ->transferRequests(10) ->keepHistory() ->build(); After transferring items, you can use the ``getHistory()`` of a batch to retrieve an array of transferred items. Be sure to periodically clear the history using ``clearHistory()``. Batch builder method: ``keepHistory()`` Exception buffering ~~~~~~~~~~~~~~~~~~~ Use the ``Guzzle\Batch\ExceptionBufferingBatch`` decorator to buffer exceptions during a transfer so that you can transfer as many items as possible then deal with the errored batches after the transfer completes. After transfer, use the ``getExceptions()`` method of a batch to retrieve an array of ``Guzzle\Batch\Exception\BatchTransferException`` objects. You can use these exceptions to attempt to retry the failed batches. Be sure to clear the buffered exceptions when you are done with them by using the ``clearExceptions()`` method. Batch builder method: ``bufferExceptions()`` guzzle/docs/_downloads/guzzle-schema-1.0.json 0000604 00000021317 15173213567 0015124 0 ustar 00 { "additionalProperties": true, "name": { "type": "string", "description": "Name of the web service" }, "apiVersion": { "type": ["string", "number"], "description": "Version identifier that the service description is compatible with" }, "baseUrl": { "type": "string", "description": "Base URL of the web service. Any relative URI specified in an operation will be merged with the baseUrl using the process defined in RFC 2396" }, "basePath": { "type": "string", "description": "Alias of baseUrl" }, "_description": { "type": "string", "description": "Short summary of the web service. This is actually called 'description' but this JSON schema wont validate using just description." }, "operations": { "description": "Operations of the web service", "type": "object", "properties": { "extends": { "type": "string", "description": "Extend from another operation by name. The parent operation must be defined before the child." }, "httpMethod": { "type": "string", "description": "HTTP method used with the operation (e.g. GET, POST, PUT, DELETE, PATCH, etc)" }, "uri": { "type": "string", "description": "URI of the operation. The uri attribute can contain URI templates. The variables of the URI template are parameters of the operation with a location value of uri" }, "summary": { "type": "string", "description": "Short summary of what the operation does" }, "class": { "type": "string", "description": "Custom class to instantiate instead of the default Guzzle\\Service\\Command\\OperationCommand" }, "responseClass": { "type": "string", "description": "This is what is returned from the method. Can be a primitive, class name, or model name." }, "responseNotes": { "type": "string", "description": "A description of the response returned by the operation" }, "responseType": { "type": "string", "description": "The type of response that the operation creates. If not specified, this value will be automatically inferred based on whether or not there is a model matching the name, if a matching class name is found, or set to 'primitive' by default.", "enum": [ "primitive", "class", "model", "documentation" ] }, "deprecated": { "type": "boolean", "description": "Whether or not the operation is deprecated" }, "errorResponses": { "description": "Errors that could occur while executing the operation", "type": "array", "items": { "type": "object", "properties": { "code": { "type": "number", "description": "HTTP response status code of the error" }, "reason": { "type": "string", "description": "Response reason phrase or description of the error" }, "class": { "type": "string", "description": "A custom exception class that would be thrown if the error is encountered" } } } }, "data": { "type": "object", "additionalProperties": "true" }, "parameters": { "$ref": "parameters", "description": "Parameters of the operation. Parameters are used to define how input data is serialized into a HTTP request." }, "additionalParameters": { "$ref": "parameters", "description": "Validation and serialization rules for any parameter supplied to the operation that was not explicitly defined." } } }, "models": { "description": "Schema models that can be referenced throughout the service description. Models can be used to define how an HTTP response is parsed into a Guzzle\\Service\\Resource\\Model object.", "type": "object", "properties": { "$ref": "parameters", "description": "Parameters of the model. When a model is referenced in a responseClass attribute of an operation, parameters define how a HTTP response message is parsed into a Guzzle\\Service\\Resource\\Model." } }, "includes": { "description": "Service description files to include and extend from (can be a .json, .js, or .php file)", "type": "array", "items": { "type": "string", "pattern": ".+\\.(js|json|php)$" } }, "definitions": { "parameters": { "extends": "http://json-schema.org/schema", "id": "parameters", "name": { "type": "string", "description": "Unique name of the parameter" }, "type": { "type": ["string", "array"], "description": "Type of variable (string, number, integer, boolean, object, array, numeric, null, any). Types are using for validation and determining the structure of a parameter. You can use a union type by providing an array of simple types. If one of the union types matches the provided value, then the value is valid." }, "instanceOf": { "type": "string", "description": "When the type is an object, you can specify the class that the object must implement" }, "required": { "type": "boolean", "description": "Whether or not the parameter is required" }, "default": { "description": "Default value to use if no value is supplied" }, "static": { "type": "bool", "description": "Set to true to specify that the parameter value cannot be changed from the default setting" }, "description": { "type": "string", "description": "Documentation of the parameter" }, "location": { "type": "string", "description": "The location of a request used to apply a parameter. Custom locations can be registered with a command, but the defaults are uri, query, statusCode, reasonPhrase, header, body, json, xml, postField, postFile, responseBody" }, "sentAs": { "type": "string", "description": "Specifies how the data being modeled is sent over the wire. For example, you may wish to include certain headers in a response model that have a normalized casing of FooBar, but the actual header is x-foo-bar. In this case, sentAs would be set to x-foo-bar." }, "filters": { "type": "array", "description": "Array of static method names to to run a parameter value through. Each value in the array must be a string containing the full class path to a static method or an array of complex filter information. You can specify static methods of classes using the full namespace class name followed by ‘::’ (e.g. FooBar::baz()). Some filters require arguments in order to properly filter a value. For complex filters, use a hash containing a ‘method’ key pointing to a static method, and an ‘args’ key containing an array of positional arguments to pass to the method. Arguments can contain keywords that are replaced when filtering a value: '@value‘ is replaced with the value being validated, '@api‘ is replaced with the Parameter object.", "items": { "type": ["string", { "object": { "properties": { "method": { "type": "string", "description": "PHP function to call", "required": true }, "args": { "type": "array" } } } }] } } } } } guzzle/docs/_static/homepage.css 0000604 00000004561 15173213567 0012755 0 ustar 00 /* Hero unit on homepage */ .hero-unit h1 { font-size: 49px; margin-bottom: 12px; } .hero-unit { padding: 40px; } .hero-unit p { font-size: 17px; } .masthead img { float: left; margin-right: 17px; } .hero-unit ul li { margin-left: 220px; } .hero-unit .buttons { text-align: center; } .jumbotron { position: relative; padding: 40px 0; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.4), 0 0 30px rgba(0,0,0,.075); background: #00312F; background: -moz-linear-gradient(45deg, #002F31 0%, #335A6D 100%); background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#00312D), color-stop(100%,#33566D)); background: -webkit-linear-gradient(45deg, #020031 0%,#334F6D 100%); background: -o-linear-gradient(45deg, #002D31 0%,#334D6D 100%); background: -ms-linear-gradient(45deg, #002F31 0%,#33516D 100%); background: linear-gradient(45deg, #020031 0%,#33516D 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#020031', endColorstr='#6d3353',GradientType=1 ); -webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); -moz-box-shadow: inset 0 3px 7px rgba(0,0,0,.2), inset 0 -3px 7px rgba(0,0,0,.2); box-shadow: inset 0 3px 7px rgba(0, 0, 0, .2), inset 0 -3px 7px rgba(0, 0, 0, .2); } .jumbotron h1 { font-size: 80px; font-weight: bold; letter-spacing: -1px; line-height: 1; } .jumbotron p { font-size: 24px; font-weight: 300; line-height: 1.25; margin-bottom: 30px; } .masthead { padding: 40px 0 30px; margin-bottom: 0; color: #fff; margin-top: -19px; } .masthead h1 { display: none; } .masthead p { font-size: 40px; font-weight: 200; line-height: 1.25; margin: 12px 0 0 0; } .masthead .btn { padding: 19px 24px; font-size: 24px; font-weight: 200; border: 0; } /* Social bar on homepage */ .social { padding: 2px 0; text-align: center; background-color: #f5f5f5; border-top: 1px solid #fff; border-bottom: 1px solid #ddd; margin: 0 0 20px 0; } .social ul { margin-top: 0; } .social-buttons { margin-left: 0; margin-bottom: 0; padding-left: 0; list-style: none; } .social-buttons li { display: inline-block; padding: 5px 8px; line-height: 1; *display: inline; *zoom: 1; } .center-announcement { padding: 10px; background-color: rgb(238, 243, 255); border-radius: 8px; text-align: center; margin: 24px 0; } guzzle/docs/_static/prettify.css 0000604 00000001122 15173213567 0013024 0 ustar 00 .com { color: #93A1A1; } .lit { color: #195F91; } .pun, .opn, .clo { color: #93A1A1; } .fun { color: #DC322F; } .str, .atv { color: #DD1144; } .kwd, .linenums .tag { color: #1E347B; } .typ, .atn, .dec, .var { color: teal; } .pln { color: #48484C; } .prettyprint { background-color: #F7F7F9; border: 1px solid #E1E1E8; padding: 8px; } .prettyprint.linenums { box-shadow: 40px 0 0 #FBFBFC inset, 41px 0 0 #ECECF0 inset; } ol.linenums { margin: 0 0 0 33px; } ol.linenums li { color: #BEBEC5; line-height: 18px; padding-left: 12px; text-shadow: 0 1px 0 #FFFFFF; } guzzle/docs/_static/guzzle-icon.png 0000604 00000001443 15173213567 0013426 0 ustar 00 �PNG IHDR �&�� bKGD ���̿ pHYs g��R tIME�,�B� �IDAT8˕��K�e ���x4�ljs���N�� ���ƊQD�"�^n���џ0��.�#X/x�6���E �Z�3_[f:���<GQ�����U�ˇ_�Ϗ����I���R)�`T��2��� �Tf�f#����MI�(��ڶ݇���L��Z8I�4�ex�<���Ng8$c|R��EG��6���v�A���b�o��j1��cvm�I�n�K##U�n�%���c����p�Zx��W�'lǭe@���Ky �#�2�P�$$,�;b�D_��C��r%<o�{4�)��W�� �4o{Q��R�Y s��VeM�5e�e�'��>�[���,j6/�K����,��p.|z���R�H�zG-��q��pْJ�|�'���7�3�^�� g�� kY)q,�5$�1��פ��Q����f�9wt�wB�%9�2����:<hg��K��a}Z�.�Y����r����W�k�i�i���>"���R2qZ5�qK.�@��A�9�/.%�=r��y�b" �]|�pUgx���K��M_�S.nUZ(3�i;4gŐr�s>�4�^^ܰ��`����n�N���}��YI5��vP����k��. Þ�gu�XW~0��1�ɤc �tYP0i�8hRz$��k�$�+��1u�[��ᴣn1�fL���[6�h��7D��؈jg����e��g��(= IEND�B`� guzzle/docs/_static/logo.png 0000604 00000743576 15173213567 0012144 0 ustar 00 �PNG IHDR _ � d`� pHYs g��R @ IDATx��]U�7��>�2s��$�ɤM �@ʄ�Œ��O,iև��=}���C�' �P$**���F����;����If���#$A��p�{�.뜻��k��oQ�9�y �%��@^�W��v��-/����K��@^�柃���K�H �|π��]�%��@^y���K /�3 ���=B�w��@^y �o��K /����=�e^y �%�W��g /���8�+�3 �|�y �%��@^�柁���K�H �|π��]�%��@^y���K /�3 ���=B�w��@^y ����l��to߾]�;.ɟ��@^y �T�J�*�`6�3#G�<�Ҟ����h�,��N�ܞo"JC�oki�t_�UL����Q>�4O���M���MM�)h�m5��uCth����r�y��������:r�[���K�Y����LSlz�Я�����B5I�K$���u���\dR��W�S���kSJ�Vb��n�ϪG�SW�X1�=P� N�9a�r��z��`S���6U�Bg��4��dN����7G���!�Y���2�cU�NW�*��b�=m���5m�9����UE����Ο�"�,\�� ���dž��t&�U�����8��j��˒��jz�������q�aA�r"S.*�@�%o:ʙ�ffM:�6�ץ ���;獳����ǖ��n��ʦ�"��ZNm��H��M��ye ����bj#���M6��A�(ߕ+W ��]]�'葈�).�H-�뮾��B�&A�a.K���"��q��w˒ۥ����Dh��E��Y%u���ױb'��*��P(K��ĥNĢ�pe�@� G�R��J%��a]�ө���IQ��V� cS2�j�GH�{���P�Nt�6��\�m����+j���Q<I�DR�s!��4n����^E�����|��uN�B��N��&R�K�S� �[���ݻ[?:kְ��>D��lj���YX�M9�<L���b�?�P(�V��v�vl���S� �U_���8x�(_�Jxʯ5�m��9�����7�(��s�NB:�]���2�L�(�Ȱ~X����$ϑM>�ۼ�=���x-&�$&�1�m+\���kl�)�s�bs3��Y&��1s��-�f�6������\����eŃ�T���b�('0.�,N"�S�]��͉�xL�KE�T�I"�1�g�3g��*%����2����u�9#�+3Υܶ_�0��E��Y��t�] ���ަ��<�n���=-��<�=���'Gڠ\m%T*�lK�DA$�Mjm���ԩS]�Z�N�h��,3L�%2[ i�(�.:Ɍ3��YB�]��AݰZ�(���x!��g\i���7=��=~�T_W���k�U81ek���udj�������x7J�]�|���6%��7�[ � ���=`K-�lJ!֥M�_�Au1>��xz#n��٧�ښ� ��s�i��c�$)u��r�-Ͷ�8��(2�*�v\�v�΅@JPE����Zfߺ��e˖YJ�6uV����B��-�������Q;.�D�>9���B?@/�� ���{D� ��'@�J��_�]�$7� ,H����_�粒 ��#�c�y.X�h,vŬf(� ��3wߣ��+��{��Ju�莥�(���J�X��;6��X�������H�-�\�=�EM�n[�� r��3i_R�� �wZ���I��RTJc%;$j*�ӣJj3�}�*�!��r˪��d̽���.�[��U�9UHRB_2MF j\&qK��@�KQ�+�Qiޒ:[͝�rOr�;tCu��%�̧*XT�����3v'����إ55���_�x�I�ݣ|_(k�M�qKRд�j[�,�ܫH�P�N��ᔘs���ĭE�I'z|��X_]�X��^�M���ߘ�"uU\�!r)E���m#�)+�9&�4��l�����t�r�J45�X�B�K� +�R�yP�&��;G2�X�j�����$c[�h�t?l��I�3{',u(|r/4}z�'��<uUmi?:b�[����ͱ���pb��¼,6^��= ����->(h��I��bu�,h�a���^l�����i2�wI:�� ��HL�mQ).�Ĥ��f��A��EBɍ�?u��+O���Z?p�y�82��G?MŴ�8�����@��A�7�N��e��ݽ��,�Eâ���Z&��g�ӹe��*M i|�nq��xu��mh%��W�a��Mئ!�}ѭ�H 2�;aDӱ��rZnO�;�����& @/�U��x`Oa;>���aQ��T"�(#��KF��0�'dEz��D�g+� 3�I��~���&��$���t�ղ�W{��+�.��L fW�/�Za�ߒ"� &���hj�L�=�$}���� N� 7�hsrt�rX����0:�"�>ؤ��i��֏-;����ߛ6] oC.�|{B��pm�em� �;�BTG��+JK�.��Q�&����'ki+��,.�B�i���̏x�C&�w�w6��}��m� Y9�hBqR�͍D�X����%m��/w���M�#r�[H��2%���O4�gf�����b���{H��M\5#�(��m��u>�!�m� ��z7�"J���Ո�^sI�z�xv�m���$��n�A�?wՎ��9��ī���X�y ��8�hG�:�eRKH��'`�.�On���ё�X!�M\�гᖲC��G"�|�c*�&��n�M�p���~��{)q�h݊4���,�̊ܦTB�Q �$��JIKtn=^����n�NM�/�~��K���_*� X-��0B�HS(�R�q�a_�]��`~a�����ロ_u0) z�v.���ZW,,�w#ȋc�<��X, ��S���ؕh���V,�?d��4�k�p=*Q�_.Y�� �^%�z$v0����FSV~%g�bQ�5�[�wIq���`���x\�9��� s�?ᠯ��K,��P��7S����]W5G�y%ϣ��S�_�������:�&�%�0z���4얧[��%gg��2[�f\$�P}K�'�z���� �1-2>��f�9"�e�s���@��ȬYPY�;a�?�%p�I�LZ �K#k;��}B����Rا�� ���26il�#uu���ٱ;^����q���~בs��T��MȦ�d��+T��Z|�g�2{Sv���^���""�%�J��}!�&���|�`Sl]mm��,[z��!��&\�9�#���E���2��6w't*�!X�|��n%T�"�0����k���=7g%m�k��a��X��ͪ�F�������G��3���Q�l�ֶJ��Kn�Șz�A�!F܊�q��6�{c����8�M�p��$Ҍ��E�|֬}��G� %�gal��C1^0������ {C�I��{9QD���/����ǜ봗S�z\R"f�>'��ݱ����'����9�quuQ^z��M���2 � �um�������>���}D 3Zn�?�+V8w sd��fd���z���ww?�l6I��"�����(�ӭ|���^�*x*���姖B�=/��Cܶ;�ndSM�3.�庺cp�#���7�o���|��:߶�M����N?�~B��0��=��{��`'Ԅ�Y¥ԢSs^mx��SO1luσօ�����D@ǣʧ�<�y�;�]�[�bwAۅ�`�R��O�m��|�7f�����W��V.1�L�#�P��^���u�@������]| ��]f� 4`6.�GC����>���(/<x_U���f ���g� �`ƳT��aǸi�sJ ʪ,Ӿ Xʩ�M���Xr��zG�>���C�e�U�&B|�Ol�+�[|p]W� ���HǗ>z�ſ 7���iM���*��v�w�\^��i�2����j�����N�qL[{�\5�Ý��1���N�o�8%p:����3Y#�J��*�e2wұ�D�gG�����6�:]x?����x|2�hF ?�m�O�rB2R='�r � ԃ�a��B�AX��m�'D&oв�>�g�=�� \N(CI���2�ȗ�1�(Nd�f&�r��g{;�(j I֖y��0mq�F�G�s^�&;�� �\DmJ��GBYX���蟂�Y@q�3��:�-;��P�\܊7�u[t�To6�B����m�fy���WM�C�M�f�ܚ���:����t���Y��X�tb@�vY&��2K�&͗/�!'E<���0��m2[i'5����4,f�D���s�h��$���y{�O���K�*���{wd~.�r�������,\���Cn�)�nǧLJJ�N#�>/��Z��2@`TY�䉳-S�� �tk�P �%�ޛl��'n�� ��1?�& lw�R���U�����LvN��/�jD�%��؋M&-����О0����v�w��2yM��F蹰xˡ��e��3�YT]�Y�R5��L����6��ߖ�gC��q��l�t�h�J��]*�D��j�+�=�h�&M:�=�V��̥�SN}�e%Y�s�O��SE?e NTu]uO4�]/�JO��V�O<��S ��BI��z���p倫�j /}��1��b�bS����X���v�#��F��I��W���źMe�Θ𑐇����ˆ�W�zhBI��(���){P0c�- �m����]�uf�5?���ع�^;��"�گd�=�gw��jX_���x����n�6�[:�֜SRr҅m���k^g�N��u���!Y�Hܲy4�j��ˇ�?�����P�q#��cl��`ފ@�#W�(�k�@��<^h~�ho6>�`�*a(�oS�=0��r�Z�s��C�v���Hɚ �B,�����q�1�͇͝�֦�Ս:4#�ķY"��H�����t� �|e�����vݤ]1%[86��d@SNF��2��0�md��@:���D� �m�3:�x�UX\R7PU gC�S��3Xv%j�n�����E>���%fVz��g���������7"�S,�=P�u��c�s���}����0I��N�ZS��u�y��(�|���mϮ�����oG�R�ľ��Qn�wz����+Q�l���2%56�6�� 疗�Z�Ǐu䳓TR9�6�V��V%z �p�f!�����W�\���Fv�y����K�l��x:Ti��B��U��n��M��}FC�6��z2UOjeQQv���"�ʮv�4�s 6��Sզ�O-*��M3�l-Ų�9T�#r��3v�����md� |���a��h��Ԭe�(�n���ϸ�r��e�+��M����\x��qӲ���a!�2ێ!Y�U�iS�m�_���vC��62�}��zr� �]^- ��{��BZ���� +^4�+d��E�-U�\���}�esk�Ж��dآ���B).����c7X�>� �`����&�^f�)* �_�BH��0A��^�b�L/�� �NJ�M� ���^I�,��4�0�$U��ܹ��y�=V����=N��(?�dr zt�d05wKa�&Ajq0�vӆ�X�"�坥��p���a�tm{�\�K�Kg�r$a zAH�ʁ�ns�¯����z����y��/�r�=��f��rUEA�p�?�8k%pƠf�f &� ��~P��[PU͒ ��ɢ����J\]�l��3�=��H�4�j-����o}� \9���i�1��oF��6�{T��1?��Ĺ^I|n�qN�z���ot��֛��IX��j�,?LB�y�A ����y]�"�t/b�M�h �uBmP��i�Z�CIQ�Y}���e�3�D���$��]\ �1=��ܲ��׃σbj�Z�lF�0��<��.f��߯i�] 9�sAXXF?�����h��02��1sv&�X����ݫ$.�U��ܣ}P%�]�5��'�2�k��f�.���QYyF��k(�����ekO��&����h|<R�Aj�%"9&'�*vꎱ~ݵ]C_�m�uf���k�+B�"]��.&}��W)3���`ߊ�oc?�5e����}+�Xo��E\� �!�},�>S��>]������٣�t�i)L�Y��*�0�v�?�8�$ ��������g��Z�t{�L�J&F�Ҫ��tQ����;)�a�x��ܜP��گSH~� �&�� I�r݇w��.M��� ���x)�Ms�־��ӷ�a���]H� '� w�r��p�f���Rv�N�ǐ��%��~$� �N�Z�T䡉Ζ��1�� ����2@��?�4�W���w\U�����d�=�X ��H"Q�Μ4왰o[�ec��s�;��hJ� ��5!]����l.|'� ���6�W��^B�f���2���TxN�k��y�{tn������J����m@�HQ�(� IB�#s^ߕ��$��R�����{���� Q�?l�µ�'I�Y ��6��[?>�_:�c��E~�!��X!Y���j��f�Kp�M�M��/�2���^$�d =����V<2��k^g�N�`ޑq:�G#?.���5k��L�6�1�ZՏ��Z�1NJ#.��6:���"�@a8���?�2j�e4�8�θ�&G,)�ý�a��e��4� ��v{�=0��`��s�=�nY�r�v���/*��/.+��������ϳ����_@6[�'��M�5*#��*��a� �4���9�.snRKb�)Q k5���Pw�Ʈ���熃;6,�^n_���g 㜏�b�s�>�Z_��XzYQ�1-��<���Љ�{*c���}!� HI#ߤ��=@�CO�͉_s��46<|/Ձ[A�M\p����7n�͒����[�7��&�A?�DD5.md�~�L��sk�z%�!��[��_�M|}]�6�e`�+"�@�W���"�e,�nn��}d&2�Y.���>�G�{n�aji�� ����%���$|���C;�f���*�r>a1vƒ?�88-�w],W�M�u���-��f���8�0���a=���R�W�� &��m�W�B QS��wK[b�[d�#(�4�؟�Y��v�3%�c�m �'�GJ�l�wY?B"Pp����|�t٭��ڞ͖�\y����ή�W��Ӱ_�Pa�A3c�P�5:��ǒ3a�!/ (8>�a_�֓��ց��FB�L�����&]�lra��%4"�J�X�%�[oCGi�Ñ����0�@B�xo2y��ݽ�4>�ͧB�~���2ւʒ�@< ��<`����y�>�mA��m��_Z��U��®v�2s�闣C_]ߙ��A�;�������ǐ��X. 7�)�w��W�T�"�6c_7��W��ŖL 8v��[+�Du�����l�}p�wN_5�X�-�<�ӴNW��3n�IϖU�V��ā� 0���ѹs�g���~�-RD�6 �t�m����������S���J�V�8�xǕ��`��uH8,3�"�&LmhN( `i|!�-�M�2��h4�fҀ�{��hI[�jS��m퉚W{7��|~WC��M(PY�wy ����=p1B��Aq#��~����$�0���ә��WVv|��U�n}�G̔q_�̮��m_�?�����[�UJ� 넨>{U�:��WX�~� ���G8���%�X�w�׃�k��3�7���c��-p9�3��_�lq�Y@�;�S�Fۗa��⏥q�nHMi��B����p���v�mh�̯ ����9c�*,ɟ#(���H]u�U��ԏ��, 9��-w��� �Ua#����� ��T.�߰�/^���d����>S��A�ѤȊ�E�@�S�����>�Ľ��T��u~E]�g�IP��,����s?�r����l���� ���Us�G�]2j� g�@���� C�����' $�����!�浘W^s�O�N��NU�[c�*5M�ʐ�K;""ٟ����;Ԋ�G�k�����%ԴU}T���&���03٥K�A��2P4V�&��!�B�!��Y9�����>�Z�K��ݸ��M����m:�P� [��/}�����C��\���b��y�諃����F� �4.�h�����y�An8�8�����ņ�L87���DF�j#�� ��n��ƼV}+�B�5�ʨ� 8����p|n�SM�M&L<��9+�뻤���$�5A����+���L :u�,�����ͦ߆{B����O��Kw�r�P�����!�����wX_��{Vv֊�ز;���1;���{���٠�;�խ;�0X�Ŋu�N*�lg��4�� �l��T�tq@�vv�t48�r~�웰�� �_0��eE��?�e� �6B��ܰ��X��Q�s_���~cWUD��f��*Ş�Um ��.�ms*�?$�Foeu�n��i���[<��ʃ��߰���}�������K��;�|���*$ދ����9�y=p����n�ʖw��a�S�aL����xfp{���tNQe�@_w�8�iP~(�JXj�m�*��Mee f���Yr�MY�`��}v�C�v��(�"�j��Ux� c�H��:[⥾���<�.lr����+�P�v�E��������-�K����̴�B!M�%�5�%<�;��7�����FƳߦ\����}4eť��rա�Z}�̇;Vb]I�l��f�\U����^l���EP�N{F�t�(�`�~=�+�X��=�D�m^��o5la�d��"�}�8~�ɦ����/w��8&�n�)�i�����c/Xe�Yx��#�@�����ц*���%��R)��R����ުY,nv,�#�Q�a���hl��������Y3�fT��j�*aB�?h��C����w�v�<��Pu�}���e�p��l�3���7���܂�����'���[�"�$�K�:���kY�ٛ��@v'��{�nn�d�y9� -��/�H?�%�.G�_}ܕ��gdAe2�r ��n���1]c�9�N���r�ZY*Z���%K�e��|[� Ȕ� ��|�Le�5O^���i�7A����f��1�-���T�FP��dW}G4���m���������M���+[ -�ȹ�ek�Hr�p�2�9�J��;�(Lv���B1x{�����1��"+{��:�?7�l����que���M2��''1��W�qV4k��*���--/������@"�o�n��� ���Z�M�\���Oå�_��w�D6�)�">��%C ~�XɝX82S��[���%���������@OgV-�T6�gơ�����Ȯ7��z�oad\c_�w�ޓ����T.a/����d�[<�ԏ��S5dxJ�D>�q]�{��z�@q�NVْ����=�'r�#�k7-#����ge��H��7/��O��Ow�_6�f����T�[����ȿ�K���)�|_<��#.D~x�ޔlP��m$�[xX���_��vg�.���Ϙ�VV�����|�m��gu=W +���e�"LJ�j$��c�ȼ�?���x�(������Y���x;�U�&S�m��-�榦�ϯ���������80���� �������n�Q�DA🏀�5��.(���<�s}��|�s�w�il�([dI�0�5Z����֎�J�!(�c{&�x���]�*+�������f�p���:��Ae�fz��r4��k:Nt�V�أcA*�3���C��*�'�����w~a�f��3������}�G�0_�L1��3�� ����= �Ǘ.՜65-�����j�J�F�$T��ײ�����};c���b�v�C �y�*u�{��x=�b+��q�H�z�����~��u�WrbŐh�{.�����5V�5��`��(��0�/��G�?bi�;t䉣�����D�O�1Y���G�]&��q��@��:�WSفO��1�o�?y �}8e�W��b��r9��U� "y�%�U5�/�{�ٲ:5�z��� �dYYL�o 7�'������Ɩ���m7×�<hmJ�����5!���fE��ej�1z=TS �˾�z��'�����I�R#J� �bp�E�B�/�Q0D�?���7l�A;/J�a{L@/�%[`ݠ�l�q���R��D�vzS�B��Ss�S,U��&)5������$���g55�K��|��;��4 ��D��[�/,�:)��p�Ƌ�Lq��#ֶ�ԡh��ָ�E~�O�쾔6P�Y���[���j�8!46��Q���&��e�}~�H@��ђ���=�7}z#_�b�h��P��ukI@��}�Q���=XXÈ<�j]C�'�ѶtuX�ܜ�Z��@��=}{Moq�=�}�Ů�2<���������Qd� ��-$G*ˏ,�j���â�BG{O\�հ�H���8�3hh�e}�ps\��o� ��fT�ĸlow�\�}��Q�H� ����8�=�;�g�v�Y�z�<ը:UW� �|�X�N�~ol��y ��K�km�5�~ݴ[�n6� &�����Ά�d���Ir���YD�̌��*�-��ԁM�rFV[�}�J��#c_��� �;V�>���-�Qp@��g��hH�e��in#Xl�V�ݫ^;}�{ ˺Q��AbnӴ�x�9��r�@#v0b���M��!c�@oP���oyG�"ؼX�W-V.�S*%+�1��$�.��A/SN�-삽=�<�i���T2WT[���������BT�hB��$`��,�K�;�L&�҂qA��͓]�����d��g�Z &/Q�|��a���߄$!�2�E��w���p���j��F�;��º�λ�"f�����"Q�� �A�Z9�Sx�%��)|��QKY��C,��r��i��h07Tf����G�F'O�u�����}�t����Q�K��� ��m���}V���p5@�?��)��5�[��{z0�ޒ��b�$��k�/�d��P�HT��q� !W @�� ��Ҵ̙�,�\����`��y� �yK�o�xg$p�,��pZ�r>+�t]��$ndz9��XEo6|�*�nj]¸<.� � � ������(Ԍ��e`9�m�K����O؎��1Ԉ�ԗn���1T�'�o���+��&��N�}���@��� �HYn1-��D�Ǧ��\:��)#P)�,f՚�vm��y0���X*�Ax@/E�GR7�'�S��"D15L\C�T���e���% ��D�0^�PD�p�0��ҹ�,������sI�X���9M�� h��p��x��UBR���(2�^�-��i�Lcw�D�Mgnp��Վ���g'������R=��y�"�E5[2��� �[�%���Y\�+-�*�DS�2�ƃi��à*��L�O��.�HFMf�Y�6����i�e^��*�����ܱ#�f�~�y瀺b�o4���k}�Er���ë�^/_�bX�:uC��T�|�Wy����K!���n)�9)�#�y=�tҌ '�\v��y �L�X��p���Y� ���q$�9rٻ������]3�@G%p�,ߑW�N�,�����C8~��m��n���fC�%�Ml3 ���?lA�itḠ����U��{z~�5N�A�_y[m��-�+��_�/���'�O%`,Gc������2�D�=�tE��X��|B�=�W��ƛ%x����^7�H���hU!�:)�v$x4�XA����.Ӛ �m#�b�")��d*��P����j���~X�Xc�5ruaS�ѭ����(:Ӷ�W<̎����s���1&�H_G��J���ӭ�џ����}ߜғ�\��1�]TTD����7�/!��,jJ�H"Q[F}9f�J#�B"�( \��غ�����z$���Q6�sU�ɋ�$1�^m��B�et�������>��A��5�87�L�wc�X4�ȗ�8W�j��ȗ Z���=��6oz:W���}��C(�ȏ����S'�G�X�<,r���2�� �y�s��%~�����pa�\6���Į���\���I��q{s��lw~,�J�+_���Ѹ��r�|���楶��j�ݰ���-_�\����? I��*�s�R3���a���˝��v�wQ� Y���j��233�?kY?t��M�F�q�����f�5���WȆd���`hɊ����:eVCc�/�ó��U�+�Sɡ�S���3;�%�J+�jgIOd��J�g��T禕È���3�?4uU��Iܵ=���9tFY0������9V�I�� "�����l�?J�Eo7�w���䱥����{��o"�`Dh��Ik�Jڕ !qCQ,5�Kq�Y��� ��}S<�Xv]��`(#���^��' �jܺ��z�I�v�ñ@�ž�{~<Y�i�gM�gH�ڎ��%y7U����o�!9v�zǼ2��K�mqi�|��<�nR`[�ɨ��j%�au��g{�>Gc��J�+_gU�o�y�Q��R���L(�;F,���I���rO�@�P�:`�k��K�T[�rq�H�Gt�eVr�e��d�{��8���t"ӭVd�rm�:)/��]��"�7-E`h��N�'ոfaTq�/��V@�`���(��߅hN�<ﻏ�{W��QLGd@��2y��r*�u�X���ROHu��:��yta�������1�w֣�鿥�=I.���>g�iχU��V����H��5��P�����Ʈ�[=U�5o��8p�TZ����e�.�Tu����w���A �8�5-�-;,r��ŕEx��]�Fd�=;%pʕ��XU��-1�{=~n��Mk�I�"`3 _-ĶxM*�����e�$:[�3�2}J�<k�}R�>}|��՞�U0| a�D%��K)�˥r�`$hʽw��)����J�V;���`1V���[��q�O�.HZf�o�WM�?��yC�8��3&]�}�, �*��N�.JB����S��E��Kbߝ��R��+���T�]�m�n� Lrc�t��6B����ni:\�J�H*3'E�B�֏�-�mcϏ�D ��J� c���݂)/t�(�k@�^�A�E2��E�!줊�ך@`دזB�ME���^UK��l�8T]]=���M)ݔ��e�=���r�84>�I���:��`�u��yWH�:D�^3� ��a�M�l>x�Q��s��?�z|�aLftW�O\��i�CR�L�e3(��N 3d�rk���v�ĝ�Dc�4�*�Qa;��ͽ^��(U���o?�����*m�ug�c�:��NuIn�b� c\���i8b�`��������J�%N}2���/��g�νuߧ\�d1cZw �`��{�s�{���:VE9[�j?f�;F���9��u��M�M x!_[8k�1����p�U������ �J�F����(P�=V���/ e�cR��w?��7o^�^���bݝC(��?˯X�ć����H�� 0��A�wp��x��r�h����\�%y�l�CPE��t����T2�y7j�i99\�����"���9���/<�VN��ff �>[���;�w�+~_�`K�?=��u�]wUe��_�,�\��VT'�}sw�#�L�/�8�n�M�6�����6����iy>8nAJnɞT��?��Vڎ�_�͝C�aRPL���`,ӻ5�Jx�F����;�!�ԩx�EdLr��k e+!� a*ص� + �+E���@z�`%� F��_UH����W�x���Uc�|�۳���6՝�]]Z: ���Y� u�g�����q �b7�`W�o�. nfZ9�xVz�Pk�����ð9��� w��;���N�����<ȟ\PtbE���鬛�)W��_9�W�$z�S� ��� ����`'6�4�XL�yY.ǣ�e��mH^�la1��LSɠn��Қk��u��������C�QƦ>XD�+���CũnYS}��Ej��+�҈5�C�`����(@[��r}��Uh��[���'Ǫ�"��7kd 1��;�I>}��7R���� �Y �D?���3��pw=�,@ɖ�B05�{��w:����F���{�[�*�Av�w�yV��~�VQ�4�:!�9����3 �N�ј�蠖�(t���o�%� � ��t]wy����k�_��;g4 ���3C��;���&�<i��`���[|WI�*���' &P��dS�^��&���� ��|���~�-�˦𫜬>p[O�L��@?hr��3�_~���w�.L,�Fp�9l��8gf,uQq���9�&a"��(����,ܛm������-d�%~���mP&��m}}D�:�}j�^�0�$�nՊ�ֹe�r&z^@����A՟�)�{�xG�/8Pc�2�os(�(L�m�J۶P#�ޠq�Up4|S��Oi��g��>ؘH[r�C��J��2E�}�ږt ��+�q#������s�t�E��� ��AV�.#�����7Z�l��'�5�t\^��Ge'�mn8��D�` S�KP3�A�y��PЁ�m�.�5�zXѥ2���ںw���t�� �9zD��DQ��!��UȺ�<�s��s��ld+V0Ś��/^>k�1}5�E�"�uѫ�]��MX`J���.�!�s �����5�|?[�J� N���U�(J��_em���}Ͳ����q<�fA�X�-Q`��P��S���^����u'v�3��܊��ġ��A{*�3��%@뫉�e|�.����+Wa��F��O���9��>�2/����0\^^ ��Aj�"%i9���>���o��?\�ףV�I�/�o�T~��/�Q�;+ni�̶��^����5�⊽���Aj�1�����J�;J�!�Q�/UX������ݼ�� }[�/"��OZ���N�\�m�}x����헃�/7)�ҵEM��O���������u:5v؆R*0},���\Ȃ<�1"ٯ��97��� �/�2d��wМ�f��B��V�]QY?�A��O���O�S�l�gNiiH���[�$̌+nFt���"�v�z��C�8�3Sru�<f��+�|���i��F��M=����x�E��{���_,/�~2ꎹ�4}��\P�]u"�vj��9�)J�3�� � kt[�ʁ]Ka�;ry�]w�K�� �̞JM�#��KH���2��FJ�3�ǿ��@�������;t�<)��|���[w�-�&EqJ�-D�bD���K����:[����ې���S�`}�xC����V��:8�C9� a Lc�:�druF����2��=����3w�S}NE��l�dbU#�@� �6� � + �(�W�5��w�P�U�3�%:B���U��=N����Nv��s��>�7!�v�q����-hSu�UA2�P}e��^��N��{ׄ�!��Yne3ɗꊎ�߸���غt'"�%�nI:�7YD����RL�ﺬ<d�����5Ѽ��_�+EK^h͔��y��I�;9��2h����`O ��Y.{����%%�;�v�5���PRr�����:����G�@�i��˄d���y��˜RAY;����C�=r�I'���?�v��z]����j纾+��@�V]��ۻ��s��A��}MT���S�uy����59�`c�= �)i}��}�/�����2T3��X"��OW�.��g�u�wZ�5��e�o/ �vB"s����ܥ�|.h��TT[���u��UF�=26�R�O�j�le��:y�`�A �{(s��[��4<�P^�=�3�U哸K9�ʃ��0��ψ���9?���~2��$P ֺeb/ȥ��`���������ͤ�� $�/ �9��W��w|�Nj\�H�A��� ��W5�?{��9���t �*)1 %k�{Ф6�u��\�0�z���;rؗ"�/d՟g�o~��Q���ױfE*�^<�- @ IDAT�UD�(¾���: gʕ����p�$��i���.����G�7#���W�Dh.�4;��C��y�[3Qi�l��4w�e��y��� ��fk�nPZ�hJd~l��-y�q���0'ɨ͒$��&�2�6Gk��[ R��5�|?�J�m+_�9�/yy����]���x��M��.�wp����l� �V��(�#i����M��p���ȐDo�O'��7JU$%���B�����<���k��e��,�D ���� z����1�i=8BR�]��WR'+r�X��6N�����_��N�±1T� T�Hݬ0�e��/�![%xF�����O�s�얢i������\j�us�f��C� D�t�2�GY%W�_��4^WP���#���N|�+�x��璩���o#�hJ��SA�T.zq |t��tK�(6��N]���8$2å'��+��)�.�Q���0W���A٠�d����u���cV�Տ>��0{�F��W��{zeg'�I�6 8�J����E��z�Mv���Ͷ��X��#/W����r��J��w�T��$By1�!�?���O�V�|aY����O��o_|�8b�s� ���&�mM��œ+{�"9p4g��J����_m�0�L}'�-��a��PV���Ξ�:� ��� -�>�tWW�>���\��H��`X�V@��ͱ���{�n9�-���75�]D,�ow �(�S����8�SY�(�r��w\�ٽqCZUע�̴2)p��u�(�$N&;�X�-:��;v�y��-?�� ��--=H5復��ɖ��<�y��Uy?��(��iOE�떡����"�[Q�n�zxq�w��t0���*^g� Fȃ���\��zv���>� (ή��ɀ�����U?�xJ й҈�dH�_���(�����q ��TdS܆��y7�>�G���j}MɄ,�̨��{� ��K?��R\���9�; ��V�(��eU��q�O4+�mV4��{wٲ��v$QA�We$0m�Rw�t�I�a���t�-�c�X���F���̈́�&W!0p��4�&����S@�c��ni<4��-.�~\��Q��9o!�� �C)u,��}2��rG�mʺ�~�M�I�v �5\PSӇ6���5�q��KҊގ��u6J��t�ud��%Խ�ԋ�l#�r��hR_[��д�d4�>_P�o�x<�3�E�bW�)��G��6���J!+>���B����@ˡ�� ��k�"���L㟠�����D�m�J7�D�6�-QX�X�L��@�#~=��.C-��3a��"�A�Ӳ�o�)�\1�O��)@�"� i�� GiRl�������@G��Z���+6o��?�SF#� ���LV~�g�9C�Jfː�E�&��.�����$��u�Қp���`4���HŖ�KZ��I+f�d���;�ѩ�L�%qi8��Փ���[�a�@G�?�K�5�e�� �@�iɮ�hIIf�����K�2{U��{m�Y��x3I�.08��=��;��wPD�QA�����S��^I!��v���v��j$p�v��m���u��E���$ �0��?Q&rw�r�)��X7{C/����Rf"㍋��%i���kQ4�_d��ML ��Ԁx?P(����(_gE>����k��w�oi��>o�d,E�b$�?6(�%iK�����z|"�d1Yx�H�!fٿ�cs�%Uݘ��N˱��3Ο/ٶ"�&���C(5�����3s8 JN�N^��^�DS@c��"EӑD�Q�>v@ ���i$��N�#H �?���tS�(�rYV�K~��d(�_d��Y-��7��G����^�{�"+M�LW�Ύ�y4��ȩ���b �m>_�������ݹ���s.�Z��pbG��7��Ea2���:����w�P�9���īQ�}��ގ�B�Uу$�)e��h;Dі)�B���x:������)~��檫�ͬKND}r�g�j;�j��hI$�����3�w��&{���~�.ڲ(�>�m�d.�,8b���9e���am�:��A�;vn�F:�t��b'�f���C���|n%����]h����>��N�@J�Nd�����+�߳5���O��r�����ۓ%O>�=����"㴱�ٕ H��`:����3��;���S'����=:�!�с]��a����d�v���u\���x{�V�2�{�^6��a�P�X;�os�|9f�!�I�(]� r��H�}} ��O��-����]��gIp�H�Vy�x��UŇ--�Q!�����#\���Ie��Ώ9w�-ǖH6���jJd;�]��<x<Tk�3��iVrݲ��ܮ�����o�H��������F��� �l�*�����Je;�Z]]Y1��X�VB�_��=�G�Ҝ�o;�q ���p�߸�kf�;�Cc����m�|� jG,]2� �����Z��_)��`^�]Dl឴m1���!�ъ��"il���������v@��hb��݉����?�����kk*�Խ�����եm�����yư�v��tOmG)y&�QpεN����c���϶Ù�ڽ}�P�� ���=}�8�H,�vd� �0&�O.<x/��ۦ�/ atyHV~����(,�W�ؾ�ҥK�sGo��p���N��ѿ�|�=58f�{�MR��r�jf-Ӛ2��n��{���9 !��2�RY�Rv�[�Yz�^����N���&�W%_*P��wtk�vǙ���%E((ӹ�}�2T��P8��&r����������k�=���7�BS� bA,AЊSk��=�jm���j�}������u��m}���Z���*���2�A�B27�ә��R�I߃��S�ͽ眻�����Z��I���P� �̄4D%��]�vn��Ңڍ.sia�O��~�s!��G��S1)��x����l'�ݪZ�>���ayR�Y9sɦ�x�m����{W���О7(��c��J`D�oP����� �y��sgG�Pk1�.g�V�z�R9`������ WI]��M����h�5ԟ��>䮫N��*�s�Mc��w� hF�`�=��N�HU�_�n[,��e˧�������Yr��=�K^EE��k?詀H���_ޟ��YzR�T�Rij~ �,�ϯ��x��A���ݺq�S��x�=�?��)��㰚��܌Q�`6Ix6�Z3�{fMEA���V�k�?1�[�Zm?�~槩��3N�۰t���%Ce�{����-�³����&�q"���&�`>�A�mM��/�,�m<��F�zr�:^�b��rV_��-���$p�wkL���~8��SV��|��N�O�������O�ԇY�b1ZPij���&]�`´/7-ҟN[�v%�%��9�����Oj[�P���]���A�u�4wÜ��bD�����m�p߶�H��;g���4ܻ�<r��]��IV4,M��$`}��]���[�|�a;����,��kr�����}#< ��G�x�}������xՀ�� e#��u�`w��k��� ��2��U&�3jko�����kD��=��v��U����8�}��?�� VY6"�D�b} no���Cy���S����������%��W2�U����Y�0v�L$��Tלk�LL�ݝ��g�j�wԟ�����m�Hv ��5�����6����s8�M�yA��>���x��ꕘ�D��<p#y�:T,ȴ��lO����>�1�9Ty'�\�d���uJ���Q!��2>}��E7��d-[�i�/;v[[<��@��=v�[�������fޑ��f��X'�`Ɣ�dӸ�4$����eg�bQ�֢`�X��ν�P'i��t����_�B}��o��Z]^�$&8��)�����k��P 1��u����7�ܞ�f�Zi[�^�����9Ub�'�ý_�={���{����=p���!���}�^�> ���+�0����V9vw�H��)�u�e"Vjە>b�X#[N_1ȣ��K=H=�[#�mmqlk=��a����������Ug���� ������ˉ���$�& �$���G���a����?(�.�X�y�%�� E�� �| Rlofԋsf���0,)��m�]Es���.uMųl�(��qU��4T��CK�m��W s���a%�v�9�9���[�I�q�=��E����P<I��k�[n����%�晝�~`��I}l�x~����q�L�f�jP _��3x���������0 �,Af�P�GQYZ��G�LуBA����UP�wx�vˌ�����67(���g�r����>�fO�O�s�"���%�ҀR!O�Ƥ �$��. ]B`��8�=��L+�E��LEU�<� �K��T�H���3 �(j�i7��b�l�-�)ߤp��M p5�Ö �sZ�8�J��O���%S�9��S����z��{�N���pT�s�K$=����}) 7 ����i9���c�f�f�Y&���p{���˄ˈ���|a��gm�R���O4�I$��-������h[^Ue�J�Nvs�g/��>T!����@�Hd��Hp&odz�1�����O�����wt\`S#��]�a�um���_G��@�'*R<���y�m�r��۔�)�)�A9�ʟx5co��I�n�O��P(��}�g�� �u�K/~�P ��^,D,��b���5�˳��� �?/�-�+��e~�}?UT�T����V���V8��aMa�AU�|�B��B ;�Ar�Ϡ�D������/���5�|j�UW�9h��iLG�����|��MV���=y��LO��e�n��B_PJ�>�>g��#����M��Ld��p�qo))����=��n�a�E㊄��%��g�@WB<�H��R�j�x�uqI�- �9<n�R��cz"%'�-�}�=�/���P[ �R��Cʡ&�*��G&�������>���g�*g��Xo<�b~N89s����$H([�>Ԝ�Z����sk������줺���.���Ǚ`��/5���z�K��Hw�z�$a`���\�����K?���wc�_���)_�tW<*\�����ز�G��}~�=��p�g�mI���*B5ww�,��.�8 L�ZIq��t#!�W`"���b����'cg�Z AM?�T:>��[D�$+4��2"�8�Z�El��^p��gu���LJ:ս+�%J�{?�O�Vz������oϠ���OB�!��_j��G���\���I��wf�V>�����U�V��{��Q��%b�&'�,�>�v�|p!Y�YT?��'�UI��{�,�tǹ g:<5��H����%��o|>bn�GsY1J��Y�p���l��j�m6�AϕY�P� ����b�����V�:��p�G��w^z?�A��k�?y�1'���T8=C,ꄲ4(M�� %r�H� �Sk���L�~�e���U��&c�ex �|�?É��"+S̖�3@ީM��� ���o�K.���Uԣ3x�r��c��¨��AHڿL.k=B�6�Gs7�%��\�^���F�'?� |Ѷ��{m{�P��M���-[2)�b�������v����Sn{�0'f1�)��$l�U���Wh���&g�q�-tUV�������ՊPvCXQf��m�K��� Ԗo D +�*��0�`bŪaQ�Sn�� �( 8����Wi�� ��,��ߩ5�y�f��p�������Ʊ#}]]̰m02��5�Ev騳\��OV�y$�����ӓ�Ż)ڻ���U���r�/2�خ{Q��8_WQ��d��g[����u���sTB/��w?��{��,��^�)����}��c~�=�'�avs|���w�N�S|7b���6�ƊTg���{-��V����H��H?��8�UU�m�W�D���a���*K�Fi������}���w�.�̙2@��֙��G�BFU����{R�?ޏ�u���wyR�),����,���^P�z*������k����W3��8����P�璊�s�@��ܬor���Y�|n6��6� �+�c�6��[�5!���κ��U^o[他����x��:Rz��W {�JɃ)B��������t��>T�8aYKI�[\-g� ��<b�f���'�k*� ���dI��][�d�������֬2�:�<8lc�Q� ����u��$i?>��du��ޔEv$���͐IFq�/�f�gN �'C[7�� ��CL�P��d��z���Q��l�RI�����"y�#���ǐi�$T�q�v��3"��4��5Cl����^%��K �M���d�C{�/X� ��$d�6���@�'+7�gƙdJJGA�֑�%k�ƞ)q��*{��6k%.N����҂��@ejK���"�s����[�m|��mÑ�n7���F��oڙ�\�r<�`R/ػl\Q�۟��<�c�k�W�;�3��º�Ϲ8�q��H�l�3P�x����M��ƌ�~��q��6���e�Mr}Y��-�{_O��+���O�S����=�O����>M�����͛c���&���_���P�^���@-bh�v���l�6"A��a|����܂���'儲��tm~���wd��]��7o�H���ɾ��A��<ts .�>;�H�6f�d���vȜ4��$1��l��:�>�x��l1�{ܯ�~��L�h��U��D:䂖 ��p�K���~h_��8��#����E�H}5��A�X�`\���얋f$}J�;cg�F 8���2~%&k1�R��:����5�� O�N�H�kD�l�+���(NZ�|� \�vM�1]nV�U�e��ݞX4x�h�] jթ4�I�����~;�Fѿ�[A�e�U9�bs��=�~��B�xΣ�%�K3Ͻ�C�;>v8��y��x�Z������e��듼W��Ck_��M���sI]y&� 2��s=v��F�� ~���2��im4�=�q�"�Jw-!�i��j�-�N����CPȊ&�|V���%ZY�$5��k���ST䵴����J���߱Wc_� ��W�]�2���V�#e��PZ����3_V���e��ns+�(�Z*��]N>@-V;-߷�"�W9�� ���?h��8���p�n�8uX�Q#<td}WW0�&v��ڼ���vtuM�=�~��<�t�`���X��v3�^�S.A!��SIY۹e� �wP&wշd:\��hj��P">�#-�� ���h��R�\)��~�wcϘ�4(������+nm�C ˴P�VTT�9�j�2�B�SR������gv�t�n(h��y\��3Hr�bu#9�P���d횜���'������+c��F |k��f#��:{e7gwII�Q4ϧ��]z+�ߒ$~}Q~@d��3 I2� ���:�� Ba%%Q���B���ۭ����bN@��~��| w�Ͽ�sP�π2��32�8_�-L �tI4�RI:�5Jk~ii��\2��.��rb���}������T(Y�*?a!�<i�? ��5� ��G�]:�Q#�?Z�L�*؋mK#+&4�Ί��(�^��υ\*���x���EJWo��B���k_y��NO��x��5�pYqC���q6I ��J��L6�i:"���Ҝ� eZ+��v���'���\�!.�y9��6����X��)��[�n���/,��uu;)�vn ���w :*�P��ou���\j�ߔ �8�(Y�3A0(�__�ŏa����uaMڛ ��iܪ�N1��M2"��]Ex��?{�]��GT�"�Q��K�y�r%����n�p�f��y��} �]&�y�� H�ط�uD� �`��4! �|��Cvv���� � ��)׃������G����Bm�f�e��n9�3�07��� �uB�M��m))���&�{��tvʐvp���<� �'��>UQ���Κ�. v ����v5uw���g���&��&��x��VŲ6���'$B��Y�@�B���hk�������p/���w$���jO⾙��D;�YTY�R���C� ��#y<�Fw������mΝ<�=��i�.�s�X`Lf�B2y�)�L��F��d<-COE�w�UC� ��������B��d{��uzK�;�*�0�f�z�{�8j��a�%����։�G���9���xڡ��I��T��6�í� ��9��9���B����q�`u8,�˟ǫ��*���x�����Z�( ,��w�粇�|�L�NU|�y��3�<�%�����ᖴ�j,x��:��*�:v�q�H�%�UI��ӥ3��ek�t]��%_��ƳB���#�|�*�"ٵȫ����[��yײ��Gʰtnsew( wnn6P�OX&��E��&S�����3Tx��2��n0@����� �S�eDG�ׇu}<��F�IdS⤉G-8lX�0м�����if/�0P�z���Zmf.���� e+.����Ӄ���G�>q��K��A6���6���;�2�˱ۣ��>��:�_ �8�,ź�w�+�|��������5.)Z�d���B��D��8.�1�h�8��s|_�.ox��z#jD�b �TT>�Lk�L$�U:�=ꓞ��};�ɖ�I��x�}��fX�T������x��L�lV9��U��{�l �,��a�N�x&�9�݆ �>ۥI��!�L�X��Щ>L�\|?��*˥9�$�u>�Mu���}b�*�6�1��L�q�>0����X��9_�U�_ OoKQd�6��y�3�����/m����B8 oٴw/�]��XQ��d��1ϱ�~-�{�X�] �����F��0��E�9 ��p��F���%s�ON V?��ۧx�g��"#�3�+P6����+f�"s�5Y斜�N�d���w��Db[��-c�2��T6�ݵ�+(�x۲��) �t�����ufJ@�"���v��3g/�K�Q���-� ���KTV� �f�]��؟�C������?v��]�*6�o�3����\�Pz�+V��ü0�5�;3<�_��]�>rC��p9��vL�.p�qM_����ߢ�.��M�=/��O����E��M}z9� }=��>�re<��mmiq�t"篼��i�����ՠ�9��S�|�VN�L�fQCmD�% �TW� ��_q��Wښ���E�g�-a� v}d�myI)Ǖ3gڪ�_W��%&5����mu�ػ�(����C�ޟ����Ϗ�)��='?�szn�ՙ�m�#��Fˁ���ؓ��������'����v�2�E�v�nj�Z���w���(l:HC(��\�i��H��h���������|�����_���G� ���y�NT�@r�*�E�Q���W?�]S�~OINK2-����י9p� �����6��Cu��F�����#{dmow�6��%�mo�c������֬Y���:NU��]�Xt}�D�"%��SĀ�M1���]��W8))��<�ɞWT�$ �@k�U8�b%_\`(L6]˚�:�)�q�*\�����5Dgwo�z��h�hAi��8<Xqnuu�Y��硸��.�ZR˺�* ��@¥� _εE�˜��2��dʹn�E1Φ�{��Uq��I��G��ee��v�:^�H���p��+JoN�x���Be�� E��9U�Ur+b7��u�s�2��Vד!F��rYq�\Uv�[�wY�� � iF����=�YH���x�Q9�|g�����kH�*����*s���Z��\x�|�ӳV�� ��41u�*�Tj�Eڹ�u&��-X��Gn+�5�x�@�j���Ǒ��M���.^*�����{?z%0��8ݖ��E W�����5��O}�� e .��R�D�^O �b�J�ʛ��h�����e��nw]�����i��#�������;-��p8�Ԋ&��= ��:E���M!_�Sx���PU�/���{A۞ԝ�J���O�VR��{-\����e��E%5�ij?g��Ϋ �����p��a�l(q�kIm��.���=�� ����hwVeVfr�m�U"\���[�~}fp���$��!�ti��i�FӦs����<�L㎭-JÇj��5u�K����,��(?�j��z��o��Yu����C��-�.���Wt˛M�" ���)�����w��r���;oѥ����}#�����!bv�'��ʊt�6��ǢYͽ������eT�ب����?�rR<�K�(�ZP�`/E�y�8),X�9%3��?�h�9X9�����Yˣ��MH���Ev!����Ogh)�d8}?���/���'��Ċ E��( ��ߘZ29 ��$�HA*;��ܘ�Z<����Z�Ķ��qV��C����T��+ư��{�3;ե�++*2�=���1��:�D��(ω��N�(2@������cp2���Ҥ�rS��:��剟��m��>@g{�Ҫ�T����^O�SnX.�L2���4���I��-M�Y���g��>�|��[o�</�ى5�,�f���f�6�^7E:��y�ڦ�`<��g���@��G���"���O}*�����5_rL�6�:un�]Eb��3fS�K���,,bO֦�)c�|u���W��;�O�/��<e�� Z�ci\~��~�-��϶L�SV� R�l�őh��u�#���� أ��N[����[����P�+���j���[�i�- 6��<�94a<.��}������{; %@Q�y|)** �s��?zn"3�Dwlx!=�N�%��^���)�-MX����X��'s@�Qˢa�Tӝ e_-��5�����]Sdg� �f檸n4WM��4u��T�4�y�{�?g�o�=�)�� �� ����Z#J� ūM�{!���)��|�LX���^��_rA��:��dS�Y.3��$�ƭD�x��/�P-��7=��<�y�iLNY�Ƽ��PfY�YƂj\jj>3OX��� >\�Xi�ж�%� �W�D�7`���&��� ?.X1���t�~y��A)K-.���o͑TT�F�fF�mif��:+�}���?��G�&�]���>ᗤ��~�1��6�46fv\ �T���&^ݽ��M�iﲋ4�ʊ�6͆����2��h�����rl﹟l[= �(=�@GG $eiH>~?�nS�t!1��T�>x𫇪�\�H5��K�x6����~mڂ�DJsb����뻰@:��~)���_�z�r^�)�T''YHD��\ޕ.�4�Ό6��q8�΅i�IEq��Y:n�v��g�)����hI�cJ)H��9 ���PG}O��+�' �;(�iӦi���\?xS�(Bԧ��{�������0���V�~��|(qB��A�ä�1���iߓY�� δ� Ja�t<8r地n#�C����?W���oa~$v��XY�Gv�|�^���Β��emS�'hW|�3QY��9���C��(=D?�����yL$f�LXt�l���Ėb&e�eF���mC�R�gjp�L�Ʒ*\R�j�3����� ��H����{��w��X܀e��:Nri2�����[y[� ࢣT ��B��TX�u��;oZii&�"` �O���n��2��sq������ҥp�- {6����>^������Y�k��1[��Sw�e<x����+���f� s��3�R��=v�n����}�˗/7J��g�:N�� )�98"���ғ3V�gZ�_h��B��Y �����$��CP!cU��h|\���sժ!+���)�sی��eЍ�u��!��@���R2�{�dX��φ��Җ� -/k '��� B _��|h��]�����l��.�Tx�mx��}��ܑ�V�V�R��$�Kf�qn(gC��4e$��/�����|j��h�SV��֬������mmT��TG���qW&L������/�.�m�'�dH %���=B,뷊fo�]'��)a53!u&�&�n� T�h��X�e4H}��� t�t�0I�� �?�`N�!٤�mY�*�))����U2Q�5Q�U����6�X�[����@�O"��<�!�Sy�y�fɳ�sT�H��գ^���&�k��� t����Z=�G�,��t��m�洑~tᄂ����Aƣ��!eoBaF�n8�S�U�6���;����g�����`K&��S 6kD���|� ��c%X�P�I������$vx�;� �����R2wy�q����y�r�y�=M��{���UNu�r�ӎnS��-�ql]&ٜ�T�z� 2!N�֔A�*E6�\í�d�ԸyG��2df� �T�Λ����`��^�J�իu�7߁I�0��Y����M!���`�` VXG}�t.6Iy�#�v� �X�%��1فŅ� l�����X`�,�צ\�3eO��\q����6��B~�)>)|�U�=�tM�:�<ҧjc� nʡ�PxCϤ��9�F��)q���K��| �?Wng���(^�vT{�,0�bL�Y��e�EM]��˞ӻ�����MSU�����C���4s^>�t"�>��r,�����ǎ�1lJF�1�c=�nyp��j� �������W�Dϲ��R/��q�Or��Gz?������P�P��D�y��A7��Y=��'4=s�;�� ���D�(�)�S�3��;V�0_�J<�d��8ޝ���m �/6��:y��P�/n|�%�L1�ida���~9QU�qUȔ�)\]H�I!T3��\J:V�_`��!J��L�A_=����g�4�j���C�l<o甼�r���6nJh$�TmVV�{̋��,��P���Fb���0�C��K��hq�'yWb'{�]X�-�������ܯ;�Tۛ����]QS%�;Ԙl��Ҥ�'+O�lrf�,�my9���Ppz���Ά�߭��w]7�pd��h?V�ĸn��k8���e��JC��u���ۯ�R�jL誓6[%Ic������U�e�)v�`~�WXԛ���a�k�<��M�vt9� ��i-����VO�U���ף}>^Y���7��x��+����3x ��݄��|XLO�y���%$%(N5U-s�����SC��D V�pxC��K�#�����W��+*F7{��Q7̼D:��/�} �g�g���wÚ�+�y�>��h-�T5!�ކO�V����^�p5T)"�] ���p�/w����D��Z��~.Υҿ �r1��ҡ\�齻k��<v�O��������ؿ_O�N��:�7x%~4�弔�˅S�N���ޒ���7����L}�ڈ�����I�PsLb_�c���\����{_݀^ሔ�dB�g@���Uf�Y%�u����xz��C<)��n}�_$�4��ޏ�p�%�/��e;�]x���%@����Pe�R�����7$�����Y���S#�h���-�m�5˧��z"�sQy�k�m., ���%�*Iki�QL���g˲�a_�j����^,K�g�Te-0��K^���* �����_G��-/���/���# �\�l�݊��K ��`y䳜��a1+��y(��c!���ʔ��aC��+VTg��/������f�����*�I��5)�n�L�6�����|���>�m��XP:��Km<>?e�,�!6��/��ݿ�$�Mմ_��9��'%��]\>gi@�'��Y�K&�� Z�XA��1���ȳ����y�j8�<�捽�a� �>��帿����|�&��5�\���r�0�C�?ˮ��T���Ō�����齞��"A��Y�K�҇�]g*����Z�J���^}������\���¥<%�K��p2��:m�V�ү8�����Z��JlNr�s�}-�?�Ao/c��.K5��:��KQ��p��`{h����!0ލ��.�T�+.3�>�4�(��'$`aq����P��w��7�&MO[\��L�\�S�d! ?�A;e�wV.�f�ͼ�vvT������M���(�}{� :ѹ��D�s�R��Ȣ�ы֎�'-go��[E�|��u���̗}���UT:>�<��� ���KÜ��L������-����~=�����^YC���k�{��{��n��'嫇64��seT17pLw䰢0ӱ�s�o}i|����Gc4T竾h��ߑ�:`#��9�)�T���gSB�І�MO�^u�UH<�E�t1���;b� �k}yV����y�� غ0���D �2��%�{�"ʗ�ͺ�\�bt��j���b��>���M������|ŖX���SU�/���Y{��H)½R �zjg�cp�����J&���9��dzw��m~"��r�ՠ&�&�����#>�R��%�߽{�k�M���y�Ö������i�ܹ��~��IE� ܙ(ر6�c$ � ���!���S&9a�b�dVaa��u]S��4ё�������-�j�W�'�(Ix�N ��/�ˉ�M��[ |���c ��lJ��W�hW>,�Yu��-=������]H@���ދ,����;��Wfw�Z{"�U���v���me�2W������/\���U�� x /`�7�L�)��N�>�A�.�"v��oa�������T� �G�f�0H���/#�k��ղ�w��W���L:N�퐒$+a[Y��A�n�3�u\ V37��:��� ��5,�1�^TP ~ �5��(kR��BEɹQQ�{�Qx� 0�3I�cm��H@,�SB��7�ȏ��i�s �*N�xEP�|ۺ�H���W|�Q)I�_�`�,�+���~.���wh��sG�!��¥�ٹ�-���<����pLu[Ǹ�2�5�z�ô��O�K����M�BIO��m{���oe����Ȁc�f�B �u5�v]0�� ����z�PF��I�����]��~�S^h�)Y�2��rTĂbn��@�r�O�j(s"��ǿ�J֗��Yx���-���.��Lz=e�$�489��ﶟ��Ă�(�2h��t������ �X��A;)�e�R�G*h-g�\�㲬�Y�� 7�C��玽�I�$������Ti�K&��Ot#X,}�r�I(����Z�]��:�����F��W�T�(Rx�;q�G�;���M�K���Rk�U*3ɀ2�њ�9 S �q�Z��U�;~���da�&���QĠ���}�m?�$ O�X�E��t�R�_���3��i��������� ��V�_�t�E=�#^�z�I����!0l�D���V��Gj�Ũyx��B�A�MPX G~���oWk��B d�ى�K��-[�e�~ng�^vQ��ew�v������{ Jy�i��]6-+�~�FED�L��s�?��ߋ6�-�w�\�ZU�[q�P���PTXy��� Y0s��^Ҙ��^��Z-7T���8��+̊p�Ls]sV��s2I+�z٤P�ʚ�n��/���W���P弨�4w����Fd�ʹLçIY�y�s���l�p�Z<מ��ٳp_|4`�U[�.Be\Ew����#��PX�T�赖���_X�� �迾�()��ЗWetĚ_��X���ϴu�0�M�@� 0m�$=o[��C�AoNY��S�a����@>�1W���م�/�>�0��G����RAh�����G�}=M=v����Ng�,*RE�xp�{=;% 0�W�rK�z�����[ZP5�P�a����̨�-��]��x�Z����M| @ IDATU�+y����z�M+�|�O�f8��� z�ؽ����S~N�����F Y�J,�����)�r_}n�5�W!�f���,(��er^D�kJ|$I�� b.T��y(\bq�_j G���'�y/ ��7�f8��}�l��v&��Uj������{�5�㞧��� �`����Yc�e��˶ݟ�)|e^@��s�WAk��++���)O��46�)��0=�%��Q��/VC��>L/B�7���RK�͜�ʳ��A�H�3�$@Wn�[�֦�7�}�*Yׯo�-�y������E"����,�]6���BϒޕҢ+�k�g�+���-M�-�bp�����\�?��2��>�G?b� �{S�m�SU���č^��u� ��2��h�@LT�|��*q�Tdn���o�}!�� �'�W���*��'6��!�� ���J����NZ�f\B� �ؙ�/(��|t&�"@���iP�G�Z�w%�s�f[�Xy��E��h�E�㽆�!ȸ�-�G��q��� י@���49c��(�ғI�<�ǿ�XW��- 7�8�L;NY�� @������re/[��N��CP�B&9|��,L�*l1��@�{��`T"��Å��M/�L��G��&���6c�0g����ޏ~ xq8m}2H���JE)����?4��[�:�ۛ�EqM-�C�\�M��X�?�Y�cY�Z�� q�2�cCU0���HX�_���������&�f���K%����VȥC\���^i��{���A��+'tj����K�� "��,.U$P @ރ�\7�ݺj[���x&4!]��sD��Z�Z��e�n&�> v��'$2�c����B��K'L�U(��2� \?k��_m�Y�bC{ޖ�tIe{�ǧ\t��A�V�i�m��7�+�*��=NY� ��k[**E��F�R$�UQ� ���g;W�f�~��K�n�u�M�r:I�/�y�E�9��i����ê��ێU�n�� g���Z��H@ˑ� Q6k�y�����Zk�µ�]2�rO�ٙ�Q*m�#� `�7�O�bMZ�1˲7ò:��i�I@�f���ǔ YU3u�w��r}Cm�q�������@x��V=�`���=S5M���d��\�/\;���z�UL���6�W�Zv��qzT�h�K�r ��mw���U�j6 dʹ��v�o(ۙ k��^^^paObG\j��EV�j��/��G4�̓�/���k��}�����-��bŐ5\fąE��H�.�]�Dm����SV�b{z9�}5 }O���+�k��)g�������6�����3/f�� 8�p�H�\�*��|x7����Z]���*\}p"�d�(���f��?ݽ��M����[U��fy�=v���E��d�#ϭ�W g87��xP�R/�]ڏ9�M��b�M�d��+@�ۍ6�:��6�SGO�.�:s�ic��o��F�h��R��x����V�J\rc/%�@��B���/���\�5u���3 3�^�}pd�>��N��ĺGv9�����t">4�E����J�H�2\��� �7�k<���4���٦�e�;d� ��&`w{<�*�]��<�k�~xEu��ׄp�s��j��>m�<e��"�"�� �}��~x=|~�j�0�}/�����曑��/�yM��Bi�J�� =�J�3��0\�TQ��NC�m�.������5m��R=�~�r��<��gUT�Z���U�r�Jk�JR��d���p�7f>1 �����"�>W������C���p��|&`��q�~kkG?a�P���Bq����HX�)�ܵl7�d�¸�\K ��a�QG��E2�Io�;�¥��= #RB���� �'˱���e���ߟ0E�T�z u�����TG�vU��Q���k�+=�3D�=��jPA��x�=pk��E="!�/�����(�� �RmP֦G`��kN��G�լ#aEQ��ҶH�r�v0�U �RT�%a�g�_�c��lq�1����RAC�4)�Z�R��=Ϊi�W���#7Ƈ�{:n� �x Է��)Y��+"J�a�~�P�ڊ���ΛS�_�=B����G�<��P�{r[y��hO�&��P��NK��͝:_\�?��`�v�jJ'�m�S��u6�3����|מWܵ=n(ZV>}�N~<�kT���J�[����|4 &M���ti��{��N��g�Ro�]r?{���y��Q���={5� Ze2�Q�7��4���:�k�����]�4�*$Q�wm�v�Ү�]:m�+���2�O��N�K�D��c��u[;+**��*'i��G#b�Z`�D�B���1�K�k��%�Z\o9�g�]:v �%�0Džn��k������31up�Oc���7L����u���8��C7{�pw�d�h� .��x������/N�,�M݇��TU�� v[�wd��&p�B`��tׅ g�O���W:q� �Y��(䪁��s�r#7ٔ�~Ɵ1���P��� 2�+�x8��]jඹ6m��q��q�P����}W�,�<��;tfmUIK̠����[7?�*ʪ�!M����s'�k:�?_����@:����/,��o����zk�e�U�E����ߟ���C�? �Z�l[���1��l�!�z���;�K�~�W�#�����S:�,��7�ҶE1�Kt�-L�Ս�N&ij�3#{퉥+�� �l�n$S;�4G�<6UV�*{dh �Y�t�1w�{����D))@������<,�C[$H@"��g�E�Vl������v���v#m��.�e��g��.�M��r�WWW��p�`���)%��S���x>��<��Ll[�n��ڢP��r#�5�E7͌h��#�w���ʾ�9�ky���_� �ۗ�u4�Q�kKK�\F'ʿ://Ѿn�^*�=�xN�̋�W(p�'�{:�="�W��\I��/B]%>���.립ݡ�(��똲k�>AVQ�Vn�\r)���N��0u#b{�$�Dt;�(*�w: n�-�x 4�5$aY \2��e�-��F��m��~[�N m@K�I����CQ�� �" xw �P"q� '�����$��)ҋ�KEm�5�P��߫��_x��*��\'�}o"�� N�}I@ʄ�0�M��ok��{����ܔb ��Ro ��Y�Z��rx"(?x��K$��"|���=��RB|/|�����n�<�;�t�4���+�c��={�/��d�����*-�k���75���>��CZ��c��Y՛;:�K� ��&��\9b��G�T���(�f�0I�H`@r�nt��߽ʞ�bk��$��Nn@�nNXN*�u�h-�n�J���,^��� ��w�26��ޅ*�V�JJ-�H;ݤ�Ꮛ���F�nȦ�����tt�=P�75�L; �0{6�$�߮*����h�0߬/�꺃�w>����n>�*cm��Ӈ�mp�p���{}�W�>~'q*2 �C�ج/���_��E��w8D@Mk'���iN�YLC{��{Zn#���'G ���F����KL���s��Xu�8��Wbٶ��A��������X5\����0D���75%W<4B�x�Ǝ�I�H�M�T}�R�>��$O�>�_o� �����[�}f�S���I`�`[��K��9p�q�g��bK��<M�p�T��/o=�Y�?1:��b���{���=��>x0�H��9�Y��մ��=���B�Dj�C�ڊ�&M�{��Je ��إC��H ��4�6.��HΔ5k�/����|\Vٹ�v%��{D�������d)���)m<}��|y2w �ka��m E�k���{Oc�S#sv��=���E����o�) 켬�F`53ǖ{�I3*=������^�dS c��&�=w��8w� ����F���������&u}4���@ǻ#�CY=��Z������ϼt��HYt���=�B�,�ˑ<����W�D7 `���5����CWL��)x��ᨮk�!A�sX��mx�tٯ|�w ����Q�m����?�rZY99gĞI�&���~�8,n��s�E���,[��|���^���wJ��l�� . ��m��OQ��p�����:����d�D$A�z%Ѵ���p�9�= j�CH�أ���c�|pn~V}��e�|�Hv}�@� VB�É!��`��'�������ɈX�� �������^I�R�aP8L�3,�#��١�seͻ5�����8D�|b������B�0.x��EB�3#��ʦH�1�c����]���o�3�Y�������4 j ��AŶ�w�t�hg<%�< t�]At�S�L�zKj��H$��I����}��ʳ+NX|�%ʿ�s�_K�P�rJ��K�F��]:}6"�WtՈ��gL*mi��o�����#��Z����Ԧ�� .~L����o�K��\y�m�0�8��VJ�T]ou)�� �ڷ�7���@O�E�m�)*�AǢy���Т�B�@� �&�F��HY$��_S�i��q�]����'J*g���V~�Tv�E�w���Γ�s{ܮ��kG��a�VLnuT�!`=w�i{\2�����gx�Q�3��T�ρ�.[�X|�#�?��Qܔ�T����d-�t�G��H�������}n�7�j~o�Xj�P�/55e���r�}GDxp�"���w�Tj:l�8:H1p �?�R��?=��@eN霠�} 0��s[ �E�@{#Q�U������F��W��;/���1ٝ�lj�����|tI`F���AO��հ#d�L���9�N"��O� �B^�|�x�8�n��h)�%C [\�la�s1J��:�H$g"��t� _����P��������3`{�l�=�X�z����j��:Y��K��E�}�*7�``os>�r�|�^9 4�euu|ުUǹ��'PRu���$���=qku��?�J8��p�G��=����,�A;��e���"t��=N��Q���rק0��R�U���Ay�$�5�~�˖��7�QT� �� ���>c�!�?3����� �4��>.�qaqF��P�t����t�?���/s��L;�ݰ��]�p�ЎJ缄y^ȡN��i�Dn����_ V��pZہ�ەu b;��c#L�q����q�?�c��ͅ�F�|�J�UI{,�/� /��p���6�/W�O�M��k�VZɟ��ZT��s�u.kzM�oi{�^�6�s`\����N�N�B�O̸|�[.)�ߕN�?�����ھ��&��'xP�8Xe$���6�bĔ/��,f����zm",��"2�� ߁�A���4+�厴��v�FS���T�'�km1�`�.7ZZLä�㼞$v��]�l��}�x�\�{_�s8�:�����/��Hʂ�V�>��]��Fa/TH��+��y�!F���$��Li�v~H�r9N �D�a^=M��9�4_2m�Y�̓Ų����q��?��]��# ���Կ����JQ��=3�}A��]ж��U�I��qb��fY�j�P�@|��I��?ܟ �c�#<d"� ;?Ҧ��^���v�{>)�� !\��7W���*+�nD���J�oE]�_\� 1Ley�'�qڶO%�E<u�T������H?���wC#|nﮜ2���_��g,a��BY���g ��ώ�X)�,s���@��<�2�c�}�T�*��̲��rr����(�Y��؛B�0����_��D����@Ko=�����O����8o_He����%�kf�"���,��Eԯ�V�2���xx-$q��0t�(|s٧�E ky��%�k�Y1KW��R��zR�M���dv��ZJ��o���f\8�q�р�����������T��\}7��Mb1��9 ���\�����.���e2n�}ٳ�����i�c�Kन��o�U���0�;� Dap�-����FE�W����;�1>}j�&5 ��TAd���gz���ګ��m�ɀhL�T��u�{�=���|�ܿ��C�e(�C�oH����x��uA� �"$-e�)>�NE !3��N���8�����b�$Q�v��x*,K��EW��@<��7)���f�/甖���UU{NQ�y~ E9;�H�� =�v��n�.U�vw�ި�n^4/��vF��!�#�t�{ֲo�8�yh�u&�_9�LD��Cz�_Y��c0c ��@,��3x�?��2�"��{�x� �t���� M�~Y��B���9'�2]]!�#X�X��~�(�#>����2�S&��4L�[�ݖ��R%�l�6K�=���fX*C�eyy�i����W�E��NŲ�K@���r�EfE��T�y�v�� P���Z瘲Pv�V|��W"Tه&4�|}t~XC����� 0�xë��L�lG �����N���̪MQ6!�̴�5��력���~&q90�3k Dv�tt&ހU�^=r|1{���A~�N�&�RF1��kv� _M� PN�-��Zņ���;���Q�8xs�-ӹ�|��sЮVh���qx�y��r_huc�}|ཟ��K[[3����eZ���Wյ��\?]8iRf��H~9J5�-ɨVb¹@��8}r��U�.���[�yU�&%��;�����r��?U��%\�u���BSz ��i��f#�F�H](�!��N�!q�T�;�Yn�� $7"n�N� ��!��c�LN�2��m��/�̟�%����DN�m~z���=���`����o�Sn���{� �r!�����nI�Dz#���#��{��Fu�)���p��=`-S'*�%�b�^P�s8�]�� S���(���=��I+;ίT�����L�~����ڶ����F�a� !�VJ��ާ5�.��b���'t������s��Y��}��[pYV����z?R���L) �tǠ�ɼ��G~�`���'Xr�Rd7n�����{d�݁��S�_�㽺�"T�|k]�ik�7c��^��I���㐷�ug����ѥ�d������(1F҈m\.�2D�ִ�H� ÖH�k`��/`�p崱�`Vǽ9i�k�D�3��'��#�/N|i�^�g�+UX�/s���pBY=�|�tX�7�6�d�t�%ɸb+(��P$�͉���"(��Y��v��6&a��G��R���3KB��1�v�y��)z8�^�5�1��aݩY�ee�ԓD�&@��\�d�z_w��%�(o^���55I�pK`�����>� >p�W�n�f�Ƨ�o������Z�H��'!bn[U��qr-5k�;��C"|���ۗe�%E�m��L���U]�I,����J<O=��¤R� &#�gX�g.�]�p�k�V���!���w�[�a�*��_ڰa`kq8uܑ��s{�-� `���0놁8���R�I��?䎤�.s I�~��,��#<7���(��4�|��)|PZ�lƷ[�ã�G|uȞ �>��V��5���[q~!Q�v���ڳ��{Y��gJb2�f�$w����=�.bCْZ����(��!6 �AQ������y?L �l� ��x�8���Wc�z���ʚY� %���vlչ�Y�8���f��������b����* �h)�ڴ!_){�H�=�R��=�z�~�`͙*�!h8_G��B{���E��:qN��B�a/0��q8�-�u,�9qؖ��:R�O�Zw�����Y*���8�@n0��ryA�%�㲬Ӝu��Qq<R�;��E���� =���h�~�Į�s>���xN��#z���;�c��s���P��.�����p7@� ��s�Y}�����b]v���#�Bϰ �n�m�0��rl~���R���M.(.X��7��r<�3D!~�j#��װ�pw�ޗ{[�����F<��V�N� �gm�8�1s.��Pi0�|TP�C*��-{���2qw1&i7E�O�zQ�H��㫁t�����@2�B��n?k��((���1�)����Ȇ6���oB�J�N�ES�]��$���,��Gn����-�SA��;�s�����U0;�ηT�н��=�'������Ӵ茕����>�.e%���j�H�z2v��-����g�"*t>��7@�-*g�p�,�SaO�;��V�)XR��^�mݗ��*PO�V�U�d_8&c���ȂsJKJ���'S�߂���pCX!@�����W���AGw�L3qF�^</}2�T��IP�K!+$��?կ�e�'';�|�u�M��==ԡ8t�=�Y�e��! ��# xzM�$���G��V �C�ͺkwڜX����7#lP'�ױ��u'#~ӓ�^7��\���y^�;���q���#%|�z��g�0���l� ����t��#�Qn� +�B���~�DފM�wf,� ~�)Σ���|AE�Ѭ��O�v�xf���_��b� `�{�l����19�B��B�|1 �wy�^ߔ�&|��iùκl�̸{ٲ���(�42��a6���C�_<�w3��ׅ����l�?4K������}x��-L ����q�!߳�e;Er�5�y%� ���`|��| ��e�L�P�h�O --��!D����R���p�ö��N����>��n���z�@v$�-�-�85DZO><AӍ���M���br| �3�* fVU�\���w��6x|1��"�|M`�/!��H�g8{��`]F�u\ˎ�����@�jy� I̪.��9�)�$���g�c5�yW�6p���7j���d��S� l�>13��Tzzm�����^��ڬ����^]=�� �ܗ��97��K��,�y7�I?�X�"�Ⳝ_))�%g!���</�~L�ZbJ_=b=�nV �jx8��|�iY�Q�hc�E�H�����<c�v�lL�yə��2�M�-?Y-��a�U�nNd֍��m��/<lժUYR]m:r0��0Dix�?���̹x���:E�U�d�c�s���w����;�+��I*�o��v��K9���� +�]٬����:�v�~M�Ԩ�a�q���+�iơPHff�3v��ú~���d���\*�ʢ�c3c͠c��O��l=c$�hd?���#E�{���O�E)j1�puf� B�4��?����?{�wD�o��Y���F�|L�#�:g�~Md��XN㈽dp�kl�p��ё��oCJ�2�aFٚ��d�RHd��X��}k�ԗ}�s��?�jߖX�x$6���R k,!��v�m�O�����^���AFۂS�aSK_jn6Y�'H):q���9������H5� �.!+��B�m%���v�ٟ~����Lp��d��� ��U&�-�#��`��Z�Ŭ���d��C~A��C�k�e�{����S8�4Tr�c1?`y�ApPJ~x�~������$x.� p+ӎ1�2�埞�x@��Nq����j�1�q�(�M ��m�i�@�������a^�X�����6���@O�,;� ��1���Ճ˞P1�f��Z�:�uw��z�;(�'��݆4R�h3�����x�HE��Y���L�e2�D����A�:����Кһ��ˀ*� ����>��$d�:Ԣ' ���8��wI1<r���Qo�A"���w�E��.�8� ������U�rQ����&�sJ�vt�Ȃu�\�A�Ws�]EE�c"ٟ�ľ�7�9Y{]�yX�yJ����@{�,�l��=�+�B�E���T!�'����NfO�sI͙��;�1�I�x�I� ���2�*�,�F��lDaƗd���P����f��|��j����k�\��M*2�M�rw�6�GȀ]d�9Y[ ��(/�5C+�dG���X��<$�JRM�DHF����|�hڢ��� >�f2ܥ����LX��噂��A��p/�A��Z�PFD����� �{�l�{�Ig7� �{v=x�B�J��ʜg���<n����/q� B�<0�;����uuM�!SQW_ *���S���@ax�(�/��@F|*�9LҔL�'��<�5��+?||�҉���m`��t�ܙ�d�{%��)�{��[4R���E �ͳ]�%g�[�U���D��6'�[vӚ�b)�� <Sl�NGo�YL9�\�?[��.��?Y2��>C]ΐߗۢ'��=!�t"�|~����b��`�cO����zCfB�[���ʡ�wH��'�đ����坝�!�n��R�g���`�z"2�g]FOf̟�S_�~�b�_;h�Ũ�a� fa��/{:RQ��d��}�6�ߍ�����-�"��v;Yu��{����[>A%}w���EQ����-�c���{Ţ�6O%7vᤡ����C�S���1��q�'����4&П�OѤ�϶D�4�h�%6qޞSQ�������k�F��ô;�T.K�?�d/V�"�^�F�)�(o[�V�26�>��#?~n{`<�;�/V"}+Z�3��i�M� ���M>����|@�8ؘ��X#�3õ͛,ǝH8��G8��l:����R��g�%�0�$�V��NZU� WuT;�3��#/x�}f�9;:�/k�\��R�[���\o�pOiP�����?nǻ��O|ʮm��&;Ǒ�����U3�H0�C�ĄyZ}}�IZ0oE�e��f�O*d��>�����l���>�u���rA�E��9�%R�xLEM-k0�trm����mݛ��|��i'�:�#�8��"��k�/>��I�?���z��X��Jm'����MpF{� �o�H���"��/���5D� �f<ϺWd���v[�/��p���(�ou'<��>��;�L��y� I=�J�g�cJ�;4b��ImRׇ1�_�"&�ƺ9<?�������P� �:�/7��H�I�\��_l�x�+)�{AJ�����p~Qj�b��g��@�\��D��<|��8Tt}Ϯ��H-[ ��a)�F�=�s��� $���k8F���Mi�~Æ k'fQ(36���)� �U��}l:�1S���b��:� q�"5P�� )�K���ֻ��xյ��t���mX��%&'*�!����.6����}[��Sjڿ�1T�}e�1ESǏG�ϛ�7i5CE5�z���ĺ���W�Oƺ3lp�u�1��6a'�7�9X�i�� ȿ7��h�Pr��a�t�m�;.����`b�K����@јϜ �o���H�J�![V�C����P#�l�����m���3b|�)O����ڑ*&�u�]�2�(��Ī�%ط�����b��Qϧ[�f��P��G]s��W4A�o��e��X��(R�4�$��c�F�a�a$���w��Dl��/F.Hf&���лS�|�ӷi�d�@��b�#0�`V��y�|b��5/,��6��/꘤s���E��l�i�L�լ�%�s$֭� ܒdV�."5��c��;kn��,+��];v���]E�a��?��`�8ӵn�}�Z`Ho�jF���9.g&I�ߐ���˚�4�._��>��M�-� �4�G�RX�F��PT�(ĽB���yȥ�]��R�e�#��~�w���8�� �I ����'���\9XK�T�t�g팺�|����w��X��HPl�ʩ�-��X�%Ĵ.A�B�&��x��U�,� 9���`��Wr�o�b�s�ε9�$@�m�il�Oc>����Wb��I��0�'pգ��w�N���;�|��|Pmyϔ��>N�WK[�$��xt��.�p�dM�!�q�Y�j�G*P�rvf=aq�n۰'�~�:K9y̸�Z�s���lIJ7�8r^�"��O�r���Ll=G��);��!���ǐ_')P��@���m�T5����E�0�%SYF��xL��a�[�HfY��tO�������!�^��c}e���;� dV`7��D�D�ӻcA�M9�#�?�=p���L�1��b�ٶ]7;�Y�cք����^6��f�;'z PF�����>���;�� �.Z�V$�R�8�T��\�o����s�X�� j�%��/�:7#��*ȻK�i�(I7ϩۛ�xp��_�$ ��}�o[?���/tb:i�⑯�GM�mPL�N�F=����=����Q3펈�҈:4E�-�xq��,�ZS�k�ǯ�V�),�ȁގs�wA}�7̕���l@9X���wC2��]��,�sa�7�kb� {˨�����c�"�����s���]��p��Bʀ���ӧ����>��O�s�=��N���t�S��9�ފ��h@�T�?�FP��߸�S�� J���3Y��涭;LF'ך��8K���N�_y�)��ݙ�]�,t����s��u�&ΦB�A�M��W�ğ�A����k��X�-�����������.�?�Sk�a� �u\�3 l]�����u�;`[���0\{QI�8z����u˒v �H���t0�����Q�/ ��k���w�����c1�?R#���A�1|9y���ZjEBQ%N�-����A�[5|��j�����s���Z7���� Η6~EG�bV���G��1E/_j?�e��| ��}Y��ƪ�N�2�A��<3�����rZ3��"�;ng�X�%ֳ3g���x���L S\2��%n�mv},kC��̚�3.\�������:P�/_� �K�*��}.�3�<8v�~�Bi�ԩ<����C�G���^���-�WWʕt{� P��̏��bE��e�<��}Ե���b-k�1�%����N{zgOIH�K_��JR7k�ϸ[�r��i1�U���OE��4V�#�}�ke[��Ϣ�ڱ�c]��4,�D;�s�"M �;��DWw���xR��{+��`���G���� �V=��mp�OA�bwO�#��/���eX�X�a�%e����D~7K����d��Xw�OϨ ���]4nze��s�tˉc���5����6������鱃��5� ���'�Md9�h�b;Ů���4��s��RY�|��-���a9��,?)ɚ\�Xޥ���$<��;�(��eH4_�H��!:~'��"sqo�)7����p���A4�s+��)��7�78�i�R���{a�˦Mk�?��e��˫�ê��q3�0�6�c�fZa�ȏ�y��~x�ɗ~���VXhNC־��H*h�p�� ��B��V�1�F5 �k�N��[�T����ҏ�r��ek"\�x��)��Zђ)˛Sh��S�ᆫ�����3�'�`���b;4S���ѵ���"��i ���͟�?"��DKC��Z��a��W>�Gn=���u���!�۷�d�z �KA�_B\�jֶn�m{�m;q��gBP� ��>Aw��B�_U�� �����ށ�uc�Z���H"{���e���-�|?�-��=�Dd�5N�7o�M��_7��$�0��@+MAǝ�;l����3�%���5���բ�}n�`��k��4�_�Vk��゜�����b�r��۶�]29^�b�̌5'ST4���F!��k|�&��1�1�f;нYg/�@{��N�&�NJ�-�} YS�����7�v��u�7� �1 [_y�e/��H�5=�e.�!�|_l����/rr�a�m������6��_]��7�Y~#rC]qTIp7��7���d����5�R���+���I A�8���d3/���?slMM>u��9�+I춌ҺpsF],{d�H���)v�����b���*�=�m�D�ѳgˁ�R�N� � �H$�;��B�@>��e�������V<���3>�U��� �D!k�t�)�ݒL�wӥ�@�c�gX�y���V�I�Z�q$B���9V���+Lw�M! �_o���p8Q�~��G�@���u'�~����UWBp��q$"��*����x]�Z��Po�*1�> ��6ZwZց�8���à�G(P:/c��k9�,GBbDC�&�?�71�&%�j�� 9.R{��Ot�JW�*��3ʃ�u���y4�Mݺ�Æ�K�4[��� .�C_5A�-�y�^�3}���8��l�̥��RVn��2�=��b=U����0�מ�]_�G��v�L�bq�����y>�~�W����0�u�~��mEdR�Nb��5 lE{�'��-�k� s�"0�s`�Yѝk|�r��鉥�<'O �-�T�2����ݹpa�p�З S�Qж��}T�7��WP��?�J،�,���e!��^i; 11�ty��4XC=�x���C+�}T�n�%S�ek�aP3d�!�+�I���菓`~��I�Fo��y �� �!��p�Tj�D�.�~�5�r,��Iس�U�ڀ>��3r�聉gI5Rl^���Sj���C3e �@���®�ѕo�Q�]x��(�\��ȩ�X?K(�0P:�0�/�c_�_S�� 9�G�`�أ�N8�*��צgMr�ƥ�MN���-�M'% #�^*T0��P�ѶEz5^����bͿ� *������Dp� �zDO�c�G� -���&v�hi�a& �ӽ���Q��N�*�+��%53��2�q윓��`��J`~Q���I�~��p}�����m��y5�Ɵ�W�䣒b�p�'���s��A�i��f�8����A�� f��_��5k�vލk�F4*<�^����vF��2���w���z8K�m4�B��g�z ���rY3��^>pM��B'^�[�d|H�Vʱ���jF��\_o?x�N�{ݍ�^�Ǵ/��s/�� 7�N*L� T�8�yB�:��G�4�(`�i�>�c0��~O����[��X69��n���|[�g_��ȫ�i��ɍ-v�]�ͳ@K���~�����6�LBSG��N)�7*��vM�m��e��d��9=�:���Y-)���v���~$��u�5J�E��I�cτ�yk���|��ҥv��Lj���]+rlo�"�$��y��0�F��kǪU��>�� �ϣ\de�s�ɻ�3�<d� H|��1�k��e1�,E��;����5]�!:m9��o���G��r?�)b㲽�H�֖����$�!�d�e�z��@��u�_��v[�������_EW�|�� ��2ojY_a�.+�+����ˣ�4���Q(7����y��qM�^2YH�wT�P�R9z��y_e�E�Px#NS �o �i]ƶgd����9;��"Ǫ���帷@kw94�4'��Zֶ��hl�,r<Cveu�p\%�m��2ol;�����K!��@o<uXap-���h�H�@ss͆�GCl��-�{V7��͍G��. 뗚�f�Q �^��c��S�G}��U{�����y��4>ߏa�|�����e"WŌ)�*�x�v�㙂��ܢ5��3+5���7����Y��Q �]^;��}���4NܨY��m���=[6�����4= �C�K�(��!�̽O�������Ө�4E�A�M� ��ED�Q˚���yE������9&URW����2�n�N\�����7�P�#��0�������%��kF �5�ˍ�84Y.GW7m� :_�oC�O�g��3�d���oS��a)HL7�PG����M�;��غ��V0�;$����Z�XRF�8�!���ђ헣�Q�ο:B4��-H���j�3+��[��0|��m'%�G}�Ec�KF���q�H��p:c� V��Xƹ ��4���c���BC**o����z0�l�ȶl� h�#�,����2�Br��e�i�7lP�w�$r&�}�tJ�M�a}O`��0��]u�0 wCP-c9�FA��ƶ �]⎜�X�ߑ��bì�3��X�9��Ԉ#�k� O��`m��KS=oq/o�]�����QsO�ū�K�3ٔ������%�KE���"���e5�U��lz�w @ IDAT^ag��}��(X�Z�Ȅ��%�]�=��h�1����&�Ǵ�2��F��)�u�v�Ҹ9����$�N�)��˲O�]g]��L��?����<]�,��v-�������E���i��[o<֞�1l7����L�)�:�ٯC�@�΄����.C�^l(�f�P���X�}5QP�|N�П��8쮻�ijIu��E��wl��D�C9~�6��3���������$��fS&�JaR��u���t�`���J�M��/�u8w5LQ\A��pT�[�Iu^���mi�$ ���,��w���h�빫 � ����=m-��+�l��O=~畦ŋs[Y�� Z�����}4b��,�+ / a=\Qr�o VHL0��hC���O�HG� 6/I�%��4����7`��� �y(-t.��.����}t<v���u6����0hv�J�v���_�in=%Κ^��'Ŏcy�.�R� l��6܉$����3xx@!�����Ș�G�����w�vwGc�od��'D��G=ֈ��K�塣�������Xj@&�ݐ5m��Qg6-�9�L���l7:�/ D�sJ��g�����؞R��k���R��d9�x"u�'_2�類���M�Q�����z;���r�|�?f����e�{�%R����&���u�^ϵ$�앮a&�:�2g9��>���b���B��q� �Fr<)�&g�c���T���]S��!N`�{tu�²BW�M���>�F3^Iz$N2� �?�*n��.%�g�:I� ���Z|���j��X��\��@=���� U#�A��& 3�#x`5�ٕ��v�˰� �͚w� a�k+�"�8�!hߧ�b�1��/�2?�ə��� �g�����w���Bm���Y'Mܘ H!�3�0[[�'^y��-0% �#��l �z�J[��F��m/��r�����]��=*'h��. x���GM6 Ç{j&/��l��f���'x%HR�@-���|| ��^Y�?H��zۓ0܄�� F�!��@�r"�#UK�ѶQ$�i���������9~d{wo���6�߅S�>�s�G�$��{Ko�<�@�<Á�{U$�w1��ٟ�c�0o����|���)�]�L���*�?K��?�z���Nat�#�&�wʮ�?��Kttx�#F�M���9�v� j�rahY<v)��Ͽ���c]s�����0/�!(��%�aΰ\{��KS)� $k��9N�i�ۋ���Bj���.k�P\��8lV�Q��(�<گ\z����؛���&4�D�����#*и��1�`�P�� U�M�>���Jz���� ��� mN�v;AlaM���t�R-Q�Ϻ;��[�ʹ==�Cf�|y[;:@�Ì���m��v|L�-��H�ۓ���b��9��I�6]'i���3V��2�-F`Z���E�m�ӆ��ў�YH�vꑖX���}��,^�儊'3�ѥFw�6`v�u�A�e���ٟ�6i�=�.4��_#�� w,�H��4g-r��g�.�4'B���~���JCU��2ןTpB .��s�M�@�g��x�d�c�lǽJZ�r�Q�ç{���^�J7z�����=E 5%��ϑ{T�%����O��dx���K�3k�;��_8a�3�5�6���:�%e�}-�(�p�o�bh1T� �F6p�xe���^9�mHpH5몙'���r9 ]"�iP��/'