Skip to content
This repository

Feature/cookies refactor #3093

Closed
wants to merge 14 commits into from

2 participants

Joseph Crawford Matthew Weier O'Phinney
Joseph Crawford

Work related to

#2572 - Zend\Http\Client\CookieJar is broken
#3044 - Extending Zend\Mvc\Router\Http\Segment causes error

Deleted user Unknown referenced this pull request from a commit
Matthew Weier O'Phinney weierophinney Merge branch 'hotfix/3093' into develop
Close #3093
Fixes #2572
215b524
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
11 library/Zend/Http/Client.php
@@ -497,11 +497,11 @@ public function addCookie($cookie, $value = null, $expire = null, $path = null,
497 497 throw new Exception\InvalidArgumentException('The cookie parameter is not a valid Set-Cookie type');
498 498 }
499 499 }
500   - } elseif ($cookie instanceof Header\SetCookie) {
501   - $this->cookies[$this->getCookieId($cookie)] = $cookie;
502 500 } elseif (is_string($cookie) && $value !== null) {
503 501 $setCookie = new Header\SetCookie($cookie, $value, $expire, $path, $domain, $secure, $httponly, $maxAge, $version);
504 502 $this->cookies[$this->getCookieId($setCookie)] = $setCookie;
  503 + } elseif ($cookie instanceof Header\SetCookie) {
  504 + $this->cookies[$this->getCookieId($cookie)] = $cookie;
505 505 } else {
506 506 throw new Exception\InvalidArgumentException('Invalid parameter type passed as Cookie');
507 507 }
@@ -883,9 +883,9 @@ public function send(Request $request = null)
883 883 }
884 884
885 885 // Get the cookies from response (if any)
886   - $setCookie = $response->getCookie();
887   - if (!empty($setCookie)) {
888   - $this->addCookie($setCookie);
  886 + $setCookies = $response->getCookie();
  887 + if (!empty($setCookies)) {
  888 + $this->addCookie($setCookies);
889 889 }
890 890
891 891 // If we got redirected, look for the Location header
@@ -1322,7 +1322,6 @@ protected function doRequest(Http $uri, $method, $secure = false, $headers = arr
1322 1322 throw new Exception\RuntimeException('Adapter does not support streaming');
1323 1323 }
1324 1324 }
1325   -
1326 1325 // HTTP connection
1327 1326 $this->lastRawRequest = $this->adapter->write($method,
1328 1327 $uri, $this->config['httpversion'], $headers, $body);
3  library/Zend/Http/Client/Adapter/Curl.php
@@ -374,6 +374,7 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $bo
374 374 foreach ($headers as $key => $value) {
375 375 $curlHeaders[] = $key . ': ' . $value;
376 376 }
  377 +
377 378 curl_setopt($this->curl, CURLOPT_HTTPHEADER, $curlHeaders);
378 379
379 380 /**
@@ -409,8 +410,8 @@ public function write($method, $uri, $httpVersion = 1.1, $headers = array(), $bo
409 410 }
410 411
411 412 // send the request
412   - $response = curl_exec($this->curl);
413 413
  414 + $response = curl_exec($this->curl);
414 415 // if we used streaming, headers are already there
415 416 if (!is_resource($this->outputStream)) {
416 417 $this->response = $response;
59 library/Zend/Http/Cookies.php
@@ -10,10 +10,11 @@
10 10
11 11 namespace Zend\Http;
12 12
13   -use Zend\Http\Header\Cookie;
  13 +use Zend\Http\Header\SetCookie;
14 14 use Zend\Http\Response;
15 15 use Zend\Uri;
16 16
  17 +
17 18 /**
18 19 * A Zend_Http_CookieJar object is designed to contain and maintain HTTP cookies, and should
19 20 * be used along with Zend_Http_Client in order to manage cookies across HTTP requests and
@@ -38,6 +39,36 @@
38 39 class Cookies extends Headers
39 40 {
40 41 /**
  42 + * Return cookie(s) as a Zend_Http_Cookie object
  43 + *
  44 + */
  45 + const COOKIE_OBJECT = 0;
  46 +
  47 + /**
  48 + * Return cookie(s) as a string (suitable for sending in an HTTP request)
  49 + *
  50 + */
  51 + const COOKIE_STRING_ARRAY = 1;
  52 +
  53 + /**
  54 + * Return all cookies as one long string (suitable for sending in an HTTP request)
  55 + *
  56 + */
  57 + const COOKIE_STRING_CONCAT = 2;
  58 +
  59 + /**
  60 + * Return all cookies as one long string (strict mode)
  61 + * - Single space after the semi-colon separating each cookie
  62 + * - Remove trailing semi-colon, if any
  63 + */
  64 + const COOKIE_STRING_CONCAT_STRICT = 3;
  65 +
  66 + /**
  67 + * @var \Zend\Http\Cookies
  68 + */
  69 + protected $cookies = array();
  70 +
  71 + /**
41 72 * @var \Zend\Http\Headers
42 73 */
43 74 protected $headers = null;
@@ -70,17 +101,17 @@ public function __construct(Headers $headers)
70 101 * Add a cookie to the class. Cookie should be passed either as a Zend\Http\Header\Cookie object
71 102 * or as a string - in which case an object is created from the string.
72 103 *
73   - * @param Cookie|string $cookie
  104 + * @param SetCookie|string $cookie
74 105 * @param Uri\Uri|string $ref_uri Optional reference URI (for domain, path, secure)
75 106 * @throws Exception\InvalidArgumentException
76 107 */
77   - public function addCookie(Cookie $cookie, $ref_uri = null)
  108 + public function addCookie( $cookie, $ref_uri = null)
78 109 {
79 110 if (is_string($cookie)) {
80   - $cookie = Cookie::fromString($cookie, $ref_uri);
  111 + $cookie = SetCookie::fromString($cookie, $ref_uri);
81 112 }
82 113
83   - if ($cookie instanceof Cookie) {
  114 + if ($cookie instanceof SetCookie) {
84 115 $domain = $cookie->getDomain();
85 116 $path = $cookie->getPath();
86 117 if (!isset($this->cookies[$domain])) {
@@ -106,7 +137,7 @@ public function addCookiesFromResponse(Response $response, $ref_uri)
106 137 {
107 138 $cookie_hdrs = $response->getHeaders()->get('Set-Cookie');
108 139
109   - if (is_array($cookie_hdrs)) {
  140 + if (is_array($cookie_hdrs) || $cookie_hdrs instanceof \ArrayIterator) {
110 141 foreach ($cookie_hdrs as $cookie) {
111 142 $this->addCookie($cookie, $ref_uri);
112 143 }
@@ -118,7 +149,7 @@ public function addCookiesFromResponse(Response $response, $ref_uri)
118 149 /**
119 150 * Get all cookies in the cookie jar as an array
120 151 *
121   - * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings
  152 + * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
122 153 * @return array|string
123 154 */
124 155 public function getAllCookies($ret_as = self::COOKIE_OBJECT)
@@ -134,7 +165,7 @@ public function getAllCookies($ret_as = self::COOKIE_OBJECT)
134 165 *
135 166 * @param string|Uri\Uri $uri URI to check against (secure, domain, path)
136 167 * @param boolean $matchSessionCookies Whether to send session cookies
137   - * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings
  168 + * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
138 169 * @param int $now Override the current time when checking for expiry time
139 170 * @throws Exception\InvalidArgumentException if invalid URI specified
140 171 * @return array|string
@@ -175,9 +206,9 @@ public function getMatchingCookies($uri, $matchSessionCookies = true,
175 206 *
176 207 * @param Uri\Uri|string $uri The uri (domain and path) to match
177 208 * @param string $cookie_name The cookie's name
178   - * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\Cookie or as strings
  209 + * @param int $ret_as Whether to return cookies as objects of \Zend\Http\Header\SetCookie or as strings
179 210 * @throws Exception\InvalidArgumentException if invalid URI specified or invalid $ret_as value
180   - * @return Cookie|string
  211 + * @return SetCookie|string
181 212 */
182 213 public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT)
183 214 {
@@ -223,7 +254,7 @@ public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT)
223 254 * Helper function to recursively flatten an array. Should be used when exporting the
224 255 * cookies array (or parts of it)
225 256 *
226   - * @param \Zend\Http\Header\Cookie|array $ptr
  257 + * @param \Zend\Http\Header\SetCookie|array $ptr
227 258 * @param int $ret_as What value to return
228 259 * @return array|string
229 260 */
@@ -239,7 +270,7 @@ protected function _flattenCookiesArray($ptr, $ret_as = self::COOKIE_OBJECT)
239 270 }
240 271 }
241 272 return $ret;
242   - } elseif ($ptr instanceof Cookie) {
  273 + } elseif ($ptr instanceof SetCookie) {
243 274 switch ($ret_as) {
244 275 case self::COOKIE_STRING_ARRAY:
245 276 return array($ptr->__toString());
@@ -270,7 +301,7 @@ protected function _matchDomain($domain)
270 301 $ret = array();
271 302
272 303 foreach (array_keys($this->cookies) as $cdom) {
273   - if (Cookie::matchCookieDomain($cdom, $domain)) {
  304 + if (SetCookie::matchCookieDomain($cdom, $domain)) {
274 305 $ret[$cdom] = $this->cookies[$cdom];
275 306 }
276 307 }
@@ -291,7 +322,7 @@ protected function _matchPath($domains, $path)
291 322
292 323 foreach ($domains as $dom => $paths_array) {
293 324 foreach (array_keys($paths_array) as $cpath) {
294   - if (Cookie::matchCookiePath($cpath, $path)) {
  325 + if (SetCookie::matchCookiePath($cpath, $path)) {
295 326 if (! isset($ret[$dom])) {
296 327 $ret[$dom] = array();
297 328 }
88 library/Zend/Http/Header/SetCookie.php
@@ -98,6 +98,7 @@ public static function fromString($headerLine, $bypassHeaderFieldName = false)
98 98 $setCookieProcessor = function($headerLine) use ($setCookieClass) {
99 99 $header = new $setCookieClass;
100 100 $keyValuePairs = preg_split('#;\s*#', $headerLine);
  101 +
101 102 foreach ($keyValuePairs as $keyValue) {
102 103 if (strpos($keyValue, '=')) {
103 104 list($headerKey, $headerValue) = preg_split('#=\s*#', $keyValue, 2);
@@ -503,6 +504,93 @@ public function isValidForRequest($requestDomain, $path, $isSecure = false)
503 504
504 505 }
505 506
  507 + /**
  508 + * Checks whether the cookie should be sent or not in a specific scenario
  509 + *
  510 + * @param string|Zend\Uri $uri URI to check against (secure, domain, path)
  511 + * @param boolean $matchSessionCookies Whether to send session cookies
  512 + * @param int $now Override the current time when checking for expiry time
  513 + * @return boolean
  514 + */
  515 + public function match($uri, $matchSessionCookies = true, $now = null)
  516 + {
  517 + if (is_string ($uri)) {
  518 + $uri = Zend\Uri\UriFactory::factory($uri);
  519 + }
  520 +
  521 + // Make sure we have a valid Zend_Uri_Http object
  522 + if (! ($uri->isValid() && ($uri->getScheme() == 'http' || $uri->getScheme() =='https'))) {
  523 + throw new Exception\InvalidArgumentException('Passed URI is not a valid HTTP or HTTPS URI');
  524 + }
  525 +
  526 + // Check that the cookie is secure (if required) and not expired
  527 + if ($this->secure && $uri->getScheme() != 'https') return false;
  528 + if ($this->isExpired($now)) return false;
  529 + if ($this->isSessionCookie() && ! $matchSessionCookies) return false;
  530 +
  531 + // Check if the domain matches
  532 + if (! self::matchCookieDomain($this->getDomain(), $uri->getHost())) {
  533 + return false;
  534 + }
  535 +
  536 + // Check that path matches using prefix match
  537 + if (! self::matchCookiePath($this->getPath(), $uri->getPath())) {
  538 + return false;
  539 + }
  540 +
  541 + // If we didn't die until now, return true.
  542 + return true;
  543 + }
  544 +
  545 + /**
  546 + * Check if a cookie's domain matches a host name.
  547 + *
  548 + * Used by Zend\Http\Cookies for cookie matching
  549 + *
  550 + * @param string $cookieDomain
  551 + * @param string $host
  552 + *
  553 + * @return boolean
  554 + */
  555 + public static function matchCookieDomain($cookieDomain, $host)
  556 + {
  557 + if (! $cookieDomain) {
  558 + throw new Exception\InvalidArgumentException('$cookieDomain is expected to be a cookie domain');
  559 + }
  560 +
  561 + if (! $host) {
  562 + throw new Exception\InvalidArgumentException('$host is expected to be a host name');
  563 + }
  564 +
  565 + $cookieDomain = strtolower($cookieDomain);
  566 + $host = strtolower($host);
  567 + // Check for either exact match or suffix match
  568 + return ($cookieDomain == $host ||
  569 + preg_match('/' . preg_quote($cookieDomain) . '$/', $host));
  570 + }
  571 +
  572 + /**
  573 + * Check if a cookie's path matches a URL path
  574 + *
  575 + * Used by Zend\Http\Cookies for cookie matching
  576 + *
  577 + * @param string $cookiePath
  578 + * @param string $path
  579 + * @return boolean
  580 + */
  581 + public static function matchCookiePath($cookiePath, $path)
  582 + {
  583 + if (! $cookiePath) {
  584 + throw new Exception\InvalidArgumentException('$cookiePath is expected to be a cookie path');
  585 + }
  586 +
  587 + if (! $path) {
  588 + throw new Exception\InvalidArgumentException('$path is expected to be a host name');
  589 + }
  590 +
  591 + return (strpos($path, $cookiePath) === 0);
  592 + }
  593 +
506 594 public function toString()
507 595 {
508 596 return 'Set-Cookie: ' . $this->getFieldValue();
2  library/Zend/Mvc/Router/Http/Segment.php
@@ -41,7 +41,7 @@ class Segment implements RouteInterface
41 41 *
42 42 * @var array
43 43 */
44   - private static $urlencodeCorrectionMap = array(
  44 + protected static $urlencodeCorrectionMap = array(
45 45 '%21' => "!", // sub-delims
46 46 '%24' => "$", // sub-delims
47 47 '%26' => "&", // sub-delims
34 tests/ZendTest/Http/ClientTest.php
@@ -11,11 +11,40 @@
11 11 namespace ZendTest\Http;
12 12
13 13 use Zend\Http\Client;
  14 +use Zend\Http\Cookies;
  15 +use Zend\Http\Request;
  16 +use Zend\Http\Response;
14 17 use Zend\Http\Exception;
15 18 use Zend\Http\Header\SetCookie;
16 19
17 20 class ClientTest extends \PHPUnit_Framework_TestCase
18 21 {
  22 + public function testIfCookiesAreSticky()
  23 + {
  24 + $initialCookies = array(
  25 + new SetCookie('foo', 'far', null, '/', 'www.domain.com' ),
  26 + new SetCookie('bar', 'biz', null, '/', 'www.domain.com')
  27 + );
  28 +
  29 + $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";
  30 + $request = Request::fromString($requestString);
  31 +
  32 + $client = new Client('http://www.domain.com/');
  33 + $client->setRequest($request);
  34 + $client->addCookie($initialCookies);
  35 +
  36 + $cookies = new Cookies($client->getRequest()->getHeaders());
  37 + $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";
  38 + $response = Response::fromString($rawHeaders);
  39 + $client->setResponse($response);
  40 +
  41 + $cookies->addCookiesFromResponse($client->getResponse(), $client->getUri());
  42 +
  43 + $client->addCookie( $cookies->getMatchingCookies($client->getUri()) );
  44 +
  45 + $this->assertEquals(4, count($client->getCookies()));
  46 +}
  47 +
19 48 public function testClientRetrievesUppercaseHttpMethodFromRequestObject()
20 49 {
21 50 $client = new Client;
@@ -42,14 +71,13 @@ public function testIfNullValueCookiesThrowsException()
42 71
43 72 public function testIfCookieHeaderCanBeSet()
44 73 {
45   - $header = new SetCookie('foo');
46   -
  74 + $header = array(new SetCookie('foo', 'bar'));
47 75 $client = new Client();
48 76 $client->addCookie($header);
49 77
50 78 $cookies = $client->getCookies();
51 79 $this->assertEquals(1, count($cookies));
52   - $this->assertEquals($header, $cookies['foo']);
  80 + $this->assertEquals($header[0], $cookies['foo']);
53 81 }
54 82
55 83 public function testIfArrayOfHeadersCanBeSet()

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.