Permalink
Browse files

Merge branch 'feature/cookies-refactor' of git://github.com/jcrawford…

…/zf2 into hotfix/3093

Conflicts:
	tests/ZendTest/Http/ClientTest.php
  • Loading branch information...
2 parents 36a4c07 + d2117d0 commit a96d8726cc5056617c0b495c061cc2b7e98daa67 @weierophinney weierophinney committed Dec 11, 2012
@@ -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');
}
}
- } elseif ($cookie instanceof Header\SetCookie) {
- $this->cookies[$this->getCookieId($cookie)] = $cookie;
} elseif (is_string($cookie) && $value !== null) {
$setCookie = new Header\SetCookie($cookie, $value, $expire, $path, $domain, $secure, $httponly, $maxAge, $version);
$this->cookies[$this->getCookieId($setCookie)] = $setCookie;
+ } elseif ($cookie instanceof Header\SetCookie) {
+ $this->cookies[$this->getCookieId($cookie)] = $cookie;
} else {
throw new Exception\InvalidArgumentException('Invalid parameter type passed as Cookie');
}
@@ -883,9 +883,9 @@ public function send(Request $request = null)
}
// Get the cookies from response (if any)
- $setCookie = $response->getCookie();
- if (!empty($setCookie)) {
- $this->addCookie($setCookie);
+ $setCookies = $response->getCookie();
+ if (!empty($setCookies)) {
+ $this->addCookie($setCookies);
}
// If we got redirected, look for the Location header
@@ -1322,7 +1322,6 @@ protected function doRequest(Http $uri, $method, $secure = false, $headers = arr
throw new Exception\RuntimeException('Adapter does not support streaming');
}
}
-
// HTTP connection
$this->lastRawRequest = $this->adapter->write($method,
$uri, $this->config['httpversion'], $headers, $body);
@@ -374,6 +374,7 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $bo
foreach ($headers as $key => $value) {
$curlHeaders[] = $key . ': ' . $value;
}
+
curl_setopt($this->curl, CURLOPT_HTTPHEADER, $curlHeaders);
/**
@@ -409,8 +410,8 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $bo
}
// send the request
- $response = curl_exec($this->curl);
+ $response = curl_exec($this->curl);
// if we used streaming, headers are already there
if (!is_resource($this->outputStream)) {
$this->response = $response;
@@ -10,10 +10,11 @@
namespace Zend\Http;
-use Zend\Http\Header\Cookie;
+use Zend\Http\Header\SetCookie;
use Zend\Http\Response;
use Zend\Uri;
+
/**
* 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
@@ -38,6 +39,36 @@
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
*/
protected $headers = null;
@@ -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
* 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)
* @throws Exception\InvalidArgumentException
*/
- public function addCookie(Cookie $cookie, $ref_uri = null)
+ public function addCookie( $cookie, $ref_uri = null)
{
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();
$path = $cookie->getPath();
if (!isset($this->cookies[$domain])) {
@@ -106,7 +137,7 @@ public function addCookiesFromResponse(Response $response, $ref_uri)
{
$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) {
$this->addCookie($cookie, $ref_uri);
}
@@ -118,7 +149,7 @@ public function addCookiesFromResponse(Response $response, $ref_uri)
/**
* 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
*/
public function getAllCookies($ret_as = self::COOKIE_OBJECT)
@@ -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 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
* @throws Exception\InvalidArgumentException if invalid URI specified
* @return array|string
@@ -175,9 +206,9 @@ public function getMatchingCookies($uri, $matchSessionCookies = true,
*
* @param Uri\Uri|string $uri The uri (domain and path) to match
* @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
- * @return Cookie|string
+ * @return SetCookie|string
*/
public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT)
{
@@ -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
* 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
* @return array|string
*/
@@ -239,7 +270,7 @@ protected function _flattenCookiesArray($ptr, $ret_as = self::COOKIE_OBJECT)
}
}
return $ret;
- } elseif ($ptr instanceof Cookie) {
+ } elseif ($ptr instanceof SetCookie) {
switch ($ret_as) {
case self::COOKIE_STRING_ARRAY:
return array($ptr->__toString());
@@ -270,7 +301,7 @@ protected function _matchDomain($domain)
$ret = array();
foreach (array_keys($this->cookies) as $cdom) {
- if (Cookie::matchCookieDomain($cdom, $domain)) {
+ if (SetCookie::matchCookieDomain($cdom, $domain)) {
$ret[$cdom] = $this->cookies[$cdom];
}
}
@@ -291,7 +322,7 @@ protected function _matchPath($domains, $path)
foreach ($domains as $dom => $paths_array) {
foreach (array_keys($paths_array) as $cpath) {
- if (Cookie::matchCookiePath($cpath, $path)) {
+ if (SetCookie::matchCookiePath($cpath, $path)) {
if (! isset($ret[$dom])) {
$ret[$dom] = array();
}
@@ -98,6 +98,7 @@ public static function fromString($headerLine, $bypassHeaderFieldName = false)
$setCookieProcessor = function ($headerLine) use ($setCookieClass) {
$header = new $setCookieClass;
$keyValuePairs = preg_split('#;\s*#', $headerLine);
+
foreach ($keyValuePairs as $keyValue) {
if (strpos($keyValue, '=')) {
list($headerKey, $headerValue) = preg_split('#=\s*#', $keyValue, 2);
@@ -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()
{
return 'Set-Cookie: ' . $this->getFieldValue();
@@ -41,7 +41,7 @@ class Segment implements RouteInterface
*
* @var array
*/
- private static $urlencodeCorrectionMap = array(
+ protected static $urlencodeCorrectionMap = array(
'%21' => "!", // sub-delims
'%24' => "$", // sub-delims
'%26' => "&", // sub-delims
@@ -11,12 +11,41 @@
namespace ZendTest\Http;
use Zend\Http\Client;
-
+use Zend\Http\Cookies;
use Zend\Http\Header\AcceptEncoding;
+use Zend\Http\Request;
+use Zend\Http\Response;
+use Zend\Http\Exception;
use Zend\Http\Header\SetCookie;
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()
{
$client = new Client;
@@ -43,14 +72,13 @@ public function testIfNullValueCookiesThrowsException()
public function testIfCookieHeaderCanBeSet()
{
- $header = new SetCookie('foo');
-
+ $header = array(new SetCookie('foo', 'bar'));
$client = new Client();
$client->addCookie($header);
$cookies = $client->getCookies();
$this->assertEquals(1, count($cookies));
- $this->assertEquals($header, $cookies['foo']);
+ $this->assertEquals($header[0], $cookies['foo']);
}
public function testIfArrayOfHeadersCanBeSet()

0 comments on commit a96d872

Please sign in to comment.