Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/3093' into develop
Browse files Browse the repository at this point in the history
Close #3093
Fixes #2572
  • Loading branch information
weierophinney committed Dec 11, 2012
2 parents 36a4c07 + a96d872 commit 1862af4
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 26 deletions.
11 changes: 5 additions & 6 deletions library/Zend/Http/Client.php
Expand Up @@ -497,11 +497,11 @@ public function addCookie($cookie, $value = null, $expire = null, $path = null,
throw new Exception\InvalidArgumentException('The cookie parameter is not a valid Set-Cookie type'); throw new Exception\InvalidArgumentException('The cookie parameter is not a valid Set-Cookie type');
} }
} }
} elseif ($cookie instanceof Header\SetCookie) {
$this->cookies[$this->getCookieId($cookie)] = $cookie;
} elseif (is_string($cookie) && $value !== null) { } elseif (is_string($cookie) && $value !== null) {
$setCookie = new Header\SetCookie($cookie, $value, $expire, $path, $domain, $secure, $httponly, $maxAge, $version); $setCookie = new Header\SetCookie($cookie, $value, $expire, $path, $domain, $secure, $httponly, $maxAge, $version);
$this->cookies[$this->getCookieId($setCookie)] = $setCookie; $this->cookies[$this->getCookieId($setCookie)] = $setCookie;
} elseif ($cookie instanceof Header\SetCookie) {
$this->cookies[$this->getCookieId($cookie)] = $cookie;
} else { } else {
throw new Exception\InvalidArgumentException('Invalid parameter type passed as Cookie'); throw new Exception\InvalidArgumentException('Invalid parameter type passed as Cookie');
} }
Expand Down Expand Up @@ -883,9 +883,9 @@ public function send(Request $request = null)
} }


// Get the cookies from response (if any) // Get the cookies from response (if any)
$setCookie = $response->getCookie(); $setCookies = $response->getCookie();
if (!empty($setCookie)) { if (!empty($setCookies)) {
$this->addCookie($setCookie); $this->addCookie($setCookies);
} }


// If we got redirected, look for the Location header // If we got redirected, look for the Location header
Expand Down Expand Up @@ -1322,7 +1322,6 @@ protected function doRequest(Http $uri, $method, $secure = false, $headers = arr
throw new Exception\RuntimeException('Adapter does not support streaming'); throw new Exception\RuntimeException('Adapter does not support streaming');
} }
} }

// HTTP connection // HTTP connection
$this->lastRawRequest = $this->adapter->write($method, $this->lastRawRequest = $this->adapter->write($method,
$uri, $this->config['httpversion'], $headers, $body); $uri, $this->config['httpversion'], $headers, $body);
Expand Down
3 changes: 2 additions & 1 deletion library/Zend/Http/Client/Adapter/Curl.php
Expand Up @@ -374,6 +374,7 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $bo
foreach ($headers as $key => $value) { foreach ($headers as $key => $value) {
$curlHeaders[] = $key . ': ' . $value; $curlHeaders[] = $key . ': ' . $value;
} }

curl_setopt($this->curl, CURLOPT_HTTPHEADER, $curlHeaders); curl_setopt($this->curl, CURLOPT_HTTPHEADER, $curlHeaders);


/** /**
Expand Down Expand Up @@ -409,8 +410,8 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $bo
} }


// send the request // send the request
$response = curl_exec($this->curl);


$response = curl_exec($this->curl);
// if we used streaming, headers are already there // if we used streaming, headers are already there
if (!is_resource($this->outputStream)) { if (!is_resource($this->outputStream)) {
$this->response = $response; $this->response = $response;
Expand Down
59 changes: 45 additions & 14 deletions library/Zend/Http/Cookies.php
Expand Up @@ -10,10 +10,11 @@


namespace Zend\Http; namespace Zend\Http;


use Zend\Http\Header\Cookie; use Zend\Http\Header\SetCookie;
use Zend\Http\Response; use Zend\Http\Response;
use Zend\Uri; use Zend\Uri;



/** /**
* A Zend_Http_CookieJar object is designed to contain and maintain HTTP cookies, and should * A Zend_Http_CookieJar object is designed to contain and maintain HTTP cookies, and should
* be used along with Zend_Http_Client in order to manage cookies across HTTP requests and * be used along with Zend_Http_Client in order to manage cookies across HTTP requests and
Expand All @@ -37,6 +38,36 @@
*/ */
class Cookies extends Headers class Cookies extends Headers
{ {
/**
* Return cookie(s) as a Zend_Http_Cookie object
*
*/
const COOKIE_OBJECT = 0;

/**
* Return cookie(s) as a string (suitable for sending in an HTTP request)
*
*/
const COOKIE_STRING_ARRAY = 1;

/**
* Return all cookies as one long string (suitable for sending in an HTTP request)
*
*/
const COOKIE_STRING_CONCAT = 2;

/**
* Return all cookies as one long string (strict mode)
* - Single space after the semi-colon separating each cookie
* - Remove trailing semi-colon, if any
*/
const COOKIE_STRING_CONCAT_STRICT = 3;

/**
* @var \Zend\Http\Cookies
*/
protected $cookies = array();

/** /**
* @var \Zend\Http\Headers * @var \Zend\Http\Headers
*/ */
Expand Down Expand Up @@ -70,17 +101,17 @@ public function __construct(Headers $headers)
* Add a cookie to the class. Cookie should be passed either as a Zend\Http\Header\Cookie object * Add a cookie to the class. Cookie should be passed either as a Zend\Http\Header\Cookie object
* or as a string - in which case an object is created from the string. * or as a string - in which case an object is created from the string.
* *
* @param Cookie|string $cookie * @param SetCookie|string $cookie
* @param Uri\Uri|string $ref_uri Optional reference URI (for domain, path, secure) * @param Uri\Uri|string $ref_uri Optional reference URI (for domain, path, secure)
* @throws Exception\InvalidArgumentException * @throws Exception\InvalidArgumentException
*/ */
public function addCookie(Cookie $cookie, $ref_uri = null) public function addCookie( $cookie, $ref_uri = null)
{ {
if (is_string($cookie)) { if (is_string($cookie)) {
$cookie = Cookie::fromString($cookie, $ref_uri); $cookie = SetCookie::fromString($cookie, $ref_uri);
} }


if ($cookie instanceof Cookie) { if ($cookie instanceof SetCookie) {
$domain = $cookie->getDomain(); $domain = $cookie->getDomain();
$path = $cookie->getPath(); $path = $cookie->getPath();
if (!isset($this->cookies[$domain])) { if (!isset($this->cookies[$domain])) {
Expand All @@ -106,7 +137,7 @@ public function addCookiesFromResponse(Response $response, $ref_uri)
{ {
$cookie_hdrs = $response->getHeaders()->get('Set-Cookie'); $cookie_hdrs = $response->getHeaders()->get('Set-Cookie');


if (is_array($cookie_hdrs)) { if (is_array($cookie_hdrs) || $cookie_hdrs instanceof \ArrayIterator) {
foreach ($cookie_hdrs as $cookie) { foreach ($cookie_hdrs as $cookie) {
$this->addCookie($cookie, $ref_uri); $this->addCookie($cookie, $ref_uri);
} }
Expand All @@ -118,7 +149,7 @@ public function addCookiesFromResponse(Response $response, $ref_uri)
/** /**
* Get all cookies in the cookie jar as an array * Get all cookies in the cookie jar as an array
* *
* @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
* @return array|string * @return array|string
*/ */
public function getAllCookies($ret_as = self::COOKIE_OBJECT) public function getAllCookies($ret_as = self::COOKIE_OBJECT)
Expand All @@ -134,7 +165,7 @@ public function getAllCookies($ret_as = self::COOKIE_OBJECT)
* *
* @param string|Uri\Uri $uri URI to check against (secure, domain, path) * @param string|Uri\Uri $uri URI to check against (secure, domain, path)
* @param boolean $matchSessionCookies Whether to send session cookies * @param boolean $matchSessionCookies Whether to send session cookies
* @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
* @param int $now Override the current time when checking for expiry time * @param int $now Override the current time when checking for expiry time
* @throws Exception\InvalidArgumentException if invalid URI specified * @throws Exception\InvalidArgumentException if invalid URI specified
* @return array|string * @return array|string
Expand Down Expand Up @@ -175,9 +206,9 @@ public function getMatchingCookies($uri, $matchSessionCookies = true,
* *
* @param Uri\Uri|string $uri The uri (domain and path) to match * @param Uri\Uri|string $uri The uri (domain and path) to match
* @param string $cookie_name The cookie's name * @param string $cookie_name The cookie's name
* @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
* @throws Exception\InvalidArgumentException if invalid URI specified or invalid $ret_as value * @throws Exception\InvalidArgumentException if invalid URI specified or invalid $ret_as value
* @return Cookie|string * @return SetCookie|string
*/ */
public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT) public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT)
{ {
Expand Down Expand Up @@ -223,7 +254,7 @@ public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT)
* Helper function to recursively flatten an array. Should be used when exporting the * Helper function to recursively flatten an array. Should be used when exporting the
* cookies array (or parts of it) * cookies array (or parts of it)
* *
* @param \Zend\Http\Header\Cookie|array $ptr * @param \Zend\Http\Header\SetCookie|array $ptr
* @param int $ret_as What value to return * @param int $ret_as What value to return
* @return array|string * @return array|string
*/ */
Expand All @@ -239,7 +270,7 @@ protected function _flattenCookiesArray($ptr, $ret_as = self::COOKIE_OBJECT)
} }
} }
return $ret; return $ret;
} elseif ($ptr instanceof Cookie) { } elseif ($ptr instanceof SetCookie) {
switch ($ret_as) { switch ($ret_as) {
case self::COOKIE_STRING_ARRAY: case self::COOKIE_STRING_ARRAY:
return array($ptr->__toString()); return array($ptr->__toString());
Expand Down Expand Up @@ -270,7 +301,7 @@ protected function _matchDomain($domain)
$ret = array(); $ret = array();


foreach (array_keys($this->cookies) as $cdom) { foreach (array_keys($this->cookies) as $cdom) {
if (Cookie::matchCookieDomain($cdom, $domain)) { if (SetCookie::matchCookieDomain($cdom, $domain)) {
$ret[$cdom] = $this->cookies[$cdom]; $ret[$cdom] = $this->cookies[$cdom];
} }
} }
Expand All @@ -291,7 +322,7 @@ protected function _matchPath($domains, $path)


foreach ($domains as $dom => $paths_array) { foreach ($domains as $dom => $paths_array) {
foreach (array_keys($paths_array) as $cpath) { foreach (array_keys($paths_array) as $cpath) {
if (Cookie::matchCookiePath($cpath, $path)) { if (SetCookie::matchCookiePath($cpath, $path)) {
if (! isset($ret[$dom])) { if (! isset($ret[$dom])) {
$ret[$dom] = array(); $ret[$dom] = array();
} }
Expand Down
88 changes: 88 additions & 0 deletions library/Zend/Http/Header/SetCookie.php
Expand Up @@ -98,6 +98,7 @@ public static function fromString($headerLine, $bypassHeaderFieldName = false)
$setCookieProcessor = function ($headerLine) use ($setCookieClass) { $setCookieProcessor = function ($headerLine) use ($setCookieClass) {
$header = new $setCookieClass; $header = new $setCookieClass;
$keyValuePairs = preg_split('#;\s*#', $headerLine); $keyValuePairs = preg_split('#;\s*#', $headerLine);

foreach ($keyValuePairs as $keyValue) { foreach ($keyValuePairs as $keyValue) {
if (strpos($keyValue, '=')) { if (strpos($keyValue, '=')) {
list($headerKey, $headerValue) = preg_split('#=\s*#', $keyValue, 2); list($headerKey, $headerValue) = preg_split('#=\s*#', $keyValue, 2);
Expand Down Expand Up @@ -503,6 +504,93 @@ public function isValidForRequest($requestDomain, $path, $isSecure = false)


} }


/**
* Checks whether the cookie should be sent or not in a specific scenario
*
* @param string|Zend\Uri $uri URI to check against (secure, domain, path)
* @param boolean $matchSessionCookies Whether to send session cookies
* @param int $now Override the current time when checking for expiry time
* @return boolean
*/
public function match($uri, $matchSessionCookies = true, $now = null)
{
if (is_string ($uri)) {
$uri = Zend\Uri\UriFactory::factory($uri);
}

// Make sure we have a valid Zend_Uri_Http object
if (! ($uri->isValid() && ($uri->getScheme() == 'http' || $uri->getScheme() =='https'))) {
throw new Exception\InvalidArgumentException('Passed URI is not a valid HTTP or HTTPS URI');
}

// Check that the cookie is secure (if required) and not expired
if ($this->secure && $uri->getScheme() != 'https') return false;
if ($this->isExpired($now)) return false;
if ($this->isSessionCookie() && ! $matchSessionCookies) return false;

// Check if the domain matches
if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) {
return false;
}

// Check that path matches using prefix match
if (! self::matchCookiePath($this->getPath(), $uri->getPath())) {
return false;
}

// If we didn't die until now, return true.
return true;
}

/**
* Check if a cookie's domain matches a host name.
*
* Used by Zend\Http\Cookies for cookie matching
*
* @param string $cookieDomain
* @param string $host
*
* @return boolean
*/
public static function matchCookieDomain($cookieDomain, $host)
{
if (! $cookieDomain) {
throw new Exception\InvalidArgumentException('$cookieDomain is expected to be a cookie domain');
}

if (! $host) {
throw new Exception\InvalidArgumentException('$host is expected to be a host name');
}

$cookieDomain = strtolower($cookieDomain);
$host = strtolower($host);
// Check for either exact match or suffix match
return ($cookieDomain == $host ||
preg_match('/' . preg_quote($cookieDomain) . '$/', $host));
}

/**
* Check if a cookie's path matches a URL path
*
* Used by Zend\Http\Cookies for cookie matching
*
* @param string $cookiePath
* @param string $path
* @return boolean
*/
public static function matchCookiePath($cookiePath, $path)
{
if (! $cookiePath) {
throw new Exception\InvalidArgumentException('$cookiePath is expected to be a cookie path');
}

if (! $path) {
throw new Exception\InvalidArgumentException('$path is expected to be a host name');
}

return (strpos($path, $cookiePath) === 0);
}

public function toString() public function toString()
{ {
return 'Set-Cookie: ' . $this->getFieldValue(); return 'Set-Cookie: ' . $this->getFieldValue();
Expand Down
2 changes: 1 addition & 1 deletion library/Zend/Mvc/Router/Http/Segment.php
Expand Up @@ -41,7 +41,7 @@ class Segment implements RouteInterface
* *
* @var array * @var array
*/ */
private static $urlencodeCorrectionMap = array( protected static $urlencodeCorrectionMap = array(
'%21' => "!", // sub-delims '%21' => "!", // sub-delims
'%24' => "$", // sub-delims '%24' => "$", // sub-delims
'%26' => "&", // sub-delims '%26' => "&", // sub-delims
Expand Down
36 changes: 32 additions & 4 deletions tests/ZendTest/Http/ClientTest.php
Expand Up @@ -11,12 +11,41 @@
namespace ZendTest\Http; namespace ZendTest\Http;


use Zend\Http\Client; use Zend\Http\Client;

use Zend\Http\Cookies;
use Zend\Http\Header\AcceptEncoding; use Zend\Http\Header\AcceptEncoding;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Http\Exception;
use Zend\Http\Header\SetCookie; use Zend\Http\Header\SetCookie;


class ClientTest extends \PHPUnit_Framework_TestCase class ClientTest extends \PHPUnit_Framework_TestCase
{ {
public function testIfCookiesAreSticky()
{
$initialCookies = array(
new SetCookie('foo', 'far', null, '/', 'www.domain.com' ),
new SetCookie('bar', 'biz', null, '/', 'www.domain.com')
);

$requestString = "GET http://www.domain.com/index.php HTTP/1.1\r\nHost: domain.com\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) Gecko/20100101 Firefox/16.0\r\nAccept: */*\r\nAccept-Language: en-US,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n";
$request = Request::fromString($requestString);

$client = new Client('http://www.domain.com/');
$client->setRequest($request);
$client->addCookie($initialCookies);

$cookies = new Cookies($client->getRequest()->getHeaders());
$rawHeaders = "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nContent-Encoding: gzip\r\nContent-Type: application/javascript\r\nDate: Sun, 18 Nov 2012 16:16:08 GMT\r\nServer: nginx/1.1.19\r\nSet-Cookie: baz=bah; domain=www.domain.com; path=/\r\nSet-Cookie: joe=test; domain=www.domain.com; path=/\r\nVary: Accept-Encoding\r\nX-Powered-By: PHP/5.3.10-1ubuntu3.4\r\nConnection: keep-alive\r\n";
$response = Response::fromString($rawHeaders);
$client->setResponse($response);

$cookies->addCookiesFromResponse($client->getResponse(), $client->getUri());

$client->addCookie( $cookies->getMatchingCookies($client->getUri()) );

$this->assertEquals(4, count($client->getCookies()));
}

public function testClientRetrievesUppercaseHttpMethodFromRequestObject() public function testClientRetrievesUppercaseHttpMethodFromRequestObject()
{ {
$client = new Client; $client = new Client;
Expand All @@ -43,14 +72,13 @@ public function testIfNullValueCookiesThrowsException()


public function testIfCookieHeaderCanBeSet() public function testIfCookieHeaderCanBeSet()
{ {
$header = new SetCookie('foo'); $header = array(new SetCookie('foo', 'bar'));

$client = new Client(); $client = new Client();
$client->addCookie($header); $client->addCookie($header);


$cookies = $client->getCookies(); $cookies = $client->getCookies();
$this->assertEquals(1, count($cookies)); $this->assertEquals(1, count($cookies));
$this->assertEquals($header, $cookies['foo']); $this->assertEquals($header[0], $cookies['foo']);
} }


public function testIfArrayOfHeadersCanBeSet() public function testIfArrayOfHeadersCanBeSet()
Expand Down

0 comments on commit 1862af4

Please sign in to comment.