From c53bc1998f0cfe06905336aab6a7c389370b9273 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Sun, 18 Aug 2013 18:24:22 +1000 Subject: [PATCH 01/13] First pass at cookie support Still needs tests. --- library/Requests.php | 10 ++ library/Requests/Cookie.php | 158 ++++++++++++++++++++++++++++++++ library/Requests/Cookie/Jar.php | 144 +++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 library/Requests/Cookie.php create mode 100644 library/Requests/Cookie/Jar.php diff --git a/library/Requests.php b/library/Requests.php index d58c81aa0..2cdbfabb3 100755 --- a/library/Requests.php +++ b/library/Requests.php @@ -344,6 +344,8 @@ public static function request($url, $headers = array(), $data = array(), $type * - `data`: Associative array of options. Same as the `$options` parameter * to {@see Requests::request} * (array, default: see {@see Requests::request}) + * - `cookies`: Associative array of cookie name to value, or cookie jar. + * (array|Requests_Cookie_Jar) * * If the `$options` parameter is specified, individual requests will * inherit options from it. This can be used to use a single hooking system, @@ -447,6 +449,7 @@ protected static function get_default_options($multirequest = false) { 'type' => self::GET, 'filename' => false, 'auth' => false, + 'cookies' => false, 'idn' => true, 'hooks' => null, 'transport' => null, @@ -485,6 +488,13 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio $options['auth']->register($options['hooks']); } + if (is_array($options['cookies'])) { + $options['cookies'] = new Requests_Cookie_Jar($options['cookies']); + } + if ($options['cookies'] !== false) { + $options['cookies']->register($options['hooks']); + } + if ($options['idn'] !== false) { $iri = new Requests_IRI($url); $iri->host = Requests_IDNAEncoder::encode($iri->ihost); diff --git a/library/Requests/Cookie.php b/library/Requests/Cookie.php new file mode 100644 index 000000000..1f0aa983b --- /dev/null +++ b/library/Requests/Cookie.php @@ -0,0 +1,158 @@ +name = $name; + $this->value = $value; + $this->attributes = $attributes; + } + + /** + * Format a cookie for a Cookie header + * + * This is used when sending cookies to a server. + * + * @return string Cookie formatted for Cookie header + */ + public function formatForHeader() { + return sprintf('%s=%s', $this->name, $this->value); + } + + /** + * Format a cookie for a Set-Cookie header + * + * This is used when sending cookies to clients. This isn't really + * applicable to client-side usage, but might be handy for debugging. + * + * @return string Cookie formatted for Set-Cookie header + */ + public function formatForSetCookie() { + $header_value = $this->formatForHeader(); + if (!empty($this->attributes)) { + $parts = array(); + foreach ($this->attributes as $key => $value) { + $parts[] = sprintf('%s=%s', $key, $value); + } + + $header_value .= '; ' . implode('; ', $parts); + } + return $header_value; + } + + /** + * Get the cookie value + * + * Attributes and other data can be accessed via methods. + */ + public function __toString() { + return $value; + } + + /** + * Parse a cookie string into a cookie object + * + * Based on Mozilla's parsing code in Firefox and related projects, which + * is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265 + * specifies some of this handling, but not in a thorough manner. + * + * @param string Cookie header value (from a Set-Cookie header) + * @return Requests_Cookie Parsed cookie object + */ + public static function parse($string) { + $parts = explode(';', $string); + $kvparts = array_shift($parts); + + // Some sites might only have a value without the equals separator. + // Deviate from RFC 6265 and pretend it was actually a blank name + // (`=foo`) + // + // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 + if (strpos($kvparts, '=') === false) { + $name = ''; + $value = trim($kvparts); + } + else { + list($name, $value) = explode('=', $kvparts, 2); + } + + // Attribute key are handled case-insensitively + $attributes = new Requests_Utility_CaseInsensitiveDictionary(); + + if (!empty($parts)) { + foreach ($parts as $part) { + if (strpos($part, '=') === false) { + $part_key = $part; + $part_value = true; + } + else { + list($part_key, $part_value) = explode('=', $part, 2); + } + + $attributes[$part_key] = $part_value; + } + } + + return new Requests_Cookie($name, $value, $attributes); + } + + /** + * Parse all Set-Cookie headers from request headers + * + * @param Requests_Response_Headers $headers + * @return array + */ + public static function parseFromHeaders(Requests_Response_Headers $headers) { + $cookie_headers = $headers->getValues('Set-Cookie'); + if (empty($cookie_headers)) { + return array(); + } + + $cookies = array(); + foreach ($cookie_headers as $header) { + $parsed = self::parse($header); + $cookies[$parsed->name] = $parsed; + } + + return $cookies; + } +} diff --git a/library/Requests/Cookie/Jar.php b/library/Requests/Cookie/Jar.php new file mode 100644 index 000000000..6baf78973 --- /dev/null +++ b/library/Requests/Cookie/Jar.php @@ -0,0 +1,144 @@ +cookies = $cookies; + } + + /** + * Normalise cookie data into a Requests_Cookie + * + * @param string|Requests_Cookie $cookie + * @return Requests_Cookie + */ + public function normalizeCookie($cookie) { + if (is_a('Requests_Cookie', $cookie)) { + return $cookie; + } + + return Requests_Cookie::parse($cookie); + } + + /** + * Check if the given item exists + * + * @param string $key Item key + * @return boolean Does the item exist? + */ + public function offsetExists($key) { + return isset($this->cookies[$key]); + } + + /** + * Get the value for the item + * + * @param string $key Item key + * @return string Item value + */ + public function offsetGet($key) { + if (!isset($this->cookies[$key])) + return null; + + return $this->cookies[$key]; + } + + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $this->cookies[$key][] = $value; + } + + /** + * Unset the given header + * + * @param string $key + */ + public function offsetUnset($key) { + unset($this->data[$key]); + } + + /** + * Get an iterator for the data + * + * @return ArrayIterator + */ + public function getIterator() { + return new ArrayIterator($this->data); + } + + /** + * Register the cookie handler with the request's hooking system + * + * @param Requests_Hooker $hooks Hooking system + */ + public function register(Requests_Hooker $hooks) { + $hooks->register('requests.before_request', array($this, 'before_request')); + $hooks->register('requests.after_request', array($this, 'after_request')); + } + + /** + * Add Cookie header to a request if we have any + * + * As per RFC 6265, cookies are separated by '; ' + * + * @param string $url + * @param array $headers + * @param array $data + * @param string $type + * @param array $options + */ + public function before_request(&$url, &$headers, &$data, &$type, &$options) { + if (!empty($this->cookies)) { + $cookies = array(); + foreach ($this->cookies as $cookie) { + $cookies[] = $cookie->formatForHeader(); + } + + $headers['Cookie'] = implode('; ', $cookies); + } + } + + /** + * Parse all cookies from a response and attach them to the response + * + * @var Requests_Response $response + */ + public function after_request(Requests_Response &$return) { + $cookies = Requests_Cookie::parseFromHeaders($return->headers); + $return->cookies = new Requests_Cookie_Jar($cookies); + } +} \ No newline at end of file From ee0ea00303153196c351484accec64541ff5a352 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Sun, 18 Aug 2013 18:57:26 +1000 Subject: [PATCH 02/13] Trim all cookie keys and values --- library/Requests/Cookie.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/Requests/Cookie.php b/library/Requests/Cookie.php index 1f0aa983b..a7db6e8cd 100644 --- a/library/Requests/Cookie.php +++ b/library/Requests/Cookie.php @@ -109,11 +109,13 @@ public static function parse($string) { // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 if (strpos($kvparts, '=') === false) { $name = ''; - $value = trim($kvparts); + $value = $kvparts; } else { list($name, $value) = explode('=', $kvparts, 2); } + $name = trim($name); + $value = trim($value); // Attribute key are handled case-insensitively $attributes = new Requests_Utility_CaseInsensitiveDictionary(); @@ -128,6 +130,8 @@ public static function parse($string) { list($part_key, $part_value) = explode('=', $part, 2); } + $part_key = trim($part_key); + $part_value = trim($part_value); $attributes[$part_key] = $part_value; } } From 2be348492058b1cc20fb50dba39e24d32d08ef45 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 00:37:08 +1000 Subject: [PATCH 03/13] Ensure casting a cookie to a string uses the value --- library/Requests/Cookie.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Requests/Cookie.php b/library/Requests/Cookie.php index a7db6e8cd..cb82d714d 100644 --- a/library/Requests/Cookie.php +++ b/library/Requests/Cookie.php @@ -85,7 +85,7 @@ public function formatForSetCookie() { * Attributes and other data can be accessed via methods. */ public function __toString() { - return $value; + return $this->value; } /** From 31d2e64fb52a43e504320ea72d2f3f104583703e Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 00:37:31 +1000 Subject: [PATCH 04/13] Implement the array-like interfaces for Cookie_Jar --- library/Requests/Cookie/Jar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Requests/Cookie/Jar.php b/library/Requests/Cookie/Jar.php index 6baf78973..8967c4ff2 100644 --- a/library/Requests/Cookie/Jar.php +++ b/library/Requests/Cookie/Jar.php @@ -12,7 +12,7 @@ * @package Requests * @subpackage Cookies */ -class Requests_Cookie_Jar { +class Requests_Cookie_Jar implements ArrayAccess, IteratorAggregate { /** * Actual item data * From e7462d107135838a53527cb6db7ba25593ab31fe Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 00:38:02 +1000 Subject: [PATCH 05/13] Always attach a cookie jar to a request --- library/Requests.php | 3 +++ library/Requests/Response.php | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/library/Requests.php b/library/Requests.php index 2cdbfabb3..f09d71c17 100755 --- a/library/Requests.php +++ b/library/Requests.php @@ -491,6 +491,9 @@ protected static function set_defaults(&$url, &$headers, &$data, &$type, &$optio if (is_array($options['cookies'])) { $options['cookies'] = new Requests_Cookie_Jar($options['cookies']); } + elseif (empty($options['cookies'])) { + $options['cookies'] = new Requests_Cookie_Jar(); + } if ($options['cookies'] !== false) { $options['cookies']->register($options['hooks']); } diff --git a/library/Requests/Response.php b/library/Requests/Response.php index 4cc4c6a8b..684d2d6c3 100755 --- a/library/Requests/Response.php +++ b/library/Requests/Response.php @@ -68,6 +68,11 @@ public function __construct() { */ public $history = array(); + /** + * Cookies from the request + */ + public $cookies = array(); + /** * Throws an exception if the request was not successful * From ffdcc799eb08015058a6edb705e0577f190d7cb5 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 00:58:01 +1000 Subject: [PATCH 06/13] Use the correct jar property --- library/Requests/Cookie/Jar.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Requests/Cookie/Jar.php b/library/Requests/Cookie/Jar.php index 8967c4ff2..91c985098 100644 --- a/library/Requests/Cookie/Jar.php +++ b/library/Requests/Cookie/Jar.php @@ -88,7 +88,7 @@ public function offsetSet($key, $value) { * @param string $key */ public function offsetUnset($key) { - unset($this->data[$key]); + unset($this->cookies[$key]); } /** @@ -97,7 +97,7 @@ public function offsetUnset($key) { * @return ArrayIterator */ public function getIterator() { - return new ArrayIterator($this->data); + return new ArrayIterator($this->cookies); } /** From 996a32c22edd977e2c5adec0aa6f89d5e8a596ae Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 01:31:41 +1000 Subject: [PATCH 07/13] Use an existing key if we have one This allows passing in [ 'a' => 'b' ] rather than just [ 'a=b' ] --- library/Requests/Cookie.php | 17 ++++++++++------- library/Requests/Cookie/Jar.php | 7 ++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/library/Requests/Cookie.php b/library/Requests/Cookie.php index cb82d714d..0f73e4339 100644 --- a/library/Requests/Cookie.php +++ b/library/Requests/Cookie.php @@ -98,16 +98,19 @@ public function __toString() { * @param string Cookie header value (from a Set-Cookie header) * @return Requests_Cookie Parsed cookie object */ - public static function parse($string) { + public static function parse($string, $name = '') { $parts = explode(';', $string); $kvparts = array_shift($parts); - // Some sites might only have a value without the equals separator. - // Deviate from RFC 6265 and pretend it was actually a blank name - // (`=foo`) - // - // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 - if (strpos($kvparts, '=') === false) { + if (!empty($name)) { + $value = $string; + } + elseif (strpos($kvparts, '=') === false) { + // Some sites might only have a value without the equals separator. + // Deviate from RFC 6265 and pretend it was actually a blank name + // (`=foo`) + // + // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 $name = ''; $value = $kvparts; } diff --git a/library/Requests/Cookie/Jar.php b/library/Requests/Cookie/Jar.php index 91c985098..2879eadbe 100644 --- a/library/Requests/Cookie/Jar.php +++ b/library/Requests/Cookie/Jar.php @@ -35,12 +35,12 @@ public function __construct($cookies = array()) { * @param string|Requests_Cookie $cookie * @return Requests_Cookie */ - public function normalizeCookie($cookie) { + public function normalizeCookie($cookie, $key = null) { if (is_a('Requests_Cookie', $cookie)) { return $cookie; } - return Requests_Cookie::parse($cookie); + return Requests_Cookie::parse($cookie, $key); } /** @@ -124,7 +124,8 @@ public function register(Requests_Hooker $hooks) { public function before_request(&$url, &$headers, &$data, &$type, &$options) { if (!empty($this->cookies)) { $cookies = array(); - foreach ($this->cookies as $cookie) { + foreach ($this->cookies as $key => $cookie) { + $cookie = $this->normalizeCookie($cookie, $key); $cookies[] = $cookie->formatForHeader(); } From 3dbca26a030fd4d9179afe3235caf91effced2b2 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 01:32:10 +1000 Subject: [PATCH 08/13] Store cookies in a flat array I was a little overzealous here; cookies can only have a single value. --- library/Requests/Cookie/Jar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Requests/Cookie/Jar.php b/library/Requests/Cookie/Jar.php index 2879eadbe..495bf53e2 100644 --- a/library/Requests/Cookie/Jar.php +++ b/library/Requests/Cookie/Jar.php @@ -79,7 +79,7 @@ public function offsetSet($key, $value) { throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); } - $this->cookies[$key][] = $value; + $this->cookies[$key] = $value; } /** From 3d05c4c166ac1cf6792549419c176335913b708f Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 01:38:24 +1000 Subject: [PATCH 09/13] Persist cookies between requests Adds a new 'requests.before_redirect_check' hook to allow for this. --- library/Requests.php | 2 ++ library/Requests/Cookie/Jar.php | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/library/Requests.php b/library/Requests.php index f09d71c17..723cf00bd 100755 --- a/library/Requests.php +++ b/library/Requests.php @@ -573,6 +573,8 @@ protected static function parse_response($headers, $url, $req_headers, $req_data unset($return->headers['connection']); } + $options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options)); + if ((in_array($return->status_code, array(300, 301, 302, 303, 307)) || $return->status_code > 307 && $return->status_code < 400) && $options['follow_redirects'] === true) { if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) { if ($return->status_code === 303) { diff --git a/library/Requests/Cookie/Jar.php b/library/Requests/Cookie/Jar.php index 495bf53e2..2b4d8e36c 100644 --- a/library/Requests/Cookie/Jar.php +++ b/library/Requests/Cookie/Jar.php @@ -107,7 +107,7 @@ public function getIterator() { */ public function register(Requests_Hooker $hooks) { $hooks->register('requests.before_request', array($this, 'before_request')); - $hooks->register('requests.after_request', array($this, 'after_request')); + $hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check')); } /** @@ -138,8 +138,9 @@ public function before_request(&$url, &$headers, &$data, &$type, &$options) { * * @var Requests_Response $response */ - public function after_request(Requests_Response &$return) { + public function before_redirect_check(Requests_Response &$return) { $cookies = Requests_Cookie::parseFromHeaders($return->headers); - $return->cookies = new Requests_Cookie_Jar($cookies); + $this->cookies = array_merge($this->cookies, $cookies); + $return->cookies = $this; } } \ No newline at end of file From 72369f70c9e8d6253c3a0e4cf0f884bfd45dee18 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 01:43:28 +1000 Subject: [PATCH 10/13] Use instanceof instead of is_a is_a was deprecated from 5.0-5.3, plus it takes parameters in the opposite order to what the code previously had. --- library/Requests/Cookie/Jar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Requests/Cookie/Jar.php b/library/Requests/Cookie/Jar.php index 2b4d8e36c..6d2f53f5a 100644 --- a/library/Requests/Cookie/Jar.php +++ b/library/Requests/Cookie/Jar.php @@ -36,7 +36,7 @@ public function __construct($cookies = array()) { * @return Requests_Cookie */ public function normalizeCookie($cookie, $key = null) { - if (is_a('Requests_Cookie', $cookie)) { + if ($cookie instanceof Requests_Cookie) { return $cookie; } From 49c9fd4ce46f2bf02aae962aecf8b51f5d52e568 Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 02:31:07 +1000 Subject: [PATCH 11/13] Properly parse non-associative cookie attributes Output too. --- library/Requests/Cookie.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/library/Requests/Cookie.php b/library/Requests/Cookie.php index 0f73e4339..365fad893 100644 --- a/library/Requests/Cookie.php +++ b/library/Requests/Cookie.php @@ -71,7 +71,13 @@ public function formatForSetCookie() { if (!empty($this->attributes)) { $parts = array(); foreach ($this->attributes as $key => $value) { - $parts[] = sprintf('%s=%s', $key, $value); + // Ignore non-associative attributes + if (is_numeric($key)) { + $parts[] = $value; + } + else { + $parts[] = sprintf('%s=%s', $key, $value); + } } $header_value .= '; ' . implode('; ', $parts); @@ -131,10 +137,10 @@ public static function parse($string, $name = '') { } else { list($part_key, $part_value) = explode('=', $part, 2); + $part_value = trim($part_value); } $part_key = trim($part_key); - $part_value = trim($part_value); $attributes[$part_key] = $part_value; } } From 136fee6fbbcd49a1e6c09359de58cf2a2892395f Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 02:31:58 +1000 Subject: [PATCH 12/13] Change CaseInsensitiveDictionary to be flat Moves all multiple-value handling back to the headers class. --- library/Requests/Response/Headers.php | 22 +++++++++++++++++++ .../Utility/CaseInsensitiveDictionary.php | 7 +----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/library/Requests/Response/Headers.php b/library/Requests/Response/Headers.php index 8db6a0073..aa9072560 100755 --- a/library/Requests/Response/Headers.php +++ b/library/Requests/Response/Headers.php @@ -31,6 +31,28 @@ public function offsetGet($key) { return $this->flatten($this->data[$key]); } + /** + * Set the given item + * + * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) + * + * @param string $key Item name + * @param string $value Item value + */ + public function offsetSet($key, $value) { + if ($key === null) { + throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); + } + + $key = strtolower($key); + + if (!isset($this->data[$key])) { + $this->data[$key] = array(); + } + + $this->data[$key][] = $value; + } + /** * Get all values for a given header * diff --git a/library/Requests/Utility/CaseInsensitiveDictionary.php b/library/Requests/Utility/CaseInsensitiveDictionary.php index a00dc6a62..f6e14966e 100644 --- a/library/Requests/Utility/CaseInsensitiveDictionary.php +++ b/library/Requests/Utility/CaseInsensitiveDictionary.php @@ -59,12 +59,7 @@ public function offsetSet($key, $value) { } $key = strtolower($key); - - if (!isset($this->data[$key])) { - $this->data[$key] = array(); - } - - $this->data[$key][] = $value; + $this->data[$key] = $value; } /** From 485927aebb04b9f571198ee77d20b886a73763ac Mon Sep 17 00:00:00 2001 From: Ryan McCue Date: Wed, 25 Sep 2013 02:39:19 +1000 Subject: [PATCH 13/13] Add testing for cookie parsing --- tests/Cookies.php | 174 +++++++++++++++++++++++++++++++++++++++++ tests/phpunit.xml.dist | 1 + 2 files changed, 175 insertions(+) create mode 100755 tests/Cookies.php diff --git a/tests/Cookies.php b/tests/Cookies.php new file mode 100755 index 000000000..0348fd800 --- /dev/null +++ b/tests/Cookies.php @@ -0,0 +1,174 @@ +assertEquals('requests-testcookie', $cookie->name); + $this->assertEquals('testvalue', $cookie->value); + $this->assertEquals('testvalue', (string) $cookie); + + $this->assertEquals('requests-testcookie=testvalue', $cookie->formatForHeader()); + $this->assertEquals('requests-testcookie=testvalue', $cookie->formatForSetCookie()); + } + + public function testCookieWithAttributes() { + $attributes = array( + 'httponly', + 'path' => '/' + ); + $cookie = new Requests_Cookie('requests-testcookie', 'testvalue', $attributes); + + $this->assertEquals('requests-testcookie=testvalue', $cookie->formatForHeader()); + $this->assertEquals('requests-testcookie=testvalue; httponly; path=/', $cookie->formatForSetCookie()); + } + + public function testEmptyCookieName() { + $cookie = Requests_Cookie::parse('test'); + $this->assertEquals('', $cookie->name); + $this->assertEquals('test', $cookie->value); + } + + public function testEmptyAttributes() { + $cookie = Requests_Cookie::parse('foo=bar; HttpOnly'); + $this->assertTrue($cookie->attributes['httponly']); + } + + public function testCookieJarSetter() { + $jar1 = new Requests_Cookie_Jar(); + $jar1['requests-testcookie'] = 'testvalue'; + + $jar2 = new Requests_Cookie_Jar(array( + 'requests-testcookie' => 'testvalue', + )); + $this->assertEquals($jar1, $jar2); + } + + public function testCookieJarUnsetter() { + $jar = new Requests_Cookie_Jar(); + $jar['requests-testcookie'] = 'testvalue'; + + $this->assertEquals('testvalue', $jar['requests-testcookie']); + + unset($jar['requests-testcookie']); + $this->assertEmpty($jar['requests-testcookie']); + $this->assertFalse(isset($jar['requests-testcookie'])); + } + + /** + * @expectedException Requests_Exception + */ + public function testCookieJarAsList() { + $cookies = new Requests_Cookie_Jar(); + $cookies[] = 'requests-testcookie1=testvalue1'; + } + + public function testCookieJarIterator() { + $cookies = array( + 'requests-testcookie1' => 'testvalue1', + 'requests-testcookie2' => 'testvalue2', + ); + $jar = new Requests_Cookie_Jar($cookies); + + foreach ($jar as $key => $value) { + $this->assertEquals($cookies[$key], $value); + } + } + + public function testReceivingCookies() { + $options = array( + 'follow_redirects' => false, + ); + $url = 'http://httpbin.org/cookies/set?requests-testcookie=testvalue'; + + $response = Requests::get($url, array(), $options); + + $cookie = $response->cookies['requests-testcookie']; + $this->assertNotEmpty( $cookie ); + $this->assertEquals( 'testvalue', $cookie->value ); + } + + public function testPersistenceOnRedirect() { + $options = array( + 'follow_redirects' => true, + ); + $url = 'http://httpbin.org/cookies/set?requests-testcookie=testvalue'; + + $response = Requests::get($url, array(), $options); + + $cookie = $response->cookies['requests-testcookie']; + $this->assertNotEmpty( $cookie ); + $this->assertEquals( 'testvalue', $cookie->value ); + } + + protected function setCookieRequest($cookies) { + $options = array( + 'cookies' => $cookies, + ); + $response = Requests::get('http://httpbin.org/cookies/set', array(), $options); + + $data = json_decode($response->body, true); + $this->assertInternalType('array', $data); + $this->assertArrayHasKey('cookies', $data); + return $data['cookies']; + } + + public function testSendingCookie() { + $cookies = array( + 'requests-testcookie1' => 'testvalue1', + ); + + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + } + + public function testSendingCookieWithJar() { + $cookies = new Requests_Cookie_Jar(array( + 'requests-testcookie1' => 'testvalue1', + )); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + } + + public function testSendingMultipleCookies() { + $cookies = array( + 'requests-testcookie1' => 'testvalue1', + 'requests-testcookie2' => 'testvalue2', + ); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + + $this->assertArrayHasKey('requests-testcookie2', $data); + $this->assertEquals('testvalue2', $data['requests-testcookie2']); + } + + public function testSendingMultipleCookiesWithJar() { + $cookies = new Requests_Cookie_Jar(array( + 'requests-testcookie1' => 'testvalue1', + 'requests-testcookie2' => 'testvalue2', + )); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie1', $data); + $this->assertEquals('testvalue1', $data['requests-testcookie1']); + + $this->assertArrayHasKey('requests-testcookie2', $data); + $this->assertEquals('testvalue2', $data['requests-testcookie2']); + } + + public function testSendingPrebakedCookie() { + $cookies = new Requests_Cookie_Jar(array( + new Requests_Cookie('requests-testcookie', 'testvalue'), + )); + $data = $this->setCookieRequest($cookies); + + $this->assertArrayHasKey('requests-testcookie', $data); + $this->assertEquals('testvalue', $data['requests-testcookie']); + } +} \ No newline at end of file diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist index 3236d04ec..04254d22c 100755 --- a/tests/phpunit.xml.dist +++ b/tests/phpunit.xml.dist @@ -9,6 +9,7 @@ ChunkedEncoding.php + Cookies.php IDNAEncoder.php IRI.php Requests.php