Commit 472403c2 authored by webchick's avatar webchick

Issue #2563799 by dawehner: Upgrade Guzzle to a tagged release

parent 3cd09437
......@@ -21,7 +21,7 @@
"twig/twig": "~1.20",
"doctrine/common": "2.5.*",
"doctrine/annotations": "1.2.*",
"guzzlehttp/guzzle": "dev-master#1879fbe853b0c64d109e369c7aeff09849e62d1e",
"guzzlehttp/guzzle": "~6.1.0",
"symfony-cmf/routing": "1.3.*",
"easyrdf/easyrdf": "0.9.*",
"phpunit/phpunit": "4.8.*",
......
......@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "d39a311bc4d6e7db527d6a43a8ac34d4",
"hash": "eb73d28ce27162c98ea1a8865fddbf7d",
"packages": [
{
"name": "behat/mink",
......@@ -790,12 +790,12 @@
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e"
"reference": "66fd14b4d0b8f2389eaf37c5458608c7cb793a81"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/33af93ab4b5e76df7a4c774910590f814a3e576c",
"reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/66fd14b4d0b8f2389eaf37c5458608c7cb793a81",
"reference": "66fd14b4d0b8f2389eaf37c5458608c7cb793a81",
"shasum": ""
},
"require": {
......@@ -811,7 +811,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.0-dev"
"dev-master": "6.1-dev"
}
},
"autoload": {
......@@ -844,7 +844,7 @@
"rest",
"web service"
],
"time": "2015-07-10 20:04:21"
"time": "2015-09-08 17:36:26"
},
{
"name": "guzzlehttp/promises",
......@@ -3376,7 +3376,6 @@
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
"guzzlehttp/guzzle": 20,
"behat/mink-goutte-driver": 20
},
"prefer-stable": true,
......
......@@ -581,70 +581,6 @@
"psr-7"
]
},
{
"name": "guzzlehttp/guzzle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/33af93ab4b5e76df7a4c774910590f814a3e576c",
"reference": "1879fbe853b0c64d109e369c7aeff09849e62d1e",
"shasum": ""
},
"require": {
"guzzlehttp/promises": "~1.0",
"guzzlehttp/psr7": "~1.1",
"php": ">=5.5.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "~4.0",
"psr/log": "~1.0"
},
"time": "2015-07-10 20:04:21",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.0-dev"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
]
},
{
"name": "behat/mink-goutte-driver",
"version": "dev-master",
......@@ -3483,5 +3419,69 @@
"serializer",
"xml"
]
},
{
"name": "guzzlehttp/guzzle",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "66fd14b4d0b8f2389eaf37c5458608c7cb793a81"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/66fd14b4d0b8f2389eaf37c5458608c7cb793a81",
"reference": "66fd14b4d0b8f2389eaf37c5458608c7cb793a81",
"shasum": ""
},
"require": {
"guzzlehttp/promises": "~1.0",
"guzzlehttp/psr7": "~1.1",
"php": ">=5.5.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "~4.0",
"psr/log": "~1.0"
},
"time": "2015-09-08 17:36:26",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.1-dev"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
]
}
]
# CHANGELOG
## 6.1.0 - 2015-09-08
* Feature: Added the `on_stats` request option to provide access to transfer
statistics for requests. https://github.com/guzzle/guzzle/pull/1202
* Feature: Added the ability to persist session cookies in CookieJars.
https://github.com/guzzle/guzzle/pull/1195
* Feature: Some compatibility updates for Google APP Engine
https://github.com/guzzle/guzzle/pull/1216
* Feature: Added support for NO_PROXY to prevent the use of a proxy based on
a simple set of rules. https://github.com/guzzle/guzzle/pull/1197
* Feature: Cookies can now contain square brackets.
https://github.com/guzzle/guzzle/pull/1237
* Bug fix: Now correctly parsing `=` inside of quotes in Cookies.
https://github.com/guzzle/guzzle/pull/1232
* Bug fix: Cusotm cURL options now correctly override curl options of the
same name. https://github.com/guzzle/guzzle/pull/1221
* Bug fix: Content-Type header is now added when using an explicitly provided
multipart body. https://github.com/guzzle/guzzle/pull/1218
* Bug fix: Now ignoring Set-Cookie headers that have no name.
* Bug fix: Reason phrase is no longer cast to an int in some cases in the
cURL handler. https://github.com/guzzle/guzzle/pull/1187
* Bug fix: Remove the Authorization header when redirecting if the Host
header changes. https://github.com/guzzle/guzzle/pull/1207
* Bug fix: Cookie path matching fixes
https://github.com/guzzle/guzzle/issues/1129
* Bug fix: Fixing the cURL `body_as_string` setting
https://github.com/guzzle/guzzle/pull/1201
* Bug fix: quotes are no longer stripped when parsing cookies.
https://github.com/guzzle/guzzle/issues/1172
* Bug fix: `form_params` and `query` now always uses the `&` separator.
https://github.com/guzzle/guzzle/pull/1163
* Bug fix: Adding a Content-Length to PHP stream wrapper requests if not set.
https://github.com/guzzle/guzzle/pull/1189
## 6.0.2 - 2015-07-04
* Fixed a memory leak in the curl handlers in which references to callbacks
......
......@@ -19,7 +19,9 @@ trivial to integrate with web services.
```php
$client = new GuzzleHttp\Client();
$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]);
$res = $client->request('GET', 'https://api.github.com/user', [
'auth' => ['user', 'pass']
]);
echo $res->getStatusCode();
// "200"
echo $res->getHeader('content-type');
......
......@@ -83,6 +83,10 @@ functions that wrap handlers (or are injected into a
- `GuzzleHttp\ClientInterface::getDefaultOption` has been renamed to
`GuzzleHttp\ClientInterface::getConfig`.
- `GuzzleHttp\ClientInterface::setDefaultOption` has been removed.
- The `json` and `xml` methods of response objects has been removed. With the
migration to strictly adhering to PSR-7 as the interface for Guzzle messages,
adding methods to message interfaces would actually require Guzzle messages
to extend from PSR-7 messages rather then work with them directly.
## Migrating to middleware
......
......@@ -35,7 +35,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.0-dev"
"dev-master": "6.1-dev"
}
}
}
......@@ -7,7 +7,6 @@
use Psr\Http\Message\UriInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use \InvalidArgumentException as Iae;
/**
* @method ResponseInterface get($uri, array $options = [])
......@@ -150,8 +149,6 @@ private function buildUri($uri, array $config)
* Configures the default options for a client.
*
* @param array $config
*
* @return array
*/
private function configureDefaults(array $config)
{
......@@ -172,6 +169,11 @@ private function configureDefaults(array $config)
$defaults['proxy']['https'] = $proxy;
}
if ($noProxy = getenv('NO_PROXY')) {
$cleanedNoProxy = str_replace(' ', '', $noProxy);
$defaults['proxy']['no'] = explode(',', $cleanedNoProxy);
}
$this->config = $config + $defaults;
if (!empty($config['cookies']) && $config['cookies'] === true) {
......@@ -298,9 +300,6 @@ private function applyOptions(RequestInterface $request, array &$options)
$elements = $options['multipart'];
unset($options['multipart']);
$options['body'] = new Psr7\MultipartStream($elements);
// Use a multipart/form-data POST if a Content-Type is not set.
$options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
. $options['body']->getBoundary();
}
if (!empty($options['decode_content'])
......@@ -351,7 +350,7 @@ private function applyOptions(RequestInterface $request, array &$options)
$value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);
}
if (!is_string($value)) {
throw new Iae('query must be a string or array');
throw new \InvalidArgumentException('query must be a string or array');
}
$modify['query'] = $value;
unset($options['query']);
......@@ -364,6 +363,11 @@ private function applyOptions(RequestInterface $request, array &$options)
}
$request = Psr7\modify_request($request, $modify);
if ($request->getBody() instanceof Psr7\MultipartStream) {
// Use a multipart/form-data POST if a Content-Type is not set.
$options['_conditional']['Content-Type'] = 'multipart/form-data; boundary='
. $request->getBody()->getBoundary();
}
// Merge in conditional headers if they are not present.
if (isset($options['_conditional'])) {
......
......@@ -12,7 +12,7 @@
*/
interface ClientInterface
{
const VERSION = '6.0.2';
const VERSION = '6.1.0';
/**
* Send an HTTP request.
......
......@@ -18,8 +18,9 @@ class CookieJar implements CookieJarInterface
/**
* @param bool $strictMode Set to true to throw exceptions when invalid
* cookies are added to the cookie jar.
* @param array $cookieArray Array of SetCookie objects or a hash of arrays
* that can be used with the SetCookie constructor
* @param array $cookieArray Array of SetCookie objects or a hash of
* arrays that can be used with the SetCookie
* constructor
*/
public function __construct($strictMode = false, $cookieArray = [])
{
......@@ -68,7 +69,7 @@ public static function getCookieValue($value)
{
if (substr($value, 0, 1) !== '"' &&
substr($value, -1, 1) !== '"' &&
strpbrk($value, ';,')
strpbrk($value, ';,=')
) {
$value = '"' . $value . '"';
}
......@@ -76,6 +77,27 @@ public static function getCookieValue($value)
return $value;
}
/**
* Evaluate if this cookie should be persisted to storage
* that survives between requests.
*
* @param SetCookie $cookie Being evaluated.
* @param bool $allowSessionCookies If we should presist session cookies
* @return bool
*/
public static function shouldPersist(
SetCookie $cookie,
$allowSessionCookies = false
) {
if ($cookie->getExpires() || $allowSessionCookies) {
if (!$cookie->getDiscard()) {
return true;
}
}
return false;
}
public function toArray()
{
return array_map(function (SetCookie $cookie) {
......@@ -127,6 +149,13 @@ function (SetCookie $cookie) {
public function setCookie(SetCookie $cookie)
{
// If the name string is empty (but not 0), ignore the set-cookie
// string entirely.
$name = $cookie->getName();
if (!$name && $name !== '0') {
return false;
}
// Only allow cookies with set and valid domain, name, value
$result = $cookie->validate();
if ($result !== true) {
......
......@@ -9,16 +9,22 @@ class FileCookieJar extends CookieJar
/** @var string filename */
private $filename;
/** @var bool Control whether to presist session cookies or not. */
private $storeSessionCookies;
/**
* Create a new FileCookieJar object
*
* @param string $cookieFile File to store the cookie data
* @param string $cookieFile File to store the cookie data
* @param bool $storeSessionCookies Set to true to store session cookies
* in the cookie jar.
*
* @throws \RuntimeException if the file cannot be found or created
*/
public function __construct($cookieFile)
public function __construct($cookieFile, $storeSessionCookies = false)
{
$this->filename = $cookieFile;
$this->storeSessionCookies = $storeSessionCookies;
if (file_exists($cookieFile)) {
$this->load($cookieFile);
......@@ -44,7 +50,7 @@ public function save($filename)
$json = [];
foreach ($this as $cookie) {
/** @var SetCookie $cookie */
if ($cookie->getExpires() && !$cookie->getDiscard()) {
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
$json[] = $cookie->toArray();
}
}
......
......@@ -8,15 +8,22 @@ class SessionCookieJar extends CookieJar
{
/** @var string session key */
private $sessionKey;
/** @var bool Control whether to presist session cookies or not. */
private $storeSessionCookies;
/**
* Create a new SessionCookieJar object
*
* @param string $sessionKey Session key name to store the cookie data in session
* @param string $sessionKey Session key name to store the cookie
* data in session
* @param bool $storeSessionCookies Set to true to store session cookies
* in the cookie jar.
*/
public function __construct($sessionKey)
public function __construct($sessionKey, $storeSessionCookies = false)
{
$this->sessionKey = $sessionKey;
$this->storeSessionCookies = $storeSessionCookies;
$this->load();
}
......@@ -36,7 +43,7 @@ public function save()
$json = [];
foreach ($this as $cookie) {
/** @var SetCookie $cookie */
if ($cookie->getExpires() && !$cookie->getDiscard()) {
if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
$json[] = $cookie->toArray();
}
}
......
......@@ -46,7 +46,7 @@ public static function fromString($cookie)
$cookieParts = explode('=', $part, 2);
$key = trim($cookieParts[0]);
$value = isset($cookieParts[1])
? trim($cookieParts[1], " \n\r\t\0\x0B\"")
? trim($cookieParts[1], " \n\r\t\0\x0B")
: true;
// Only check for non-cookies when cookies have been found
......@@ -286,15 +286,43 @@ public function setHttpOnly($httpOnly)
}
/**
* Check if the cookie matches a path value
* Check if the cookie matches a path value.
*
* @param string $path Path to check against
* A request-path path-matches a given cookie-path if at least one of
* the following conditions holds:
*
* - The cookie-path and the request-path are identical.
* - The cookie-path is a prefix of the request-path, and the last
* character of the cookie-path is %x2F ("/").
* - 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.
*
* @param string $requestPath Path to check against
*
* @return bool
*/
public function matchesPath($path)
public function matchesPath($requestPath)
{
return !$this->getPath() || 0 === stripos($path, $this->getPath());
$cookiePath = $this->getPath();
// Match on exact matches or when path is the default empty "/"
if ($cookiePath == '/' || $cookiePath == $requestPath) {
return true;
}
// Ensure that the cookie-path is a prefix of the request path.
if (0 !== strpos($requestPath, $cookiePath)) {
return false;
}
// Match if the last character of the cookie-path is "/"
if (substr($cookiePath, -1, 1) == '/') {
return true;
}
// Match if the first character not included in cookie path is "/"
return substr($requestPath, strlen($cookiePath), 1) == '/';
}
/**
......@@ -321,7 +349,7 @@ public function matchesDomain($domain)
return false;
}
return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain);
return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/', $domain);
}
/**
......@@ -348,8 +376,13 @@ public function validate()
}
// Check if any of the invalid characters are present in the cookie name
if (preg_match('/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5b-\x5d\x7b\x7d\x7f]/', $name)) {
return 'Cookie name must not contain invalid characters: ASCII Control characters (0-31;127), space, tab and the following characters: ()<>@,;:\"/[]?={}';
if (preg_match(
'/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/',
$name)
) {
return 'Cookie name must not contain invalid characters: ASCII '
. 'Control characters (0-31;127), space, tab and the '
. 'following characters: ()<>@,;:\"/?={}';
}
// Value must not be empty, but can be 0
......
......@@ -7,6 +7,7 @@
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\LazyOpenStream;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
/**
......@@ -30,6 +31,11 @@ public function __construct($maxHandles)
public function create(RequestInterface $request, array $options)
{
if (isset($options['curl']['body_as_string'])) {
$options['_body_as_string'] = $options['curl']['body_as_string'];
unset($options['curl']['body_as_string']);
}
$easy = new EasyHandle;
$easy->request = $request;
$easy->options = $options;
......@@ -39,14 +45,9 @@ public function create(RequestInterface $request, array $options)
$this->applyHeaders($easy, $conf);
unset($conf['_headers']);
if (isset($options['curl']['body_as_string'])) {
$options['_body_as_string'] = $options['curl']['body_as_string'];
unset($options['curl']['body_as_string']);
}
// Add handler options from the request configuration options
if (isset($options['curl'])) {
$conf += $options['curl'];
$conf = array_replace($conf, $options['curl']);
}
$conf[CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy);
......@@ -94,6 +95,10 @@ public static function finish(
EasyHandle $easy,
CurlFactoryInterface $factory
) {
if (isset($easy->options['on_stats'])) {
self::invokeStats($easy);
}
if (!$easy->response || $easy->errno) {
return self::finishError($handler, $easy, $factory);
}
......@@ -110,6 +115,19 @@ public static function finish(
return new FulfilledPromise($easy->response);
}
private static function invokeStats(EasyHandle $easy)
{
$curlStats = curl_getinfo($easy->handle);
$stats = new TransferStats(
$easy->request,
$easy->response,
$curlStats['total_time'],
$easy->errno,
$curlStats
);
call_user_func($easy->options['on_stats'], $stats);
}
private static function finishError(
callable $handler,
EasyHandle $easy,
......@@ -361,9 +379,15 @@ private function applyHandlerOptions(EasyHandle $easy, array &$conf)
if (isset($options['proxy'])) {
if (!is_array($options['proxy'])) {
$conf[CURLOPT_PROXY] = $options['proxy'];
} elseif ($scheme = $easy->request->getUri()->getScheme()) {
} else {
$scheme = $easy->request->getUri()->getScheme();
if (isset($options['proxy'][$scheme])) {
$conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
$host = $easy->request->getUri()->getHost();
if (!isset($options['proxy']['no']) ||
!\GuzzleHttp\is_host_in_noproxy($host, $options['proxy']['no'])
) {
$conf[CURLOPT_PROXY] = $options['proxy'][$scheme];
}
}
}
}
......
......@@ -73,7 +73,7 @@ public function createResponse()
$headers,
$this->sink,
substr($startLine[0], 5),
isset($startLine[2]) ? (int) $startLine[2] : null
isset($startLine[2]) ? (string) $startLine[2] : null
);
}
......
......@@ -4,6 +4,7 @@
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
......@@ -80,18 +81,16 @@ public function __invoke(RequestInterface $request, array $options)
? new RejectedPromise($response)
: \GuzzleHttp\Promise\promise_for($response);
if (!$this->onFulfilled && !$this->onRejected) {
return $response;
}
return $response->then(
function ($value) {
function ($value) use ($request, $options) {
$this->invokeStats($request, $options, $value);
if ($this->onFulfilled) {
call_user_func($this->onFulfilled, $value);
}
return $value;
},
function ($reason) {
function ($reason) use ($request, $options) {
$this->invokeStats($request, $options, null, $reason);
if ($this->onRejected) {
call_user_func($this->onRejected, $reason);
}
......@@ -149,4 +148,16 @@ public function count()
{
return count($this->queue);
}
private function invokeStats(
RequestInterface $request,
array $options,
ResponseInterface $response = null,
$reason = null
) {
if (isset($options['on_stats'])) {
$stats = new TransferStats($request, $response, 0, $reason);
call_user_func($options['on_stats'], $stats);
}
}
}
......@@ -7,7 +7,9 @@
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
/**
......@@ -32,11 +34,24 @@ public function __invoke(RequestInterface $request, array $options)
usleep($options['delay'] * 1000);